{"id":16805268,"url":"https://github.com/joshjohanning/azdo_commit_message_validator","last_synced_at":"2026-03-10T03:03:41.705Z","repository":{"id":51855184,"uuid":"520601696","full_name":"joshjohanning/azdo_commit_message_validator","owner":"joshjohanning","description":"GitHub Action to enforce commits are linked to Azure Boards work items, and link the work item to the pull request","archived":false,"fork":false,"pushed_at":"2026-03-09T19:39:22.000Z","size":9364,"stargazers_count":5,"open_issues_count":5,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-09T23:59:24.381Z","etag":null,"topics":["actions","azure-boards","azure-devops","github","javascript","node-action"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joshjohanning.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-08-02T18:06:18.000Z","updated_at":"2026-03-06T21:16:55.000Z","dependencies_parsed_at":"2024-10-27T12:12:19.925Z","dependency_job_id":null,"html_url":"https://github.com/joshjohanning/azdo_commit_message_validator","commit_stats":{"total_commits":41,"total_committers":2,"mean_commits":20.5,"dds":"0.024390243902439046","last_synced_commit":"82fdaffda82dd48c8a8275d8283d7c5ca69dab41"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/joshjohanning/azdo_commit_message_validator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshjohanning%2Fazdo_commit_message_validator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshjohanning%2Fazdo_commit_message_validator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshjohanning%2Fazdo_commit_message_validator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshjohanning%2Fazdo_commit_message_validator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joshjohanning","download_url":"https://codeload.github.com/joshjohanning/azdo_commit_message_validator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshjohanning%2Fazdo_commit_message_validator/sbom","scorecard":{"id":534083,"data":{"date":"2025-08-11","repo":{"name":"github.com/joshjohanning/azdo_commit_message_validator","commit":"352b87e7c9313c7a77cc7911b143d6be0df290cb"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/29 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql-analysis.yml:28","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql-analysis.yml:29","Warn: no topLevel permission defined: .github/workflows/codeql-analysis.yml:1","Warn: no topLevel permission defined: .github/workflows/publish.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:41: update your workflow using https://app.stepsecurity.io/secureworkflow/joshjohanning/azdo_commit_message_validator/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:45: update your workflow using https://app.stepsecurity.io/secureworkflow/joshjohanning/azdo_commit_message_validator/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:59: update your workflow using https://app.stepsecurity.io/secureworkflow/joshjohanning/azdo_commit_message_validator/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:72: update your workflow using https://app.stepsecurity.io/secureworkflow/joshjohanning/azdo_commit_message_validator/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/joshjohanning/azdo_commit_message_validator/publish.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/publish.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/joshjohanning/azdo_commit_message_validator/publish.yml/main?enable=pin","Info:   0 out of   5 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":7,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 0 commits out of 4 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"11 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-h5c3-5r3r-rr8q","Warn: Project is vulnerable to: GHSA-rmvr-2pp2-xj38","Warn: Project is vulnerable to: GHSA-xx4v-prfh-6cgc","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T06:29:10.395Z","repository_id":51855184,"created_at":"2025-08-20T06:29:10.395Z","updated_at":"2025-08-20T06:29:10.395Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30322648,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T01:36:58.598Z","status":"online","status_checked_at":"2026-03-10T02:00:06.579Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["actions","azure-boards","azure-devops","github","javascript","node-action"],"created_at":"2024-10-13T09:47:39.853Z","updated_at":"2026-03-10T03:03:41.698Z","avatar_url":"https://github.com/joshjohanning.png","language":"JavaScript","readme":"# Azure DevOps Commit/PR Validator and Commit+Pull Request Linker Action\n\n[![GitHub release](https://img.shields.io/github/release/joshjohanning/azdo_commit_message_validator.svg?logo=github\u0026labelColor=333)](https://github.com/joshjohanning/azdo_commit_message_validator/releases)\n[![GitHub marketplace](https://img.shields.io/badge/marketplace-Azure%20DevOps%20Commit%20Validator%20and%20Pull%20Request%20Linker-blue?logo=github\u0026labelColor=333)](https://github.com/marketplace/actions/azure-devops-commit-validator-and-pull-request-linker)\n[![CI](https://github.com/joshjohanning/azdo_commit_message_validator/actions/workflows/ci.yml/badge.svg)](https://github.com/joshjohanning/azdo_commit_message_validator/actions/workflows/ci.yml)\n[![Publish GitHub Action](https://github.com/joshjohanning/azdo_commit_message_validator/actions/workflows/publish.yml/badge.svg)](https://github.com/joshjohanning/azdo_commit_message_validator/actions/workflows/publish.yml)\n![Coverage](./badges/coverage.svg)\n\nThis action validates that pull requests and commits contain Azure DevOps work item links (e.g. `AB#123`), and **automatically links the GitHub Pull Request to work items found in commit messages**.\n\n## Key Features\n\n1. **Validates Pull Requests** - Ensures PR title or body contains an Azure DevOps work item link (e.g. `AB#123`)\n2. **Validates Commits** - Ensures each commit in a pull request has an Azure DevOps work item link (e.g. `AB#123`) in the commit message\n3. **Automatically Links PRs to Work Items** - When a work item is referenced in a commit message, the action adds a GitHub Pull Request link to that work item in Azure DevOps\n   - 🎯 **This is the key differentiator**: By default, Azure DevOps only adds the Pull Request link to work items mentioned directly in the PR title or body, but this action also links work items found in commit messages!\n4. **Visibility \u0026 Tracking** - Work item linkages are added to the job summary for easy visibility\n\n## Action Output\n\nThe action provides visibility into work items through the **Job Summary**:\n\n- A summary of all work items found in commits and PR is added to the workflow run's job summary page\n- Includes clickable links to commits and displays associated work items\n- Shows which work items were **linked** to the PR (when `link-commits-to-pull-request` is enabled) vs. **verified** (when `validate-work-item-exists` is enabled)\n- Provides a quick reference of work items associated with the PR\n\n## Usage\n\nThis should only be triggered via pull requests.\n\n```yml\nname: pr-commit-message-enforcer-and-linker\n\non:\n  pull_request:\n    branches: ['main']\n    types:\n      - opened\n      - synchronize\n      - reopened\n      - edited # can re-run without code changes\n\njobs:\n  pr-commit-message-enforcer-and-linker:\n    runs-on: ubuntu-latest\n    # Skip runs triggered by azure-boards bot editing the PR body to avoid duplicate workflow runs\n    if: github.actor != 'azure-boards[bot]'\n    permissions:\n      contents: read\n      pull-requests: write\n\n    steps:\n      - uses: actions/checkout@v4\n      - name: Azure DevOps Commit Validator and Pull Request Linker\n        uses: joshjohanning/azdo_commit_message_validator@v2\n        with:\n          check-pull-request: true\n          check-commits: true\n          fail-if-missing-workitem-commit-link: true\n          link-commits-to-pull-request: true\n          azure-devops-organization: my-azdo-org\n          azure-devops-token: ${{ secrets.AZURE_DEVOPS_PAT }}\n```\n\n### Inputs\n\n| Name                                   | Description                                                                                                                                                                              | Required | Default               |\n| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------------------- |\n| `check-pull-request`                   | Check the pull request for `AB#xxx` (scope configurable via `pull-request-check-scope`)                                                                                                  | `true`   | `false`               |\n| `pull-request-check-scope`             | Only if `check-pull-request=true`, where to look for `AB#` in the PR: `title-or-body`, `body-only`, or `title-only`                                                                      | `false`  | `title-or-body`       |\n| `check-commits`                        | Check each commit in the pull request for `AB#xxx`                                                                                                                                       | `true`   | `true`                |\n| `fail-if-missing-workitem-commit-link` | Only if `check-commits=true`, fail the action if a commit in the pull request is missing AB# in every commit message                                                                     | `false`  | `true`                |\n| `link-commits-to-pull-request`         | Only if `check-commits=true`, link the work items found in commits to the pull request                                                                                                   | `false`  | `true`                |\n| `validate-work-item-exists`            | Validate that the work item(s) referenced in commits and PR exist in Azure DevOps (requires `azure-devops-token` and `azure-devops-organization`)                                        | `false`  | `true`                |\n| `add-work-item-table`                  | Add a \"Linked Work Items\" table to the PR body showing titles for `AB#xxx` references (original references are preserved). Requires `azure-devops-token` and `azure-devops-organization` | `false`  | `false`               |\n| `append-work-item-title`               | **Deprecated** - use `add-work-item-table` instead. Will be removed in a future major version.                                                                                           | `false`  | `false`               |\n| `azure-devops-organization`            | Only if `check-commits=true`, link the work items found in commits to the pull request                                                                                                   | `false`  | `''`                  |\n| `azure-devops-token`                   | Only required if `link-commits-to-pull-request=true`, Azure DevOps PAT used to link work item to PR (needs to be a `full` PAT)                                                           | `false`  | `''`                  |\n| `github-token`                         | The GitHub token that has contents-read and pull_request-write access                                                                                                                    | `true`   | `${{ github.token }}` |\n| `comment-on-failure`                   | Comment on the pull request if the action fails                                                                                                                                          | `true`   | `true`                |\n\n## Screenshots\n\n### Failing pull request, including comment back to the pull request showing why it failed\n\n\u003cimg width=\"917\" alt=\"image\" src=\"https://github.com/joshjohanning/azdo_commit_message_validator/assets/19912012/383358aa-748e-4666-be52-2ae6e371530e\"\u003e\n\n\u003cimg width=\"868\" alt=\"image\" src=\"https://github.com/joshjohanning/azdo_commit_message_validator/assets/19912012/2dbc0775-7810-4ba3-98e0-b49391c63e7e\"\u003e\n\n### Failing commit\n\n\u003cimg width=\"1033\" alt=\"image\" src=\"https://user-images.githubusercontent.com/19912012/182519049-3bd1281d-985c-41ea-b35c-c3cd35994d48.png\"\u003e\n\n### Adding Pull Request link in Azure DevOps to work item linked to a commit in a pull request\n\n\u003cimg width=\"290\" alt=\"image\" src=\"https://user-images.githubusercontent.com/19912012/182518941-4c7d5bad-b19f-456a-b3bd-504b3ab2f45d.png\"\u003e\n\n### Validating the logs and creating pull requests\n\n\u003cimg width=\"713\" alt=\"image\" src=\"https://user-images.githubusercontent.com/19912012/182616583-70ef5ac4-c669-40df-8fa4-60b15ab1f58f.png\"\u003e\n\n## How the commit / pull request linking in Azure DevOps works\n\nIf the `check-commits: true` the action will look at each commit in the pull request and check for `AB#123` in the commit message.\n\nThe action loops through each commit and:\n\n1. Makes sure it has `AB#123` in the commit message\n2. If it does, and if `link-commits-to-pull-request: true`, add a GitHub Pull Request link to the work item in Azure DevOps\n\nAdding the link to the GitHub Pull Request was the tricky part.\n\nIf you use an API to look at the links of a work item with a GitHub pull request link, you will see:\n\n```json\n      \"attributes\": {\n        \"authorizedDate\": \"2022-08-02T18:45:03.567Z\",\n        \"id\": 3916078,\n        \"name\": \"GitHub Pull Request\",\n        \"resourceCreatedDate\": \"2022-08-02T18:45:03.567Z\",\n        \"resourceModifiedDate\": \"2022-08-02T18:45:03.567Z\",\n        \"revisedDate\": \"9999-01-01T00:00:00Z\"\n      },\n      \"rel\": \"Artifact Link\",\n      \"url\": \"vstfs:///GitHub/PullRequest/62f33e8a-c421-441d-88e1-06c46c4ffbbb%2f7\"\n```\n\nNote the `url` field - `vstfs:///GitHub/PullRequest/62f33e8a-c421-441d-88e1-06c46c4ffbbb%2f7`\n\nCreating a [new link is (relatively) easy with the API](https://docs.microsoft.com/en-us/rest/api/azure/devops/wit/work-items/update?view=azure-devops-rest-7.1\u0026tabs=HTTP#add-a-link), but you can't just use the regular GitHub pull request link. They use a garbled GUID that isn't the GUID or ID of the repo in GitHub.\n\nThe GUID can be found using an (undocumented) API:\n\n```\nPOST https://dev.azure.com/%DEVOPS_ORG%/_apis/Contribution/dataProviders/query?api-version=7.1-preview.1\n```\n\nSee this [thread](https://developercommunity.visualstudio.com/t/artifact-uri-format-in-external-link-of-work-items/964448#T-N988703) for slightly more info.\n\nFound the javascript sample [here](https://github.com/dc-ag/azure-devops-pr-notification/blob/fcb9cd24ffbcc2dbe81a7500a3d5577213afa7e3/lib/main.js). Other samples are [here](https://github.com/search?q=%22vstfs%3A%2F%2F%2FGitHub%22\u0026type=code).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshjohanning%2Fazdo_commit_message_validator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoshjohanning%2Fazdo_commit_message_validator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshjohanning%2Fazdo_commit_message_validator/lists"}