| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  | import * as core from '@actions/core'; | 
					
						
							|  |  |  | import NotImplementedException from './error/not-implemented-exception'; | 
					
						
							|  |  |  | import ValidationError from './error/validation-error'; | 
					
						
							| 
									
										
										
										
											2020-05-21 15:44:56 +00:00
										 |  |  | import Input from './input'; | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  | import System from './system'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default class Versioning { | 
					
						
							| 
									
										
										
										
											2020-05-20 22:06:08 +00:00
										 |  |  |   static get projectPath() { | 
					
						
							| 
									
										
										
										
											2020-05-21 15:44:56 +00:00
										 |  |  |     return Input.projectPath; | 
					
						
							| 
									
										
										
										
											2020-05-20 22:06:08 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-21 12:45:52 +00:00
										 |  |  |   static get isDirtyAllowed() { | 
					
						
							| 
									
										
										
										
											2020-08-10 14:30:06 +00:00
										 |  |  |     return Input.allowDirtyBuild; | 
					
						
							| 
									
										
										
										
											2020-05-21 12:45:52 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |   static get strategies() { | 
					
						
							|  |  |  |     return { None: 'None', Semantic: 'Semantic', Tag: 'Tag', Custom: 'Custom' }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Get the branch name of the (related) branch | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static get branch() { | 
					
						
							| 
									
										
										
										
											2020-04-27 23:43:15 +00:00
										 |  |  |     // Todo - use optional chaining (https://github.com/zeit/ncc/issues/534)
 | 
					
						
							|  |  |  |     return this.headRef || (this.ref && this.ref.slice(11)); | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * For pull requests we can reliably use GITHUB_HEAD_REF | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static get headRef() { | 
					
						
							|  |  |  |     return process.env.GITHUB_HEAD_REF; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * For branches GITHUB_REF will have format `refs/heads/feature-branch-1` | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static get ref() { | 
					
						
							|  |  |  |     return process.env.GITHUB_REF; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-15 20:35:53 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * The commit SHA that triggered the workflow run. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static get sha() { | 
					
						
							|  |  |  |     return process.env.GITHUB_SHA; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-08 23:48:46 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Maximum number of lines to print when logging the git diff | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static get maxDiffLines() { | 
					
						
							| 
									
										
										
										
											2020-07-09 01:08:14 +00:00
										 |  |  |     return 60; | 
					
						
							| 
									
										
										
										
											2020-07-08 23:48:46 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Log up to maxDiffLines of the git diff. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async logDiff() { | 
					
						
							| 
									
										
										
										
											2020-07-09 00:58:15 +00:00
										 |  |  |     const diffCommand = `git --no-pager diff | head -n ${this.maxDiffLines.toString()}`; | 
					
						
							| 
									
										
										
										
											2020-07-09 00:36:57 +00:00
										 |  |  |     await System.run('sh', undefined, { | 
					
						
							| 
									
										
										
										
											2020-07-09 00:58:15 +00:00
										 |  |  |       input: Buffer.from(diffCommand), | 
					
						
							| 
									
										
										
										
											2020-07-09 01:08:14 +00:00
										 |  |  |       silent: true, | 
					
						
							| 
									
										
										
										
											2020-07-09 00:36:57 +00:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-07-08 23:48:46 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Regex to parse version description into separate fields | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2021-01-12 13:50:52 +00:00
										 |  |  |   static get descriptionRegex1() { | 
					
						
							| 
									
										
										
										
											2021-12-10 00:54:24 +00:00
										 |  |  |     return /^v?([\d.]+)-(\d+)-g(\w+)-?(\w+)*/g; | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-12 13:50:52 +00:00
										 |  |  |   static get descriptionRegex2() { | 
					
						
							| 
									
										
										
										
											2021-12-10 00:54:24 +00:00
										 |  |  |     return /^v?([\d.]+-\w+)-(\d+)-g(\w+)-?(\w+)*/g; | 
					
						
							| 
									
										
										
										
											2021-01-12 13:50:52 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static get descriptionRegex3() { | 
					
						
							| 
									
										
										
										
											2021-12-10 00:54:24 +00:00
										 |  |  |     return /^v?([\d.]+-\w+\.\d+)-(\d+)-g(\w+)-?(\w+)*/g; | 
					
						
							| 
									
										
										
										
											2021-01-12 13:50:52 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-13 23:44:01 +00:00
										 |  |  |   static async determineVersion(strategy: string, inputVersion: string) { | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |     // Validate input
 | 
					
						
							|  |  |  |     if (!Object.hasOwnProperty.call(this.strategies, strategy)) { | 
					
						
							| 
									
										
										
										
											2021-03-13 23:44:01 +00:00
										 |  |  |       throw new ValidationError(`Versioning strategy should be one of ${Object.values(this.strategies).join(', ')}.`); | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let version; | 
					
						
							|  |  |  |     switch (strategy) { | 
					
						
							|  |  |  |       case this.strategies.None: | 
					
						
							|  |  |  |         version = 'none'; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       case this.strategies.Custom: | 
					
						
							|  |  |  |         version = inputVersion; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       case this.strategies.Semantic: | 
					
						
							|  |  |  |         version = await this.generateSemanticVersion(); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       case this.strategies.Tag: | 
					
						
							|  |  |  |         version = await this.generateTagVersion(); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       default: | 
					
						
							|  |  |  |         throw new NotImplementedException(`Strategy ${strategy} is not implemented.`); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return version; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Automatically generates a version based on SemVer out of the box. | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * The version works as follows: `<major>.<minor>.<patch>` for example `0.1.2`. | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * The latest tag dictates `<major>.<minor>` | 
					
						
							|  |  |  |    * The number of commits since that tag dictates`<patch>`. | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * @See: https://semver.org/
 | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async generateSemanticVersion() { | 
					
						
							| 
									
										
										
										
											2021-02-13 05:22:23 +00:00
										 |  |  |     if (await this.isShallow()) { | 
					
						
							|  |  |  |       await this.fetch(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-08 23:48:46 +00:00
										 |  |  |     await this.logDiff(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-21 12:45:52 +00:00
										 |  |  |     if ((await this.isDirty()) && !this.isDirtyAllowed) { | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |       throw new Error('Branch is dirty. Refusing to base semantic version on uncommitted changes'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!(await this.hasAnyVersionTags())) { | 
					
						
							|  |  |  |       const version = `0.0.${await this.getTotalNumberOfCommits()}`; | 
					
						
							|  |  |  |       core.info(`Generated version ${version} (no version tags found).`); | 
					
						
							|  |  |  |       return version; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-12 13:50:52 +00:00
										 |  |  |     const versionDescriptor = await this.parseSemanticVersion(); | 
					
						
							|  |  |  |     if (versionDescriptor) { | 
					
						
							|  |  |  |       const { tag, commits, hash } = versionDescriptor; | 
					
						
							| 
									
										
										
										
											2021-03-13 23:44:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // Ensure 3 digits (commits should always be patch level)
 | 
					
						
							|  |  |  |       const [major, minor, patch] = `${tag}.${commits}`.split('.'); | 
					
						
							|  |  |  |       const threeDigitVersion = /^\d+$/.test(patch) ? `${major}.${minor}.${patch}` : `${major}.0.${minor}`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       core.info(`Found semantic version ${threeDigitVersion} for ${this.branch}@${hash}`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return `${threeDigitVersion}`; | 
					
						
							| 
									
										
										
										
											2021-01-12 13:50:52 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-03-13 23:44:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-12 13:50:52 +00:00
										 |  |  |     const version = `0.0.${await this.getTotalNumberOfCommits()}`; | 
					
						
							|  |  |  |     core.info(`Generated version ${version} (semantic version couldn't be determined).`); | 
					
						
							|  |  |  |     return version; | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Generate the proper version for unity based on an existing tag. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async generateTagVersion() { | 
					
						
							|  |  |  |     let tag = await this.getTag(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (tag.charAt(0) === 'v') { | 
					
						
							|  |  |  |       tag = tag.slice(1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return tag; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Parses the versionDescription into their named parts. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async parseSemanticVersion() { | 
					
						
							|  |  |  |     const description = await this.getVersionDescription(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-27 23:43:15 +00:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2021-03-13 23:44:01 +00:00
										 |  |  |       const [match, tag, commits, hash] = this.descriptionRegex1.exec(description) as RegExpExecArray; | 
					
						
							| 
									
										
										
										
											2020-04-27 23:43:15 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       return { | 
					
						
							|  |  |  |         match, | 
					
						
							|  |  |  |         tag, | 
					
						
							|  |  |  |         commits, | 
					
						
							|  |  |  |         hash, | 
					
						
							|  |  |  |       }; | 
					
						
							| 
									
										
										
										
											2021-03-13 23:44:01 +00:00
										 |  |  |     } catch { | 
					
						
							| 
									
										
										
										
											2021-01-12 13:50:52 +00:00
										 |  |  |       try { | 
					
						
							| 
									
										
										
										
											2021-03-13 23:44:01 +00:00
										 |  |  |         const [match, tag, commits, hash] = this.descriptionRegex2.exec(description) as RegExpExecArray; | 
					
						
							| 
									
										
										
										
											2021-01-12 13:50:52 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |           match, | 
					
						
							|  |  |  |           tag, | 
					
						
							|  |  |  |           commits, | 
					
						
							|  |  |  |           hash, | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2021-03-13 23:44:01 +00:00
										 |  |  |       } catch { | 
					
						
							| 
									
										
										
										
											2021-01-12 13:50:52 +00:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2021-03-13 23:44:01 +00:00
										 |  |  |           const [match, tag, commits, hash] = this.descriptionRegex3.exec(description) as RegExpExecArray; | 
					
						
							| 
									
										
										
										
											2021-01-12 13:50:52 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |           return { | 
					
						
							|  |  |  |             match, | 
					
						
							|  |  |  |             tag, | 
					
						
							|  |  |  |             commits, | 
					
						
							|  |  |  |             hash, | 
					
						
							|  |  |  |           }; | 
					
						
							| 
									
										
										
										
											2021-03-13 23:44:01 +00:00
										 |  |  |         } catch { | 
					
						
							| 
									
										
										
										
											2021-01-12 13:50:52 +00:00
										 |  |  |           core.warning( | 
					
						
							|  |  |  |             `Failed to parse git describe output or version can not be determined through: "${description}".`, | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |           return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-13 05:22:23 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Returns whether the repository is shallow. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async isShallow() { | 
					
						
							|  |  |  |     const output = await this.git(['rev-parse', '--is-shallow-repository']); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-13 07:40:19 +00:00
										 |  |  |     return output !== 'false\n'; | 
					
						
							| 
									
										
										
										
											2021-02-13 05:22:23 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-01 11:57:42 +00:00
										 |  |  |   /** | 
					
						
							| 
									
										
										
										
											2020-05-01 12:11:37 +00:00
										 |  |  |    * Retrieves refs from the configured remote. | 
					
						
							|  |  |  |    * | 
					
						
							| 
									
										
										
										
											2020-05-01 12:17:30 +00:00
										 |  |  |    * Fetch unshallow for incomplete repository, but fall back to normal fetch. | 
					
						
							|  |  |  |    * | 
					
						
							| 
									
										
										
										
											2020-05-01 12:11:37 +00:00
										 |  |  |    * Note: `--all` should not be used, and would break fetching for push event. | 
					
						
							| 
									
										
										
										
											2020-05-01 11:57:42 +00:00
										 |  |  |    */ | 
					
						
							|  |  |  |   static async fetch() { | 
					
						
							| 
									
										
										
										
											2020-05-01 12:17:30 +00:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2020-05-20 22:06:08 +00:00
										 |  |  |       await this.git(['fetch', '--unshallow']); | 
					
						
							| 
									
										
										
										
											2020-05-01 12:17:30 +00:00
										 |  |  |     } catch (error) { | 
					
						
							| 
									
										
										
										
											2020-05-21 18:51:17 +00:00
										 |  |  |       core.warning(`Fetch --unshallow caught: ${error}`); | 
					
						
							| 
									
										
										
										
											2020-05-20 22:06:08 +00:00
										 |  |  |       await this.git(['fetch']); | 
					
						
							| 
									
										
										
										
											2020-05-01 12:17:30 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Retrieves information about the branch. | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * Format: `v0.12-24-gd2198ab` | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * In this format v0.12 is the latest tag, 24 are the number of commits since, and gd2198ab | 
					
						
							|  |  |  |    * identifies the current commit. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async getVersionDescription() { | 
					
						
							| 
									
										
										
										
											2021-01-26 18:54:07 +00:00
										 |  |  |     return this.git(['describe', '--long', '--tags', '--always', this.sha]); | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Returns whether there are uncommitted changes that are not ignored. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async isDirty() { | 
					
						
							| 
									
										
										
										
											2020-05-20 22:06:08 +00:00
										 |  |  |     const output = await this.git(['status', '--porcelain']); | 
					
						
							| 
									
										
										
										
											2021-05-09 23:08:53 +00:00
										 |  |  |     const isDirty = output !== ''; | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-09 23:08:53 +00:00
										 |  |  |     if (isDirty) { | 
					
						
							|  |  |  |       core.warning('Changes were made to the following files and folders:\n'); | 
					
						
							|  |  |  |       core.warning(output); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return isDirty; | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Get the tag if there is one pointing at HEAD | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async getTag() { | 
					
						
							| 
									
										
										
										
											2021-09-06 16:31:24 +00:00
										 |  |  |     return (await this.git(['tag', '--points-at', 'HEAD'])).trim(); | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Whether or not the repository has any version tags yet. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async hasAnyVersionTags() { | 
					
						
							| 
									
										
										
										
											2020-05-01 18:19:25 +00:00
										 |  |  |     const numberOfCommitsAsString = await System.run('sh', undefined, { | 
					
						
							|  |  |  |       input: Buffer.from('git tag --list --merged HEAD | grep v[0-9]* | wc -l'), | 
					
						
							|  |  |  |       silent: false, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const numberOfCommits = Number.parseInt(numberOfCommitsAsString, 10); | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-01 18:19:25 +00:00
										 |  |  |     return numberOfCommits !== 0; | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Get the total number of commits on head. | 
					
						
							| 
									
										
										
										
											2020-05-02 13:43:21 +00:00
										 |  |  |    * | 
					
						
							|  |  |  |    * Note: HEAD should not be used, as it may be detached, resulting in an additional count. | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |    */ | 
					
						
							|  |  |  |   static async getTotalNumberOfCommits() { | 
					
						
							| 
									
										
										
										
											2020-06-15 20:35:53 +00:00
										 |  |  |     const numberOfCommitsAsString = await this.git(['rev-list', '--count', this.sha]); | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-01 14:52:08 +00:00
										 |  |  |     return Number.parseInt(numberOfCommitsAsString, 10); | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-05-20 22:06:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Run git in the specified project path | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async git(arguments_, options = {}) { | 
					
						
							|  |  |  |     return System.run('git', arguments_, { cwd: this.projectPath, ...options }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-04-26 18:22:09 +00:00
										 |  |  | } |