{"id":21281459,"url":"https://github.com/christophebedard/dco-check","last_synced_at":"2025-10-10T12:40:12.112Z","repository":{"id":40477243,"uuid":"250293800","full_name":"christophebedard/dco-check","owner":"christophebedard","description":"Simple DCO check script to be used in any CI","archived":false,"fork":false,"pushed_at":"2024-09-20T21:11:36.000Z","size":151,"stargazers_count":15,"open_issues_count":7,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-08T09:57:05.495Z","etag":null,"topics":["check","ci","commit","dco","signoff"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/christophebedard.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-03-26T15:16:52.000Z","updated_at":"2025-04-28T04:07:08.000Z","dependencies_parsed_at":"2023-02-01T10:46:57.569Z","dependency_job_id":null,"html_url":"https://github.com/christophebedard/dco-check","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/christophebedard/dco-check","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophebedard%2Fdco-check","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophebedard%2Fdco-check/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophebedard%2Fdco-check/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophebedard%2Fdco-check/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/christophebedard","download_url":"https://codeload.github.com/christophebedard/dco-check/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophebedard%2Fdco-check/sbom","scorecard":{"id":280990,"data":{"date":"2025-08-11","repo":{"name":"github.com/christophebedard/dco-check","commit":"7b0205d25ead0f898e0b706b58227dd5fa7e3f55"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.4,"checks":[{"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":"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":"Code-Review","score":2,"reason":"Found 7/30 approved changesets -- score normalized to 2","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":"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":["Warn: no topLevel permission defined: .github/workflows/dco.yml:1","Warn: no topLevel permission defined: .github/workflows/publish.yml:1","Warn: no topLevel permission defined: .github/workflows/tag.yml:1","Warn: no topLevel permission defined: .github/workflows/test.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":"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":"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":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"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: Apache License 2.0: 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":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/publish.yml:9"],"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":"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":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: third-party GitHubAction not pinned by hash: .github/workflows/dco.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/dco.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/publish.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/publish.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/publish.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/publish.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/publish.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/publish.yml:31: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/publish.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/publish.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/publish.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/publish.yml:37: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tag.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/tag.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/tag.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/tag.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/test.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/test.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/christophebedard/dco-check/test.yml/master?enable=pin","Warn: containerImage not pinned by hash: Dockerfile:1: pin your Docker image by updating ubuntu:20.04 to ubuntu:20.04@sha256:8feb4d8ca5354def3d8fce243717141ce31e2c428701f6682bd2fafe15388214","Warn: pipCommand not pinned by hash: Dockerfile:7","Warn: pipCommand not pinned by hash: .github/workflows/publish.yml:19","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:23","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:24","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:25","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   8 third-party GitHubAction dependencies pinned","Info:   0 out of   1 containerImage dependencies pinned","Info:   0 out of   5 pipCommand 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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 17 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"}}]},"last_synced_at":"2025-08-17T15:52:05.757Z","repository_id":40477243,"created_at":"2025-08-17T15:52:05.757Z","updated_at":"2025-08-17T15:52:05.757Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279003892,"owners_count":26083641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"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":["check","ci","commit","dco","signoff"],"created_at":"2024-11-21T10:47:58.357Z","updated_at":"2025-10-10T12:40:12.082Z","avatar_url":"https://github.com/christophebedard.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dco-check\n\n[![PyPI](https://img.shields.io/pypi/v/dco-check)](https://pypi.org/project/dco-check/)\n[![codecov](https://codecov.io/gh/christophebedard/dco-check/branch/master/graph/badge.svg)](https://codecov.io/gh/christophebedard/dco-check)\n[![License](https://img.shields.io/github/license/christophebedard/dco-check)](https://github.com/christophebedard/dco-check/blob/master/LICENSE)\n[![Docker Pulls](https://img.shields.io/docker/pulls/christophebedard/dco-check?logo=docker)](https://hub.docker.com/r/christophebedard/dco-check)\n\n[![GitHub Action Status](https://img.shields.io/github/actions/workflow/status/christophebedard/dco-check/test.yml?label=CI\u0026logo=github)](https://github.com/christophebedard/dco-check)\n[![GitLab pipeline status](https://img.shields.io/gitlab/pipeline/christophebedard/dco-check?label=CI\u0026logo=gitlab)](https://gitlab.com/christophebedard/dco-check/commits/master)\n[![Travis CI](https://img.shields.io/travis/com/christophebedard/dco-check?label=CI\u0026logo=travis)](https://travis-ci.com/github/christophebedard/dco-check)\n[![Azure DevOps builds](https://img.shields.io/azure-devops/build/christophebedard/74e64a5d-0fe6-4759-bb97-eb77bb0d15af/1?label=CI\u0026logo=azure%20pipelines)](https://dev.azure.com/christophebedard/dco-check/_build/latest?definitionId=1\u0026branchName=master)\n[![AppVeyor](https://img.shields.io/appveyor/build/christophebedard/dco-check?label=CI\u0026logo=appveyor)](https://ci.appveyor.com/project/christophebedard/dco-check)\n[![CircleCI](https://img.shields.io/circleci/build/github/christophebedard/dco-check?label=CI\u0026logo=circle\u0026logoColor=white)](https://circleci.com/gh/christophebedard/dco-check)\n\nSimple DCO check script to be used in [any CI](#ci-support).\n\n## Motivation\n\nMany open-source projects require the use of a `Signed-off-by:` line in every commit message.\nThis is to certify that a contributor has the right to submit their code according to the [Developer Certificate of Origin (DCO)](https://developercertificate.org/).\nHowever, to my knowledge, there is no automated check that can run on any CI platform (or most platforms).\nSome platforms simply do not possess such a feature.\n\nThis was inspired by the [DCO GitHub App](https://github.com/apps/dco).\n\n## How to get \u0026 use\n\nThere are a few options:\n\n1. Using the [package from PyPI](https://pypi.org/project/dco-check/)\n    ```shell\n    $ pip install dco-check\n    $ dco-check\n    ```\n1. Using the Docker image ([`christophebedard/dco-check`](https://hub.docker.com/r/christophebedard/dco-check)) with your CI (see [examples](#Example-CI-configurations))\n    ```shell\n    $ dco-check\n    ```\n1. Downloading the script and running it (you can replace `master` with a specific version)  \n    This is enabled by the fact that `dco-check` is a single Python file without any third-party dependencies.\n    ```shell\n    $ wget https://raw.githubusercontent.com/christophebedard/dco-check/master/dco_check/dco_check.py\n    $ python3 dco_check.py\n    ```\n\nIt exits with 0 if all checked commits have been signed-off.\nOtherwise, it exits with a non-zero number.\n\nRun with `--help` for more information and options, including:\n\n* ignoring merge commits\n* default branch\n* default remote\n* list of commit author emails to exclude from checks\n* regular expression pattern to exclude generic emails when matched \n* quiet mode\n* verbose mode\n* excluding certain author emails (e.g., for bots)\n\nThose options can alternatively be set through environment variables (see `--help`), but commandline arguments always have precedence over environment variables.\n\n## How it works\n\n`dco-check` focuses on two use-cases:\n\n1. Commits part of a feature branch, i.e. a proposed change (pull request or merge request)\n1. Commits on the default branch, e.g. `master`, or more specifically the new commits pushed to the default branch\n\nThe first use-case is easy to cover given a normal git repository.\nWe can simply use `git merge-base --fork-point $DEFAULT_BRANCH` to get the list of commits on a specific feature branch off of the default branch.\nSome CIs provide even more information, such as the target branch of the change, which is useful if we don't expect to always target the default branch.\nThen we can just check every commit using `git log` and make sure it is signed-off by the author.\n\nThe second use-case isn't really possible with simple git repositories, because they do not contain the necessary information (AFAIK).\nFortunately, some CIs do provide this information.\n\nFurthermore, by default, some CI platforms only clone git repositories up to a specific depth, i.e. you only get a partial commit history.\nThis depth can sometimes be 1 for some CIs, i.e. a shallow clone.\nFor those cases, it is usually possible to prevent shallow cloning by setting the right parameter(s) in the job configuration.\nHowever, since one of the goals of `dco-check` is to be as easy to use as possible, it tries not to rely on that.\n\nThis is why `dco-check` detects the current CI platform and uses whatever information that platform can provide.\nOtherwise, it falls back on a default generic implementation which uses simple git commands.\nIn those cases, the CLI options allow users to provide a lot of the missing information.\n\n## CI support\n\nBelow is a summary of the supported CIs along with their known behaviours.\n\n| CI | Detects new changes when pushing to default branch | Detects PRs/MRs | Gets base branch using | Gets default branch using | Notes |\n|:--:|:--------------------------------------------------:|:---------------:|:----------------------:|:-------------------------:|:-----:|\n|GitHub|✓|✓|CI|(not used)|retrieves commit data using the GitHub API, since GitHub does shallow clones by default|\n|GitLab|✓|✓|CI|CI|detects normal GitLab MRs and external (GitHub) MRs|\n|Azure Pipelines||✓|CI|CLI arguments||\n|AppVeyor||✓|CI|CLI arguments||\n|CircleCI|✓||CI\\* (or CLI arguments)|CLI arguments|\\*can use base revision information if provided (see example)|\n|Travis CI|||CLI arguments|CLI arguments|supported by default as a normal git repo|\n|default (git)|||CLI arguments|CLI arguments|use locally; using in an unsupported CI which only does a shallow clone might cause problems|\n\n## Example CI configurations\n\nHere are some example CI configurations.\n\n### GitHub\n\n```yaml\n# .github/workflows/dco.yml\nname: DCO\non:\n  pull_request:\n  push:\n    branches:\n      - master\njobs:\n  check_dco:\n    runs-on: ubuntu-latest\n    name: Check DCO\n    steps:\n      - name: Run dco-check\n        uses: christophebedard/dco-check@0.5.0\n        with:\n            python-version: '3.12'\n            args: '--verbose'\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n```\n\nPlease note that the `with:` property is optional, the default Python version is 3.12 and no additional arguments are passed by default.\n\n### GitLab\n\n```yaml\n# .gitlab-ci.yml\nvariables:\n  DOCKER_DRIVER: overlay2\ndco:\n  image: christophebedard/dco-check:latest\n  rules:\n    - if: $CI_MERGE_REQUEST_ID\n    - if: $CI_EXTERNAL_PULL_REQUEST_IID\n    - if: $CI_COMMIT_BRANCH == 'master'\n  script:\n    - pip3 install -U dco-check  # optional\n    - dco-check\n```\n\n## Python version support\n\nPython 3.6+ is required because of the use of f-strings.\nHowever, it shouldn't be too hard to remove them to support older versions of Python 3, if there is a demand for it, or if such a change is contributed to `dco-check`.\n\n## Contributing\n\nSee [`CONTRIBUTING.md`](./CONTRIBUTING.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchristophebedard%2Fdco-check","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchristophebedard%2Fdco-check","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchristophebedard%2Fdco-check/lists"}