Repository: intuit/auto (ts)

Issue:
Auto release is failing on  repository without tags 
**Describe the bug**
(related to : https://github.com/intuit/auto/issues/554)
Executing `auto shipit` on repo with no  tags causes the following exceptions. _Temp fix is to manually add a tag to the repo._ 
```text
Error: Running command 'git' with args [describe, --tags, --abbrev=0, ] failed
fatal: No names found, cannot describe anything.
    at ChildProcess.child.on.code (/snapshot/auto/node_modules/@auto-it/core/dist/utils/exec-promise.js:49:24)
    at ChildProcess.emit (events.js:198:13)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:248:12)
   script returned exit code 1
```
<!-- A clear and concise description of what the bug is. -->

**To Reproduce**

<!-- Steps to reproduce the behavior: -->
- A project without tags
- with following plugin: plugins: ["git-tag"]
- execute `auto shipit`

**Expected behavior**

<!-- A clear and concise description of what you expected to happen. -->

**Screenshots**

<!-- If applicable, add screenshots to help explain your problem. -->

**Desktop (please complete the following information):**

- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Additional context**

<!-- Add any other context about the problem here. -->


Pull request description:
fallback to 0.0.0 in git-tag plugin with no previous releases
# What Changed

Default to 0.0.0 when no previous release detected in git-tag plugin.

# Why

closes #886 

Todo:

- [ ] Add tests
- [ ] Add docs

<!-- GITHUB_RELEASE PR BODY: canary-version -->
Published PR with canary version: <details><summary>Published under canary scope @auto-canary</summary>
- `@auto-canary/auto@9.2.1-canary.888.11616.0`
- `@auto-canary/core@9.2.1-canary.888.11616.0`
- `@auto-canary/all-contributors@9.2.1-canary.888.11616.0`
- `@auto-canary/chrome@9.2.1-canary.888.11616.0`
- `@auto-canary/conventional-commits@9.2.1-canary.888.11616.0`
- `@auto-canary/crates@9.2.1-canary.888.11616.0`
- `@auto-canary/first-time-contributor@9.2.1-canary.888.11616.0`
- `@auto-canary/git-tag@9.2.1-canary.888.11616.0`
- `@auto-canary/jira@9.2.1-canary.888.11616.0`
- `@auto-canary/maven@9.2.1-canary.888.11616.0`
- `@auto-canary/npm@9.2.1-canary.888.11616.0`
- `@auto-canary/omit-commits@9.2.1-canary.888.11616.0`
- `@auto-canary/omit-release-notes@9.2.1-canary.888.11616.0`
- `@auto-canary/released@9.2.1-canary.888.11616.0`
- `@auto-canary/s3@9.2.1-canary.888.11616.0`
- `@auto-canary/slack@9.2.1-canary.888.11616.0`
- `@auto-canary/twitter@9.2.1-canary.888.11616.0`
- `@auto-canary/upload-assets@9.2.1-canary.888.11616.0`</details>
<!-- GITHUB_RELEASE PR BODY: canary-version -->


Patch:
diff --git a/packages/cli/src/run.ts b/packages/cli/src/run.ts
index 5d9691d5..427884f6 100644
--- a/packages/cli/src/run.ts
+++ b/packages/cli/src/run.ts
@@ -83,9 +83,7 @@ export default async function main(command: string, args: ApiOptions) {
   try {
     await run(command, args);
   } catch (error) {
-    if (error) {
-      console.log(error);
-      process.exit(1);
-    }
+    console.log(error);
+    process.exit(1);
   }
 }
diff --git a/packages/core/src/auto.ts b/packages/core/src/auto.ts
index 7f9f44bd..89cba8c7 100644
--- a/packages/core/src/auto.ts
+++ b/packages/core/src/auto.ts
@@ -735,7 +735,8 @@ export default class Auto {
 
     this.logger.verbose.info('Canary info found:', { pr, build });
 
-    const head = await this.release.getCommitsInRelease('HEAD^');
+    const from = await this.git.shaExists('HEAD^') ? 'HEAD^' : 'HEAD';
+    const head = await this.release.getCommitsInRelease(from);
     const labels = head.map(commit => commit.labels);
     const version =
       calculateSemVerBump(labels, this.semVerLabels!, this.config) ||
@@ -913,7 +914,8 @@ export default class Auto {
     this.hooks.beforeShipIt.call();
 
     const isPR = 'isPr' in env && env.isPr;
-    const head = await this.release.getCommitsInRelease('HEAD^');
+    const from = await this.git.shaExists('HEAD^') ? 'HEAD^' : 'HEAD';
+    const head = await this.release.getCommitsInRelease(from);
     // env-ci sets branch to target branch (ex: master) in some CI services.
     // so we should make sure we aren't in a PR just to be safe
     const currentBranch = getCurrentBranch();
diff --git a/packages/core/src/git.ts b/packages/core/src/git.ts
index 7b0ee4cb..69c5227f 100644
--- a/packages/core/src/git.ts
+++ b/packages/core/src/git.ts
@@ -197,6 +197,20 @@ export default class Git {
     return result;
   }
 
+    /** Get the SHA of the latest commit */
+    async shaExists(sha?: string): Promise<boolean> {
+      try {
+        await execPromise('git', [
+          'rev-parse',
+          '--verify',
+          sha
+        ]);
+        return true
+      } catch (error) {
+        return false;
+      }
+    }
+
   /** Get the labels for a PR */
   @memoize()
   async getLabels(prNumber: number) {
diff --git a/plugins/git-tag/src/index.ts b/plugins/git-tag/src/index.ts
index b534f45d..01021b1b 100644
--- a/plugins/git-tag/src/index.ts
+++ b/plugins/git-tag/src/index.ts
@@ -14,6 +14,15 @@ export default class GitTagPlugin implements IPlugin {
 
   /** Tap into auto plugin points. */
   apply(auto: Auto) {
+    /** Get the latest tag in the repo, if none then the first commit */
+    async function getTag() {
+      try {
+        return await auto.git!.getLatestTagInBranch();
+      } catch (error) {
+        return auto.prefixRelease('0.0.0')
+      }
+    }
+
     auto.hooks.getPreviousVersion.tapPromise(this.name, async () => {
       if (!auto.git) {
         throw new Error(
@@ -21,7 +30,7 @@ export default class GitTagPlugin implements IPlugin {
         );
       }
 
-      return auto.git.getLatestTagInBranch();
+      return getTag();
     });
 
     auto.hooks.version.tapPromise(this.name, async version => {
@@ -29,7 +38,7 @@ export default class GitTagPlugin implements IPlugin {
         return;
       }
 
-      const lastTag = await auto.git.getLatestTagInBranch();
+      const lastTag = await getTag();
       const newTag = inc(lastTag, version as ReleaseType);
       const branch = getCurrentBranch() || '';
 
@@ -37,7 +46,7 @@ export default class GitTagPlugin implements IPlugin {
         return;
       }
 
-      await execPromise('git', ['tag', newTag]);
+      await execPromise('git', ['tag', auto.prefixRelease(newTag)]);
       await execPromise('git', [
         'push',
         '--follow-tags',


Test patch:
diff --git a/plugins/git-tag/__tests__/git-tag.test.ts b/plugins/git-tag/__tests__/git-tag.test.ts
index 1c2fbc3e..34e3ff79 100644
--- a/plugins/git-tag/__tests__/git-tag.test.ts
+++ b/plugins/git-tag/__tests__/git-tag.test.ts
@@ -39,6 +39,16 @@ describe('Git Tag Plugin', () => {
       const previousVersion = await hooks.getPreviousVersion.promise();
       expect(previousVersion).toBe('v1.0.0');
     });
+
+    test('should default to 0.0.0 when no previous version', async () => {
+      const hooks = setup({
+        getLatestTagInBranch: () => {
+          throw new Error();
+        }
+      });
+      const previousVersion = await hooks.getPreviousVersion.promise();
+      expect(previousVersion).toBe('0.0.0');
+    });
   });
 
   describe('version', () => {