@exadev/build-identity 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,17 +18,17 @@ pnpm add -D @exadev/build-identity
18
18
  import { resolveBuildIdentity } from '@exadev/build-identity';
19
19
 
20
20
  const identity = resolveBuildIdentity(process.cwd(), 'exadev/build-identity');
21
- // { kind: 'release', version: '1.4.0', url: 'https://github.com/exadev/build-identity/releases/tag/v1.4.0', date: '2026-09-08T09:12:03+01:00' }
21
+ // { kind: 'release', version: '1.4.0', url: 'https://github.com/exadev/build-identity/releases/tag/v1.4.0', date: '2026-09-08T09:12:03+01:00', commit: 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2' }
22
22
  // or, on any commit that isn't itself tagged:
23
- // { kind: 'commit', version: 'a1b2c3d', url: 'https://github.com/exadev/build-identity/commit/a1b2c3d4e5f6...', date: '2026-09-08T09:12:03+01:00' }
23
+ // { kind: 'commit', version: 'a1b2c3d', url: 'https://github.com/exadev/build-identity/commit/a1b2c3d4e5f6...', date: '2026-09-08T09:12:03+01:00', commit: 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2' }
24
24
  ```
25
25
 
26
26
  ## `resolveBuildIdentity(repoRoot, repoSlug, options?)`
27
27
 
28
28
  ```ts
29
29
  type BuildIdentity =
30
- | { kind: 'release'; version: string; url: string; date: string }
31
- | { kind: 'commit'; version: string; url: string; date: string };
30
+ | { kind: 'release'; version: string; url: string; date: string; commit: string }
31
+ | { kind: 'commit'; version: string; url: string; date: string; commit: string };
32
32
 
33
33
  function resolveBuildIdentity(repoRoot: string, repoSlug: string, options?: ResolveBuildIdentityOptions): BuildIdentity;
34
34
 
@@ -43,6 +43,8 @@ interface ResolveBuildIdentityOptions {
43
43
 
44
44
  **The one property this function exists to guarantee:** it returns `kind: 'release'` if, and only if, a tag named `tagName(version)` provably points at the exact commit `HEAD` is on right now -- checked live against git (`git tag --list <tag> --points-at HEAD`), never inferred from `package.json`, an environment variable, or any other proxy that could be true before the tag actually exists. Every other case, including a commit sitting directly on top of the commit a release will eventually tag, returns `kind: 'commit'` instead, with `version` set to the short commit hash (there is no released version to show yet) and `url` pointing at that exact commit's permalink (using the full SHA, not the short one, so the link stays a valid, unambiguous permalink).
45
45
 
46
+ **`commit`** is always the full git commit SHA of the exact commit this build resolves to, in both branches -- present so a caller who needs the raw SHA (to compare two builds for identity, or to construct its own link) never has to parse it back out of `url`, which never carries one at all in the `'release'` case.
47
+
46
48
  `resolveBuildIdentity` throws rather than defaulting whenever it can't establish a real identity -- `repoRoot` isn't a git repository, `package.json` is missing or has no non-empty string `"version"` field, or `repoSlug` isn't a real `"owner/repo"` slug. There is no sensible placeholder identity for a build that isn't sitting in real, readable git history.
47
49
 
48
50
  ## `resolvePredictedIdentity(build, predictedVersion)`
package/dist/index.cjs CHANGED
@@ -10,7 +10,7 @@ function runGit(repoRoot, args) {
10
10
  }).trim();
11
11
  }
12
12
  /**
13
- * Reads HEAD's full hash, short hash, and committer date from the given git working tree. Throws whatever `git` itself throws (e.g. `repoRoot` is not a git repository, or has no commits) -- there is no sensible default identity for a build that isn't sitting in real git history.
13
+ * Reads HEAD's full hash, short hash, and author date from the given git working tree. Author date, not committer date: a rebase-merged commit keeps its original author date but gets a fresh committer date at rebase time, and every consumer of this identity treats the date as "when this commit was authored", not "when it was last rewritten onto its current position". Throws whatever `git` itself throws (e.g. `repoRoot` is not a git repository, or has no commits) -- there is no sensible default identity for a build that isn't sitting in real git history.
14
14
  */
15
15
  function getHeadCommit(repoRoot) {
16
16
  return {
@@ -23,7 +23,7 @@ function getHeadCommit(repoRoot) {
23
23
  date: runGit(repoRoot, [
24
24
  "log",
25
25
  "-1",
26
- "--format=%cI",
26
+ "--format=%aI",
27
27
  "HEAD"
28
28
  ])
29
29
  };
@@ -78,13 +78,15 @@ function resolveBuildIdentity(repoRoot, repoSlug, options = {}) {
78
78
  kind: "release",
79
79
  version,
80
80
  url: `https://github.com/${repoSlug}/releases/tag/${tagName}`,
81
- date: head.date
81
+ date: head.date,
82
+ commit: head.fullSha
82
83
  };
83
84
  return {
84
85
  kind: "commit",
85
86
  version: head.shortSha,
86
87
  url: `https://github.com/${repoSlug}/commit/${head.fullSha}`,
87
- date: head.date
88
+ date: head.date,
89
+ commit: head.fullSha
88
90
  };
89
91
  }
90
92
  //#endregion
@@ -105,6 +107,7 @@ function resolvePredictedIdentity(build, predictedVersion) {
105
107
  version: trimmedPrediction,
106
108
  url: build.url,
107
109
  date: build.date,
110
+ commit: build.commit,
108
111
  predicted: true
109
112
  };
110
113
  }
