Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 35 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ Collects Git commit info from git CLI
Requires [Node](https://nodejs.org/en/) version 8 or above.

```sh
npm install --save @currents-dev/commit-info
npm install --save @currents/commit-info
```

## Use

```js
const {commitInfo} = require('@currents-dev/commit-info')
const {commitInfo} = require('@currents/commit-info')
// default folder is current working directory
commitInfo(folder)
.then(info => {
Expand All @@ -25,7 +25,7 @@ commitInfo(folder)
// author
// sha
// timestamp (in seconds since epoch)
// remote
// remote (without credentials)
})
```

Expand All @@ -35,7 +35,30 @@ Notes:
- Resolves with [Bluebird](https://github.com/petkaantonov/bluebird) promise.
- Only uses Git commands, see [src/git-api.js](src/git-api.js)
- If a command fails, returns `null` for each property
- If you need to debug, run with `DEBUG=commit-info` environment variable.
- `remote` never contains a user name or password, also when it comes from `COMMIT_INFO_REMOTE`.
- If you need to debug, run with `DEBUG=commit-info` environment variable. The debug output does not contain the remote's credentials.

## CI provider variables

`getCiCommitInfo()` reads the commit from the variables of the CI provider the process runs on. The branch comes from:

| Provider | Branch |
| --- | --- |
| GitHub Actions | `GH_BRANCH`, `GITHUB_HEAD_REF` (pull requests), `GITHUB_REF_NAME`, or `GITHUB_REF` without `refs/heads/` or `refs/tags/` |
| GitLab | `CI_COMMIT_REF_NAME` |
| CircleCI | `CIRCLE_BRANCH` |
| Jenkins | `CHANGE_BRANCH` (multibranch pull requests), or `GIT_BRANCH` without `origin/`, `refs/remotes/origin/` or `refs/heads/` |
| Azure Pipelines | `SYSTEM_PULLREQUEST_SOURCEBRANCH` without `refs/heads/` (pull requests), or `BUILD_SOURCEBRANCHNAME`, which is only the last segment: `x` for `feature/x` |
| Bitbucket Pipelines | `BITBUCKET_BRANCH` |
| Buildkite | `BUILDKITE_BRANCH` |

AWS CodeBuild gives no branch. The other properties and providers are in [src/ci.js](src/ci.js).

## Containers

git 2.35.2 and later refuse to read a repository owned by another user, with `fatal: unsafe repository` (2.35.2 to 2.37.x) or `fatal: detected dubious ownership in repository` (2.38.0 and later). This is common when a container runs as root on a checkout made by another user. On CI, the read-only git commands then run again with `-c safe.directory=*`. CI means that the `CI` variable is set to a value other than `false` or `0`, or that one of the CI providers in [src/ci-provider.js](src/ci-provider.js) is detected; Jenkins, for example, does not set `CI`. `GOOGLE_CLOUD_PROJECT`, `GCP_PROJECT`, `GCLOUD_PROJECT` and `JENKINS_HOME` do not count, because developers often have them set in their shell. `*` because the folder can be a subfolder of the repository.

git 2.35.2 to 2.37.x ignore `safe.directory` on the command line, so there the git values are `null` (git 2.38.0 and later respect it); run `git config --global --add safe.directory '*'` in the container or set the `COMMIT_INFO_*` variables.

## Pull request builds

Expand All @@ -51,7 +74,7 @@ When the checked-out commit is such a merge, `commitInfo` reports the pull reque
- Buildkite: `BUILDKITE_PULL_REQUEST_HEAD_COMMIT`
- Bitbucket Pipelines: `BITBUCKET_COMMIT`

The commit is used only when the checked-out commit is a merge and the commit is one of its parents. If a shallow clone does not contain it (for example `actions/checkout` with the default `fetch-depth: 1`), it is fetched with `git fetch --depth=1 origin <sha>`, with a 3 second timeout. If the fetch fails, the checked-out commit is reported. Set `CURRENTS_DISABLE_HEAD_COMMIT_FETCH=true` to skip the fetch.
The commit is used only when the checked-out commit is a merge and the commit is one of its parents. If a shallow clone does not contain it (for example `actions/checkout` with the default `fetch-depth: 1`), it is fetched with `git fetch --depth=1 origin <sha>`, with a 3 second timeout. If the fetch fails, the checked-out commit is reported. The fetch does not use the `safe.directory` retry. Set `CURRENTS_DISABLE_HEAD_COMMIT_FETCH=true` to skip the fetch.

The `COMMIT_INFO_*` variables below still take priority. When `COMMIT_INFO_SHA` is set, the pull request's commit is not looked up.

Expand Down Expand Up @@ -85,7 +108,13 @@ See [docker-example](docker-example) for a full example.
## Individual methods

In addition to `commitInfo` this module also exposes individual promise-returning
methods `getBranch`, `getMessage`, `getEmail`, `getAuthor`, `getSha`, `getTimestamp`, `getRemoteOrigin`. These methods do NOT use fallback environment variables.
methods `getBranch`, `getMessage`, `getEmail`, `getAuthor`, `getSha`, `getTimestamp`, `getRemoteOrigin`. These methods do NOT use fallback environment variables. `getRemoteOrigin` returns the remote without credentials.

Other exports:

- `getCiCommitInfo(env = process.env)`: the CI provider's values and the provider name, see [CI provider variables](#ci-provider-variables). The `remote` has no credentials.
- `detectCiProvider(env = process.env)`: the CI provider name, such as `githubActions`, or `null`.
- `removeCredentials(url)`: removes the user name and password from a URL, keeping the port. Returns other values, such as `git@github.com:o/r.git`, as they are.

For example

Expand Down
10 changes: 8 additions & 2 deletions __snapshots__/commit-info-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ exports['commit-info no environment variables has certain api 1'] = [
"getRemoteOrigin",
"getSubject",
"getTimestamp",
"getBody"
"getBody",
"getCiCommitInfo",
"detectCiProvider",
"removeCredentials"
]

exports['commit-info no environment variables returns information 1'] = {
Expand Down Expand Up @@ -41,7 +44,10 @@ exports['commit-info combination with environment variables has certain api 1']
"getRemoteOrigin",
"getSubject",
"getTimestamp",
"getBody"
"getBody",
"getCiCommitInfo",
"detectCiProvider",
"removeCredentials"
]

exports['commit-info combination with environment variables returns information 1'] = {
Expand Down
57 changes: 57 additions & 0 deletions src/ci-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict'

const anyKey = (env, pattern) => Object.keys(env).some(key => pattern.test(key))

// The order matters when the variables of two providers are set: the first
// match wins. It is the order the Currents clients detect providers in.
const CI_PROVIDERS = [
['appveyor', env => env.APPVEYOR],
['azure', env => env.TF_BUILD && env.AZURE_HTTP_USER_AGENT],
['awsCodeBuild', env => anyKey(env, /^CODEBUILD_/)],
['bamboo', env => env.bamboo_buildNumber],
['bitbucket', env => env.BITBUCKET_BUILD_NUMBER],
['buildkite', env => env.BUILDKITE],
['circle', env => env.CIRCLECI],
['concourse', env => anyKey(env, /^CONCOURSE_/)],
['codeFresh', env => env.CF_BUILD_ID],
['drone', env => env.DRONE],
['githubActions', env => env.GITHUB_ACTIONS],
[
'gitlab',
env =>
env.GITLAB_CI ||
(env.CI_SERVER_NAME && /^GitLab/.test(env.CI_SERVER_NAME))
],
['goCD', env => env.GO_JOB_NAME],
[
'jenkins',
env =>
env.JENKINS_URL ||
env.JENKINS_HOME ||
env.JENKINS_VERSION ||
env.HUDSON_URL ||
env.HUDSON_HOME
],
[
'googleCloud',
env =>
(env.BUILD_ID && env.PROJECT_ID && env.PROJECT_NUMBER) ||
env.GCP_PROJECT ||
env.GCLOUD_PROJECT ||
env.GOOGLE_CLOUD_PROJECT
],
['semaphore', env => env.SEMAPHORE],
['teamcity', env => env.TEAMCITY_VERSION],
['travis', env => env.TRAVIS],
['netlify', env => env.NETLIFY]
]

/**
* Returns the name of the CI provider the process runs on, or null.
*/
function detectCiProvider (env = process.env) {
const found = CI_PROVIDERS.find(([, isProvider]) => isProvider(env))
return found ? found[0] : null
}

module.exports = { detectCiProvider }
Loading
Loading