Skip to content

Commit 43ac335

Browse files
authored
Merge pull request #28 from s0/skip-empty-commits
Allow skipping of empty commits
2 parents ff973c4 + 7fed164 commit 43ac335

File tree

7 files changed

+172
-9
lines changed

7 files changed

+172
-9
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ All configuration options are passed in via `env`, as environment variables.
168168
| `KNOWN_HOSTS_FILE` | Path to a file in the repository that contains the known SSH fingerprints for the target host. | When the target host is not github.com |
169169
| `GITHUB_TOKEN` | Should always be equal to `${{ secrets.GITHUB_TOKEN }}` | When `REPO = self` |
170170
| `SQUASH_HISTORY` | If set to `true`, all previous commits on the target branch will be discarded. For example, if you are deploying a static site with lots of binary artifacts, this can help the repository becoming overly bloated. | No |
171+
| `SKIP_EMPTY_COMMITS` | If set to `true`, commits will only be pushed if the contents of the target branch will be changed as a result. This is useful if, for example, you'd like to easily track which upstream changes result in changes to your target branch. | No |
171172
| `MESSAGE` | A custom template to use as the commit message pushed to the target branch. See [custom commit messages](#custom-commit-messages). | No |
172173
| `TAG` | A string following the [git-check-ref-format](https://git-scm.com/docs/git-check-ref-format) that tags the commit with a lightweight git-tag. | No |
173174

action/dist/index.js

+39-6
Original file line numberDiff line numberDiff line change
@@ -8016,6 +8016,7 @@ var config = (function () {
80168016
var branch = ENV.BRANCH;
80178017
var folder = ENV.FOLDER;
80188018
var squashHistory = ENV.SQUASH_HISTORY === 'true';
8019+
var skipEmptyCommits = ENV.SKIP_EMPTY_COMMITS === 'true';
80198020
var message = ENV.MESSAGE || DEFAULT_MESSAGE;
80208021
var tag = ENV.TAG;
80218022
// Determine the type of URL
@@ -8030,6 +8031,7 @@ var config = (function () {
80308031
branch: branch,
80318032
folder: folder,
80328033
squashHistory: squashHistory,
8034+
skipEmptyCommits: skipEmptyCommits,
80338035
mode: 'self',
80348036
message: message,
80358037
tag: tag,
@@ -8045,6 +8047,7 @@ var config = (function () {
80458047
branch: branch,
80468048
folder: folder,
80478049
squashHistory: squashHistory,
8050+
skipEmptyCommits: skipEmptyCommits,
80488051
mode: 'ssh',
80498052
parsedUrl: parsedUrl,
80508053
privateKey: ENV.SSH_PRIVATE_KEY,
@@ -8085,7 +8088,7 @@ var writeToProcess = function (command, args, opts) { return new Promise(functio
80858088
});
80868089
}); };
80878090
(function () { return __awaiter(void 0, void 0, void 0, function () {
8088-
var TMP_PATH, REPO_TEMP, SSH_AUTH_SOCK, event, _a, _b, name, email, tag, getGitInformation, gitInfo, env, known_hosts, sshAgentMatch, _c, _d, branchCheck, folder, message, forceArg, tagsArg, push;
8091+
var TMP_PATH, REPO_TEMP, SSH_AUTH_SOCK, event, _a, _b, name, email, tag, getGitInformation, gitInfo, env, known_hosts, sshAgentMatch, _c, _d, branchCheck, folder, message, head, currentCommit, previousCommit, forceArg, tagsArg, push;
80898092
var _e, _f;
80908093
return __generator(this, function (_g) {
80918094
switch (_g.label) {
@@ -8313,21 +8316,51 @@ var writeToProcess = function (command, args, opts) { return new Promise(functio
83138316
_g.sent();
83148317
_g.label = 30;
83158318
case 30:
8319+
if (!config.skipEmptyCommits) return [3 /*break*/, 34];
8320+
console.log("##[info] Checking whether contents have changed before pushing");
8321+
return [4 /*yield*/, isomorphic_git_1.default.resolveRef({
8322+
fs: fs,
8323+
dir: REPO_TEMP,
8324+
ref: 'HEAD'
8325+
})];
8326+
case 31:
8327+
head = _g.sent();
8328+
return [4 /*yield*/, isomorphic_git_1.default.readCommit({
8329+
fs: fs,
8330+
dir: REPO_TEMP,
8331+
oid: head,
8332+
})];
8333+
case 32:
8334+
currentCommit = _g.sent();
8335+
if (!(currentCommit.commit.parent.length === 1)) return [3 /*break*/, 34];
8336+
return [4 /*yield*/, isomorphic_git_1.default.readCommit({
8337+
fs: fs,
8338+
dir: REPO_TEMP,
8339+
oid: currentCommit.commit.parent[0],
8340+
})];
8341+
case 33:
8342+
previousCommit = _g.sent();
8343+
if (currentCommit.commit.tree === previousCommit.commit.tree) {
8344+
console.log("##[info] Contents of target repo unchanged, exiting.");
8345+
return [2 /*return*/];
8346+
}
8347+
_g.label = 34;
8348+
case 34:
83168349
console.log("##[info] Pushing");
83178350
forceArg = config.squashHistory ? '-f' : '';
83188351
tagsArg = tag ? '--tags' : '';
83198352
return [4 /*yield*/, exec("git push " + forceArg + " origin \"" + config.branch + "\" " + tagsArg, { env: env, cwd: REPO_TEMP })];
8320-
case 31:
8353+
case 35:
83218354
push = _g.sent();
83228355
console.log(push.stdout);
83238356
console.log("##[info] Deployment Successful");
8324-
if (!(config.mode === 'ssh')) return [3 /*break*/, 33];
8357+
if (!(config.mode === 'ssh')) return [3 /*break*/, 37];
83258358
console.log("##[info] Killing ssh-agent");
83268359
return [4 /*yield*/, exec("ssh-agent -k", { env: env })];
8327-
case 32:
8360+
case 36:
83288361
_g.sent();
8329-
_g.label = 33;
8330-
case 33: return [2 /*return*/];
8362+
_g.label = 37;
8363+
case 37: return [2 /*return*/];
83318364
}
83328365
});
83338366
}); })().catch(function (err) {

action/src/index.ts

+37
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ export interface EnvironmentVariables {
4545
* Set to "true" to clear all of the history of the target branch and force push
4646
*/
4747
SQUASH_HISTORY?: string;
48+
/**
49+
* Set to "true" to avoid pushing commits that don't change any files.
50+
*
51+
* This is useful for example when you want to be able to easily identify
52+
* which upstream changes resulted in changes to this repository.
53+
*/
54+
SKIP_EMPTY_COMMITS?: string;
4855
/**
4956
* An optional template string to use for the commit message,
5057
* if not provided, a default template is used.
@@ -119,6 +126,7 @@ interface BaseConfig {
119126
folder: string;
120127
repo: string;
121128
squashHistory: boolean;
129+
skipEmptyCommits: boolean;
122130
message: string;
123131
tag?: string;
124132
}
@@ -158,6 +166,7 @@ const config: Config = (() => {
158166
const branch = ENV.BRANCH;
159167
const folder = ENV.FOLDER;
160168
const squashHistory = ENV.SQUASH_HISTORY === 'true';
169+
const skipEmptyCommits = ENV.SKIP_EMPTY_COMMITS === 'true';
161170
const message = ENV.MESSAGE || DEFAULT_MESSAGE;
162171
const tag = ENV.TAG;
163172

@@ -173,6 +182,7 @@ const config: Config = (() => {
173182
branch,
174183
folder,
175184
squashHistory,
185+
skipEmptyCommits,
176186
mode: 'self',
177187
message,
178188
tag,
@@ -189,6 +199,7 @@ const config: Config = (() => {
189199
branch,
190200
folder,
191201
squashHistory,
202+
skipEmptyCommits,
192203
mode: 'ssh',
193204
parsedUrl,
194205
privateKey: ENV.SSH_PRIVATE_KEY,
@@ -415,6 +426,32 @@ const writeToProcess = (command: string, args: string[], opts: { env: { [id: str
415426
ref: tag,
416427
});
417428
}
429+
if (config.skipEmptyCommits) {
430+
console.log(`##[info] Checking whether contents have changed before pushing`);
431+
// Before we push, check whether it changed the tree,
432+
// and avoid pushing if not
433+
const head = await git.resolveRef({
434+
fs,
435+
dir: REPO_TEMP,
436+
ref: 'HEAD'
437+
});
438+
const currentCommit = await git.readCommit({
439+
fs,
440+
dir: REPO_TEMP,
441+
oid: head,
442+
});
443+
if (currentCommit.commit.parent.length === 1) {
444+
const previousCommit = await git.readCommit({
445+
fs,
446+
dir: REPO_TEMP,
447+
oid: currentCommit.commit.parent[0],
448+
});
449+
if (currentCommit.commit.tree === previousCommit.commit.tree) {
450+
console.log(`##[info] Contents of target repo unchanged, exiting.`);
451+
return;
452+
}
453+
}
454+
}
418455
console.log(`##[info] Pushing`);
419456
const forceArg = config.squashHistory ? '-f' : '';
420457
const tagsArg = tag ? '--tags' : '';
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
jest.setTimeout(15000);
1+
jest.setTimeout(1000 * 60 * 60);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Skip empty commits 1`] = `
4+
"msg:Update branch-a to output generated at <sha1>
5+
6+
tree:b60735c9b4d9728b47c0f2752bb973cd6f3d9d80
7+
author:s0 <s0@users.noreply.github.com>
8+
msg:Update branch-a to output generated at <sha2>
9+
10+
tree:8bf87c66655949e66937b11593cc4ae732d1f610
11+
author:s0 <s0@users.noreply.github.com>"
12+
`;

action/test/specs/ssh-github.spec.ts

-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ const DATA_DIR = path.join(util.DATA_DIR, 'ssh-no-branch-github');
77

88
it('Deploy to an existing branch on GitHub', async () => {
99

10-
jest.setTimeout(1000 * 60 * 60);
11-
1210
// Create empty repo
1311
await util.mkdir(REPO_DIR);
1412
await util.execWithOutput('git init --bare', { cwd: REPO_DIR });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import * as path from 'path';
2+
3+
import * as util from '../util';
4+
5+
const REPO_DIR = path.join(util.REPOS_DIR, 'ssh-skip-empty-commits.git');
6+
const DATA_DIR = path.join(util.DATA_DIR, 'ssh-skip-empty-commits');
7+
8+
it('Skip empty commits', async () => {
9+
10+
// Create empty repo
11+
await util.mkdir(REPO_DIR);
12+
await util.execWithOutput('git init --bare', { cwd: REPO_DIR });
13+
14+
// Create dummy data
15+
await util.mkdir(DATA_DIR);
16+
await util.mkdir(path.join(DATA_DIR, 'dummy'));
17+
await util.writeFile(path.join(DATA_DIR, 'dummy', 'baz'), 'foobar');
18+
await util.writeFile(path.join(DATA_DIR, 'dummy', '.bat'), 'foobar');
19+
20+
// Run Action
21+
await util.runWithGithubEnv(
22+
path.basename(__filename),
23+
{
24+
REPO: 'ssh://git@git-ssh/git-server/repos/ssh-skip-empty-commits.git',
25+
BRANCH: 'branch-a',
26+
FOLDER: DATA_DIR,
27+
SSH_PRIVATE_KEY: (await util.readFile(util.SSH_PRIVATE_KEY)).toString(),
28+
KNOWN_HOSTS_FILE: util.KNOWN_HOSTS,
29+
SKIP_EMPTY_COMMITS: 'true',
30+
},
31+
's0/test',
32+
{},
33+
's0',
34+
);
35+
const fullSha1 = await util.getFullRepoSha();
36+
// Change files and run action again
37+
await util.writeFile(path.join(DATA_DIR, 'dummy', 'bat'), 'foobar');
38+
await util.runWithGithubEnv(
39+
path.basename(__filename),
40+
{
41+
REPO: 'ssh://git@git-ssh/git-server/repos/ssh-skip-empty-commits.git',
42+
BRANCH: 'branch-a',
43+
FOLDER: DATA_DIR,
44+
SSH_PRIVATE_KEY: (await util.readFile(util.SSH_PRIVATE_KEY)).toString(),
45+
KNOWN_HOSTS_FILE: util.KNOWN_HOSTS,
46+
SKIP_EMPTY_COMMITS: 'true',
47+
},
48+
's0/test',
49+
{},
50+
's0',
51+
);
52+
const fullSha2 = await util.getFullRepoSha();
53+
// Run the action again with no content changes to test skip behaviour
54+
await util.runWithGithubEnv(
55+
path.basename(__filename),
56+
{
57+
REPO: 'ssh://git@git-ssh/git-server/repos/ssh-skip-empty-commits.git',
58+
BRANCH: 'branch-a',
59+
FOLDER: DATA_DIR,
60+
SSH_PRIVATE_KEY: (await util.readFile(util.SSH_PRIVATE_KEY)).toString(),
61+
KNOWN_HOSTS_FILE: util.KNOWN_HOSTS,
62+
SKIP_EMPTY_COMMITS: 'true',
63+
},
64+
's0/test',
65+
{},
66+
's0',
67+
);
68+
69+
// Check that the log of the repo is as expected
70+
// (check tree-hash, commit message, and author)
71+
// TODO: test {msg} placeholder and running action outside of a git repo
72+
let log = (await util.exec(
73+
'git log --pretty="format:msg:%B%ntree:%T%nauthor:%an <%ae>" branch-a',
74+
{
75+
cwd: REPO_DIR
76+
}
77+
)).stdout;
78+
const sha1 = fullSha1.substr(0, 7);
79+
const sha2 = fullSha2.substr(0, 7);
80+
const cleanedLog = log.replace(sha1, '<sha1>').replace(sha2, '<sha2>');
81+
expect(cleanedLog).toMatchSnapshot();
82+
});

0 commit comments

Comments
 (0)