UNPKG

3.96 kBJavaScriptView Raw
1'use strict';
2const execa = require('execa');
3const escapeStringRegexp = require('escape-string-regexp');
4const {verifyRequirementSatisfied} = require('./version');
5
6exports.latestTag = async () => {
7 const {stdout} = await execa('git', ['describe', '--abbrev=0', '--tags']);
8 return stdout;
9};
10
11const firstCommit = async () => {
12 const {stdout} = await execa('git', ['rev-list', '--max-parents=0', 'HEAD']);
13 return stdout;
14};
15
16exports.latestTagOrFirstCommit = async () => {
17 let latest;
18 try {
19 // In case a previous tag exists, we use it to compare the current repo status to.
20 latest = await exports.latestTag();
21 } catch (_) {
22 // Otherwise, we fallback to using the first commit for comparison.
23 latest = await firstCommit();
24 }
25
26 return latest;
27};
28
29exports.hasUpstream = async () => {
30 const escapedCurrentBranch = escapeStringRegexp(await exports.currentBranch());
31 const {stdout} = await execa('git', ['status', '--short', '--branch', '--porcelain']);
32
33 return new RegExp(String.raw`^## ${escapedCurrentBranch}\.\.\..+\/${escapedCurrentBranch}`).test(stdout);
34};
35
36exports.currentBranch = async () => {
37 const {stdout} = await execa('git', ['symbolic-ref', '--short', 'HEAD']);
38 return stdout;
39};
40
41exports.verifyCurrentBranchIsMaster = async () => {
42 if (await exports.currentBranch() !== 'master') {
43 throw new Error('Not on `master` branch. Use --any-branch to publish anyway.');
44 }
45};
46
47exports.isWorkingTreeClean = async () => {
48 try {
49 const {stdout: status} = await execa('git', ['status', '--porcelain']);
50 if (status !== '') {
51 return false;
52 }
53
54 return true;
55 } catch (_) {
56 return false;
57 }
58};
59
60exports.verifyWorkingTreeIsClean = async () => {
61 if (!(await exports.isWorkingTreeClean())) {
62 throw new Error('Unclean working tree. Commit or stash changes first.');
63 }
64};
65
66exports.isRemoteHistoryClean = async () => {
67 let history;
68 try { // Gracefully handle no remote set up.
69 const {stdout} = await execa('git', ['rev-list', '--count', '--left-only', '@{u}...HEAD']);
70 history = stdout;
71 } catch (_) {}
72
73 if (history && history !== '0') {
74 return false;
75 }
76
77 return true;
78};
79
80exports.verifyRemoteHistoryIsClean = async () => {
81 if (!(await exports.isRemoteHistoryClean())) {
82 throw new Error('Remote history differs. Please pull changes.');
83 }
84};
85
86exports.verifyRemoteIsValid = async () => {
87 try {
88 await execa('git', ['ls-remote', 'origin', 'HEAD']);
89 } catch (error) {
90 throw new Error(error.stderr.replace('fatal:', 'Git fatal error:'));
91 }
92};
93
94exports.fetch = async () => {
95 await execa('git', ['fetch']);
96};
97
98exports.tagExistsOnRemote = async tagName => {
99 try {
100 const {stdout: revInfo} = await execa('git', ['rev-parse', '--quiet', '--verify', `refs/tags/${tagName}`]);
101
102 if (revInfo) {
103 return true;
104 }
105
106 return false;
107 } catch (error) {
108 // Command fails with code 1 and no output if the tag does not exist, even though `--quiet` is provided
109 // https://github.com/sindresorhus/np/pull/73#discussion_r72385685
110 if (error.stdout === '' && error.stderr === '') {
111 return false;
112 }
113
114 throw error;
115 }
116};
117
118exports.verifyTagDoesNotExistOnRemote = async tagName => {
119 if (await exports.tagExistsOnRemote(tagName)) {
120 throw new Error(`Git tag \`${tagName}\` already exists.`);
121 }
122};
123
124exports.commitLogFromRevision = async revision => {
125 const {stdout} = await execa('git', ['log', '--format=%s %h', `${revision}..HEAD`]);
126 return stdout;
127};
128
129exports.push = async () => {
130 await execa('git', ['push', '--follow-tags']);
131};
132
133exports.deleteTag = async tagName => {
134 await execa('git', ['tag', '--delete', tagName]);
135};
136
137exports.removeLastCommit = async () => {
138 await execa('git', ['reset', '--hard', 'HEAD~1']);
139};
140
141const gitVersion = async () => {
142 const {stdout} = await execa('git', ['version']);
143 return stdout.match(/git version (\d+\.\d+\.\d+).*/)[1];
144};
145
146exports.verifyRecentGitVersion = async () => {
147 const installedVersion = await gitVersion();
148
149 verifyRequirementSatisfied('git', installedVersion);
150};