package/dist/index.d.cts CHANGED
@@ -12,6 +12,8 @@ type BuildIdentity = {
12
12
  readonly url: string;
13
13
  /** ISO 8601 date of the commit this release was tagged at. */
14
14
  readonly date: string;
15
+ /** Full git commit SHA of the commit this release was tagged at -- the same commit `url` and `date` describe, exposed as its own field so a caller never has to parse it back out of a release URL that never contains one. */
16
+ readonly commit: string;
15
17
  } | {
16
18
  readonly kind: 'commit';
17
19
  /** The short commit hash this build was made from -- there is no released version to show yet. */
@@ -20,6 +22,8 @@ type BuildIdentity = {
20
22
  readonly url: string;
21
23
  /** ISO 8601 date of this commit. */
22
24
  readonly date: string;
25
+ /** Full git commit SHA this build was made from -- the same commit `version`'s short form and `url`'s permalink describe. */
26
+ readonly commit: string;
23
27
  };
24
28
  interface ResolveBuildIdentityOptions {
25
29
  /**
package/dist/index.d.ts CHANGED
@@ -12,6 +12,8 @@ type BuildIdentity = {
12
12
  readonly url: string;
13
13
  /** ISO 8601 date of the commit this release was tagged at. */
14
14
  readonly date: string;
15
+ /** Full git commit SHA of the commit this release was tagged at -- the same commit `url` and `date` describe, exposed as its own field so a caller never has to parse it back out of a release URL that never contains one. */
16
+ readonly commit: string;
15
17
  } | {
16
18
  readonly kind: 'commit';
17
19
  /** The short commit hash this build was made from -- there is no released version to show yet. */
@@ -20,6 +22,8 @@ type BuildIdentity = {
20
22
  readonly url: string;
21
23
  /** ISO 8601 date of this commit. */
22
24
  readonly date: string;
25
+ /** Full git commit SHA this build was made from -- the same commit `version`'s short form and `url`'s permalink describe. */
26
+ readonly commit: string;
23
27
  };
24
28
  interface ResolveBuildIdentityOptions {
25
29
  /**
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ function runGit(repoRoot, args) {
9
9
  }).trim();
10
10
  }
11
11
  /**
12
- * Reads HEAD's full hash, short hash, and committer date from the given git working tree. Throws whatever `git` itself throws (e.g. `repoRoot` is not a git repository, or has no commits) -- there is no sensible default identity for a build that isn't sitting in real git history.
12
+ * Reads HEAD's full hash, short hash, and author date from the given git working tree. Author date, not committer date: a rebase-merged commit keeps its original author date but gets a fresh committer date at rebase time, and every consumer of this identity treats the date as "when this commit was authored", not "when it was last rewritten onto its current position". Throws whatever `git` itself throws (e.g. `repoRoot` is not a git repository, or has no commits) -- there is no sensible default identity for a build that isn't sitting in real git history.
13
13
  */
14
14
  function getHeadCommit(repoRoot) {
15
15
  return {
@@ -22,7 +22,7 @@ function getHeadCommit(repoRoot) {
22
22
  date: runGit(repoRoot, [
23
23
  "log",
24
24
  "-1",
25
- "--format=%cI",
25
+ "--format=%aI",
26
26
  "HEAD"
27
27
  ])
28
28
  };
@@ -77,13 +77,15 @@ function resolveBuildIdentity(repoRoot, repoSlug, options = {}) {
77
77
  kind: "release",
78
78
  version,
79
79
  url: `https://github.com/${repoSlug}/releases/tag/${tagName}`,
80
- date: head.date
80
+ date: head.date,
81
+ commit: head.fullSha
81
82
  };
82
83
  return {
83
84
  kind: "commit",
84
85
  version: head.shortSha,
85
86
  url: `https://github.com/${repoSlug}/commit/${head.fullSha}`,
86
- date: head.date
87
+ date: head.date,
88
+ commit: head.fullSha
87
89
  };
88
90
  }
89
91
  //#endregion
@@ -104,6 +106,7 @@ function resolvePredictedIdentity(build, predictedVersion) {
104
106
  version: trimmedPrediction,
105
107
  url: build.url,
106
108
  date: build.date,
109
+ commit: build.commit,
107
110
  predicted: true
108
111
  };
109
112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exadev/build-identity",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Resolve a build's true identity (a real release tag, or the commit it was built from) from git state, with an optional predicted-version display layer",
5
5
  "type": "module",
6
6
  "sideEffects": false,