From 33ef60fcefadfb46346ef62281d38c5fdf517875 Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Tue, 26 Dec 2023 21:03:40 +0200 Subject: [PATCH 01/14] feat(skip-ci): adding logic to walk forward through skipped commits --- .nvmrc | 1 + dist/index.js | 31 +++++- find-successful-workflow.ts | 189 +++++++++++++++++++++++++++++++++++- 3 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..eb800ed --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v18.19.0 diff --git a/dist/index.js b/dist/index.js index 7d60c65..4675189 100644 --- a/dist/index.js +++ b/dist/index.js @@ -37960,6 +37960,7 @@ function proxyPlugin(octokit) { } }); } +const messagesToSkip = ["[skip ci]"]; /** * Find last successful workflow run on the repo */ @@ -37993,7 +37994,35 @@ function findSuccessfulCommit(workflow_id, run_id, owner, repo, branch, lastSucc status: "success", }) .then(({ data: { workflow_runs } }) => workflow_runs.map((run) => run.head_sha)); - return yield findExistingCommit(octokit, branch, shas); + const headSha = yield findExistingCommit(octokit, branch, shas); + if (!headSha) { + return headSha; + } + process.stdout.write(`Checking commits from "${headSha}" onwards for skip ci messages\n`); + // now we have the commit, step forward and find the next few commits that don't have [skip ci] + const commits = (yield octokit.request("GET /repos/{owner}/{repo}/commits", { + owner, + repo, + sha: headSha, + per_page: 100, + })).data.map((c) => { + return { + sha: c.sha, + message: c.commit.message, + }; + }); + process.stdout.write(`Got ${commits.length} commits:\n`); + let shaResult = headSha; + for (const commit of commits) { + const containsAnySkipMessages = messagesToSkip.some((m) => commit.message.indexOf(m) >= 0); + process.stdout.write(`[${commit.sha}][${containsAnySkipMessages}]: ${commit.message}\n`); + if (containsAnySkipMessages) { + shaResult = commit.sha; + continue; + } + return shaResult; + } + return shaResult; }); } function findMergeBaseRef() { diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index 0504758..5d41ab0 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -81,6 +81,13 @@ let BASE_SHA: string; process.stdout.write( `WARNING: Unable to find a successful workflow run on 'origin/${mainBranchName}', or the latest successful workflow was connected to a commit which no longer exists on that branch (e.g. if that branch was rebased)\n`, ); + process.stdout.write( + `We are therefore defaulting to use HEAD~1 on 'origin/${mainBranchName}'\n`, + ); + process.stdout.write("\n"); + process.stdout.write( + `NOTE: You can instead make this a hard error by setting 'error-on-no-successful-workflow' on the action in your workflow.\n`, + ); if (fallbackSHA) { BASE_SHA = fallbackSHA; process.stdout.write(`Using provided fallback SHA: ${fallbackSHA}\n`); @@ -145,6 +152,8 @@ function proxyPlugin(octokit: Octokit): void { }); } +const messagesToSkip = ["[skip ci]"]; + /** * Find last successful workflow run on the repo */ @@ -193,7 +202,44 @@ async function findSuccessfulCommit( workflow_runs.map((run: { head_sha: any }) => run.head_sha), ); - return await findExistingCommit(octokit, branch, shas); + const headSha = await findExistingCommit(octokit, branch, shas); + if (!headSha) { + return headSha; + } + process.stdout.write( + `Checking commits from "${headSha}" onwards for skip ci messages\n`, + ); + // now we have the commit, step forward and find the next few commits that don't have [skip ci] + const commits = ( + await octokit.request("GET /repos/{owner}/{repo}/commits", { + owner, + repo, + sha: headSha, + per_page: 100, + }) + ).data.map((c) => { + return { + sha: c.sha, + message: c.commit.message, + }; + }); + process.stdout.write(`Got ${commits.length} commits:\n`); + + let shaResult = headSha; + for (const commit of commits) { + const containsAnySkipMessages = messagesToSkip.some( + (m) => commit.message.indexOf(m) >= 0, + ); + process.stdout.write( + `[${commit.sha}][${containsAnySkipMessages}]: ${commit.message}\n`, + ); + if (containsAnySkipMessages) { + shaResult = commit.sha; + continue; + } + return shaResult; + } + return shaResult; } async function findMergeBaseRef(): Promise { @@ -286,3 +332,144 @@ async function commitExists( function stripNewLineEndings(string: string): string { return string.replace("\n", ""); } + +export interface Commit { + url: string; + sha: string; + node_id: string; + html_url: string; + comments_url: string; + commit: { + url: string; + author: null | GitUser; + committer: null | GitUser1; + message: string; + comment_count: number; + tree: { + sha: string; + url: string; + [k: string]: unknown; + }; + verification?: Verification; + [k: string]: unknown; + }; + author: null | SimpleUser; + committer: null | SimpleUser1; + parents: { + sha: string; + url: string; + html_url?: string; + [k: string]: unknown; + }[]; + stats?: { + additions?: number; + deletions?: number; + total?: number; + [k: string]: unknown; + }; + files?: DiffEntry[]; + [k: string]: unknown; +} +/** + * Metaproperties for Git author/committer information. + */ +export interface GitUser { + name?: string; + email?: string; + date?: string; + [k: string]: unknown; +} +/** + * Metaproperties for Git author/committer information. + */ +export interface GitUser1 { + name?: string; + email?: string; + date?: string; + [k: string]: unknown; +} +export interface Verification { + verified: boolean; + reason: string; + payload: string | null; + signature: string | null; + [k: string]: unknown; +} +/** + * A GitHub user. + */ +export interface SimpleUser { + name?: string | null; + email?: string | null; + login: string; + id: number; + node_id: string; + avatar_url: string; + gravatar_id: string | null; + url: string; + html_url: string; + followers_url: string; + following_url: string; + gists_url: string; + starred_url: string; + subscriptions_url: string; + organizations_url: string; + repos_url: string; + events_url: string; + received_events_url: string; + type: string; + site_admin: boolean; + starred_at?: string; + [k: string]: unknown; +} +/** + * A GitHub user. + */ +export interface SimpleUser1 { + name?: string | null; + email?: string | null; + login: string; + id: number; + node_id: string; + avatar_url: string; + gravatar_id: string | null; + url: string; + html_url: string; + followers_url: string; + following_url: string; + gists_url: string; + starred_url: string; + subscriptions_url: string; + organizations_url: string; + repos_url: string; + events_url: string; + received_events_url: string; + type: string; + site_admin: boolean; + starred_at?: string; + [k: string]: unknown; +} +/** + * Diff Entry + */ +export interface DiffEntry { + sha: string; + filename: string; + status: + | "added" + | "removed" + | "modified" + | "renamed" + | "copied" + | "changed" + | "unchanged"; + additions: number; + deletions: number; + changes: number; + blob_url: string; + raw_url: string; + contents_url: string; + patch?: string; + previous_filename?: string; + [k: string]: unknown; +} From 90d8b35d74929379671c2eaaf4c22163314d810f Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Tue, 26 Dec 2023 21:34:31 +0200 Subject: [PATCH 02/14] fix(skip-commit): filtering commits --- dist/index.js | 15 +++++++++++++-- dist/licenses.txt | 25 +++++++++++++++++++++++++ find-successful-workflow.ts | 8 ++++++-- package.json | 1 + yarn.lock | 5 +++++ 5 files changed, 50 insertions(+), 4 deletions(-) diff --git a/dist/index.js b/dist/index.js index 4675189..2d2eb46 100644 --- a/dist/index.js +++ b/dist/index.js @@ -9794,6 +9794,13 @@ function removeHook(state, name, method) { } +/***/ }), + +/***/ 7401: +/***/ (function(module) { + +!function(t,e){ true?module.exports=e():0}(this,(function(){"use strict";var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",c="month",f="quarter",h="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return"["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return!r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()1)return t(u[0])}else{var a=e.name;D[a]=e,i=a}return!r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,!0),this.parse(t),this.$x=this.$x||t.x||{},this[p]=!0}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init()},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},m.$utils=function(){return b},m.isValid=function(){return!(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t) c.sha !== headSha).date); + const filtered = commits.filter((c) => dayjs(c.date).isAfter(dayJsStart)); + process.stdout.write(`Got ${filtered.length} commits:\n`); let shaResult = headSha; - for (const commit of commits) { + for (const commit of filtered) { const containsAnySkipMessages = messagesToSkip.some((m) => commit.message.indexOf(m) >= 0); process.stdout.write(`[${commit.sha}][${containsAnySkipMessages}]: ${commit.message}\n`); if (containsAnySkipMessages) { diff --git a/dist/licenses.txt b/dist/licenses.txt index dbf8dc3..cb9a031 100644 --- a/dist/licenses.txt +++ b/dist/licenses.txt @@ -509,6 +509,31 @@ Apache-2.0 limitations under the License. +dayjs +MIT +MIT License + +Copyright (c) 2018-present, iamkun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + debug MIT (The MIT License) diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index 5d41ab0..b9607fe 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -5,6 +5,7 @@ import { spawnSync } from "child_process"; import { existsSync } from "fs"; import { HttpsProxyAgent } from "https-proxy-agent"; import { getProxyForUrl } from "proxy-from-env"; +import * as dayjs from "dayjs"; const { runId, @@ -221,12 +222,15 @@ async function findSuccessfulCommit( return { sha: c.sha, message: c.commit.message, + date: c.commit.committer.date, }; }); - process.stdout.write(`Got ${commits.length} commits:\n`); + const dayJsStart = dayjs(commits.find((c) => c.sha !== headSha).date); + const filtered = commits.filter((c) => dayjs(c.date).isAfter(dayJsStart)); + process.stdout.write(`Got ${filtered.length} commits:\n`); let shaResult = headSha; - for (const commit of commits) { + for (const commit of filtered) { const containsAnySkipMessages = messagesToSkip.some( (m) => commit.message.indexOf(m) >= 0, ); diff --git a/package.json b/package.json index bb7f2fc..7d2fc23 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@actions/core": "^1.10.0", "@actions/github": "^5.1.1", "@octokit/action": "^6.0.6", + "dayjs": "^1.11.10", "https-proxy-agent": "7.0.2", "proxy-from-env": "1.1.0" }, diff --git a/yarn.lock b/yarn.lock index b2a942e..59a5bb8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -339,6 +339,11 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +dayjs@^1.11.10: + version "1.11.10" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" + integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== + debug@4, debug@4.3.4, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" From 01cb594f06bb8811c6b284019f26503b34caeb18 Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Wed, 27 Dec 2023 06:43:43 +0200 Subject: [PATCH 03/14] fix(skip-ci): improving logic for finding the skipped-ci commit --- dist/index.js | 119 +++++++++++++++++--------- dist/licenses.txt | 25 ------ find-successful-workflow.ts | 164 ++++++++++++++++++++++++++---------- package.json | 1 - yarn.lock | 5 -- 5 files changed, 198 insertions(+), 116 deletions(-) diff --git a/dist/index.js b/dist/index.js index 2d2eb46..bdd711b 100644 --- a/dist/index.js +++ b/dist/index.js @@ -9794,13 +9794,6 @@ function removeHook(state, name, method) { } -/***/ }), - -/***/ 7401: -/***/ (function(module) { - -!function(t,e){ true?module.exports=e():0}(this,(function(){"use strict";var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",c="month",f="quarter",h="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return"["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return!r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()1)return t(u[0])}else{var a=e.name;D[a]=e,i=a}return!r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,!0),this.parse(t),this.$x=this.$x||t.x||{},this[p]=!0}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init()},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},m.$utils=function(){return b},m.isValid=function(){return!(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t) workflow_runs.map((run) => run.head_sha)); - const headSha = yield findExistingCommit(octokit, branch, shas); - if (!headSha) { - return headSha; - } - process.stdout.write(`Checking commits from "${headSha}" onwards for skip ci messages\n`); - // now we have the commit, step forward and find the next few commits that don't have [skip ci] - const commits = (yield octokit.request("GET /repos/{owner}/{repo}/commits", { - owner, - repo, - sha: headSha, - per_page: 100, - })).data.map((c) => { - return { - sha: c.sha, - message: c.commit.message, - date: c.commit.committer.date, - }; - }); - const dayJsStart = dayjs(commits.find((c) => c.sha !== headSha).date); - const filtered = commits.filter((c) => dayjs(c.date).isAfter(dayJsStart)); - process.stdout.write(`Got ${filtered.length} commits:\n`); - let shaResult = headSha; - for (const commit of filtered) { - const containsAnySkipMessages = messagesToSkip.some((m) => commit.message.indexOf(m) >= 0); - process.stdout.write(`[${commit.sha}][${containsAnySkipMessages}]: ${commit.message}\n`); - if (containsAnySkipMessages) { - shaResult = commit.sha; - continue; - } - return shaResult; - } - return shaResult; + return yield findExistingCommit(octokit, branch, shas); }); } function findMergeBaseRef() { @@ -38113,6 +38079,77 @@ function commitExists(octokit, branchName, commitSha) { function stripNewLineEndings(string) { return string.replace("\n", ""); } +/** + * Takes in an sha and then will walk forward in time finding the last commit that was a skip-ci type to use that as the new base + */ +function findLastSkippedCommitAfterSha(baseSha, headSha, messagesToSkip = [], branchName) { + return __awaiter(this, void 0, void 0, function* () { + process.stdout.write(`Checking commits from "${baseSha}" onwards for skip ci messages\n`); + if (!messagesToSkip.length) { + process.stdout.write(`messagesToSkip was empty, returning\n`); + return; + } + const octokit = new ProxifiedClient(); + const baseCommit = yield getCommit(octokit, baseSha); + const headCommit = yield getCommit(octokit, headSha); + const commitsToCheck = (yield findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit)).filter((c) => c.sha !== baseSha); + process.stdout.write(`Got ${commitsToCheck.length} total commits to check:\n`); + let newBaseSha = baseSha; + for (const commit of commitsToCheck) { + const containsAnySkipMessages = messagesToSkip.some((m) => commit.message.indexOf(m) >= 0); + process.stdout.write(`[${commit.sha}][${containsAnySkipMessages}]: ${commit.message}\n`); + if (containsAnySkipMessages) { + newBaseSha = commit.sha; + continue; + } + return newBaseSha; + } + return newBaseSha; + }); +} +function findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit, page = 1) { + return __awaiter(this, void 0, void 0, function* () { + process.stdout.write(`Finding all commits on branch "${branchName}" between ${baseCommit.sha}|${baseCommit.date} and ${headCommit.sha}|${headCommit.date}, page: ${page}\n`); + let commits = (yield octokit.request("GET /repos/{owner}/{repo}/commits", { + owner, + repo, + sha: branchName, + since: baseCommit.date, + until: headCommit.date, + page, + per_page: 100, + })).data.map(getSimplifiedCommit); + const resultsContainsHead = commits.some((c) => c.sha === headCommit.sha); + if (!resultsContainsHead) { + //need to get the next page as we haven't reached the head yet. + commits = commits.concat(yield findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit, page + 1)); + } + return commits; + }); +} +/** + * Gets the specified commit by its SHA + */ +function getCommit(octokit, commitSha) { + return __awaiter(this, void 0, void 0, function* () { + const fullCommit = (yield octokit.request("GET /repos/{owner}/{repo}/commits/{commit_sha}", { + owner, + repo, + commit_sha: commitSha, + })).data; + return getSimplifiedCommit(fullCommit); + }); +} +/** + * strips out properties from the GitHub commit object to a simplified version for working with + */ +function getSimplifiedCommit(commit) { + return { + sha: commit.sha, + message: commit.commit.message, + date: commit.commit.committer.date, + }; +} /***/ }), diff --git a/dist/licenses.txt b/dist/licenses.txt index cb9a031..dbf8dc3 100644 --- a/dist/licenses.txt +++ b/dist/licenses.txt @@ -509,31 +509,6 @@ Apache-2.0 limitations under the License. -dayjs -MIT -MIT License - -Copyright (c) 2018-present, iamkun - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - debug MIT (The MIT License) diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index b9607fe..d5b9088 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -5,7 +5,6 @@ import { spawnSync } from "child_process"; import { existsSync } from "fs"; import { HttpsProxyAgent } from "https-proxy-agent"; import { getProxyForUrl } from "proxy-from-env"; -import * as dayjs from "dayjs"; const { runId, @@ -72,6 +71,17 @@ let BASE_SHA: string; core.setFailed(e.message); return; } + //todo move to inputs + const getLastSkippedCommitAfterBase = true; + const messagesToSkip = ["[skip ci]"]; + if (getLastSkippedCommitAfterBase && BASE_SHA) { + BASE_SHA = await findLastSkippedCommitAfterSha( + BASE_SHA, + HEAD_SHA, + messagesToSkip, + mainBranchName, + ); + } if (!BASE_SHA) { if (errorOnNoSuccessfulWorkflow === "true") { @@ -153,8 +163,6 @@ function proxyPlugin(octokit: Octokit): void { }); } -const messagesToSkip = ["[skip ci]"]; - /** * Find last successful workflow run on the repo */ @@ -203,47 +211,7 @@ async function findSuccessfulCommit( workflow_runs.map((run: { head_sha: any }) => run.head_sha), ); - const headSha = await findExistingCommit(octokit, branch, shas); - if (!headSha) { - return headSha; - } - process.stdout.write( - `Checking commits from "${headSha}" onwards for skip ci messages\n`, - ); - // now we have the commit, step forward and find the next few commits that don't have [skip ci] - const commits = ( - await octokit.request("GET /repos/{owner}/{repo}/commits", { - owner, - repo, - sha: headSha, - per_page: 100, - }) - ).data.map((c) => { - return { - sha: c.sha, - message: c.commit.message, - date: c.commit.committer.date, - }; - }); - const dayJsStart = dayjs(commits.find((c) => c.sha !== headSha).date); - const filtered = commits.filter((c) => dayjs(c.date).isAfter(dayJsStart)); - process.stdout.write(`Got ${filtered.length} commits:\n`); - - let shaResult = headSha; - for (const commit of filtered) { - const containsAnySkipMessages = messagesToSkip.some( - (m) => commit.message.indexOf(m) >= 0, - ); - process.stdout.write( - `[${commit.sha}][${containsAnySkipMessages}]: ${commit.message}\n`, - ); - if (containsAnySkipMessages) { - shaResult = commit.sha; - continue; - } - return shaResult; - } - return shaResult; + return await findExistingCommit(octokit, branch, shas); } async function findMergeBaseRef(): Promise { @@ -336,6 +304,114 @@ async function commitExists( function stripNewLineEndings(string: string): string { return string.replace("\n", ""); } +/** + * Takes in an sha and then will walk forward in time finding the last commit that was a skip-ci type to use that as the new base + */ +async function findLastSkippedCommitAfterSha( + baseSha: string, + headSha: string, + messagesToSkip: string[] = [], + branchName: string, +): Promise { + process.stdout.write( + `Checking commits from "${baseSha}" onwards for skip ci messages\n`, + ); + if (!messagesToSkip.length) { + process.stdout.write(`messagesToSkip was empty, returning\n`); + return; + } + const octokit = new ProxifiedClient(); + const baseCommit = await getCommit(octokit, baseSha); + const headCommit = await getCommit(octokit, headSha); + const commitsToCheck = ( + await findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit) + ).filter((c) => c.sha !== baseSha); + process.stdout.write( + `Got ${commitsToCheck.length} total commits to check:\n`, + ); + + let newBaseSha = baseSha; + for (const commit of commitsToCheck) { + const containsAnySkipMessages = messagesToSkip.some( + (m) => commit.message.indexOf(m) >= 0, + ); + process.stdout.write( + `[${commit.sha}][${containsAnySkipMessages}]: ${commit.message}\n`, + ); + if (containsAnySkipMessages) { + newBaseSha = commit.sha; + continue; + } + return newBaseSha; + } + return newBaseSha; +} + +async function findAllCommitsBetweenShas( + octokit: Octokit, + branchName: string, + baseCommit: SimplifiedCommit, + headCommit: SimplifiedCommit, + page = 1, +): Promise { + process.stdout.write( + `Finding all commits on branch "${branchName}" between ${baseCommit.sha}|${baseCommit.date} and ${headCommit.sha}|${headCommit.date}, page: ${page}\n`, + ); + let commits = ( + await octokit.request("GET /repos/{owner}/{repo}/commits", { + owner, + repo, + sha: branchName, + since: baseCommit.date, + until: headCommit.date, + page, + per_page: 100, + }) + ).data.map(getSimplifiedCommit); + const resultsContainsHead = commits.some((c) => c.sha === headCommit.sha); + if (!resultsContainsHead) { + //need to get the next page as we haven't reached the head yet. + commits = commits.concat( + await findAllCommitsBetweenShas( + octokit, + branchName, + baseCommit, + headCommit, + page + 1, + ), + ); + } + return commits; +} + +/** + * Gets the specified commit by its SHA + */ +async function getCommit(octokit: Octokit, commitSha: string) { + const fullCommit = ( + await octokit.request("GET /repos/{owner}/{repo}/commits/{commit_sha}", { + owner, + repo, + commit_sha: commitSha, + }) + ).data; + return getSimplifiedCommit(fullCommit); +} +/** + * strips out properties from the GitHub commit object to a simplified version for working with + */ +function getSimplifiedCommit(commit: Commit): SimplifiedCommit { + return { + sha: commit.sha, + message: commit.commit.message, + date: commit.commit.committer.date, + }; +} +interface SimplifiedCommit { + sha: string; + message: string; + date: string; +} export interface Commit { url: string; diff --git a/package.json b/package.json index 7d2fc23..bb7f2fc 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "@actions/core": "^1.10.0", "@actions/github": "^5.1.1", "@octokit/action": "^6.0.6", - "dayjs": "^1.11.10", "https-proxy-agent": "7.0.2", "proxy-from-env": "1.1.0" }, diff --git a/yarn.lock b/yarn.lock index 59a5bb8..b2a942e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -339,11 +339,6 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -dayjs@^1.11.10: - version "1.11.10" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" - integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== - debug@4, debug@4.3.4, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" From 19fce662b88ff7744d05a36e8c3ca52869b34006 Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Wed, 27 Dec 2023 07:01:25 +0200 Subject: [PATCH 04/14] fix(skip-ci): stripping line endings before passing params in to prevent error --- dist/index.js | 4 +++- find-successful-workflow.ts | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/dist/index.js b/dist/index.js index bdd711b..727e86f 100644 --- a/dist/index.js +++ b/dist/index.js @@ -37910,7 +37910,7 @@ let BASE_SHA; const getLastSkippedCommitAfterBase = true; const messagesToSkip = ["[skip ci]"]; if (getLastSkippedCommitAfterBase && BASE_SHA) { - BASE_SHA = yield findLastSkippedCommitAfterSha(BASE_SHA, HEAD_SHA, messagesToSkip, mainBranchName); + BASE_SHA = yield findLastSkippedCommitAfterSha(stripNewLineEndings(BASE_SHA), stripNewLineEndings(HEAD_SHA), messagesToSkip, mainBranchName); } if (!BASE_SHA) { if (errorOnNoSuccessfulWorkflow === "true") { @@ -38132,11 +38132,13 @@ function findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit, */ function getCommit(octokit, commitSha) { return __awaiter(this, void 0, void 0, function* () { + process.stdout.write(`Getting commit for sha: ${commitSha}\n`); const fullCommit = (yield octokit.request("GET /repos/{owner}/{repo}/commits/{commit_sha}", { owner, repo, commit_sha: commitSha, })).data; + process.stdout.write(`SHA get succeeded: ${commitSha}\n`); return getSimplifiedCommit(fullCommit); }); } diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index d5b9088..a6aa22f 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -76,8 +76,8 @@ let BASE_SHA: string; const messagesToSkip = ["[skip ci]"]; if (getLastSkippedCommitAfterBase && BASE_SHA) { BASE_SHA = await findLastSkippedCommitAfterSha( - BASE_SHA, - HEAD_SHA, + stripNewLineEndings(BASE_SHA), + stripNewLineEndings(HEAD_SHA), messagesToSkip, mainBranchName, ); @@ -388,6 +388,7 @@ async function findAllCommitsBetweenShas( * Gets the specified commit by its SHA */ async function getCommit(octokit: Octokit, commitSha: string) { + process.stdout.write(`Getting commit for sha: ${commitSha}\n`); const fullCommit = ( await octokit.request("GET /repos/{owner}/{repo}/commits/{commit_sha}", { owner, @@ -395,6 +396,7 @@ async function getCommit(octokit: Octokit, commitSha: string) { commit_sha: commitSha, }) ).data; + process.stdout.write(`SHA get succeeded: ${commitSha}\n`); return getSimplifiedCommit(fullCommit); } /** From 1ecd4dd81764ebfbb75109f18fd434b386e0a2a6 Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Wed, 27 Dec 2023 07:10:41 +0200 Subject: [PATCH 05/14] fix(skip-ci): sorting commits before stepping through --- dist/index.js | 7 ++++--- find-successful-workflow.ts | 9 ++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dist/index.js b/dist/index.js index 727e86f..4501475 100644 --- a/dist/index.js +++ b/dist/index.js @@ -38092,10 +38092,11 @@ function findLastSkippedCommitAfterSha(baseSha, headSha, messagesToSkip = [], br const octokit = new ProxifiedClient(); const baseCommit = yield getCommit(octokit, baseSha); const headCommit = yield getCommit(octokit, headSha); - const commitsToCheck = (yield findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit)).filter((c) => c.sha !== baseSha); - process.stdout.write(`Got ${commitsToCheck.length} total commits to check:\n`); + const commits = (yield findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit)).filter((c) => c.sha !== baseSha); + process.stdout.write(`Got ${commits.length} total commits to check:\n`); + const sortedCommits = commits.sort((a, b) => a.date.localeCompare(b.date)); let newBaseSha = baseSha; - for (const commit of commitsToCheck) { + for (const commit of sortedCommits) { const containsAnySkipMessages = messagesToSkip.some((m) => commit.message.indexOf(m) >= 0); process.stdout.write(`[${commit.sha}][${containsAnySkipMessages}]: ${commit.message}\n`); if (containsAnySkipMessages) { diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index a6aa22f..b3be788 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -323,15 +323,14 @@ async function findLastSkippedCommitAfterSha( const octokit = new ProxifiedClient(); const baseCommit = await getCommit(octokit, baseSha); const headCommit = await getCommit(octokit, headSha); - const commitsToCheck = ( + const commits = ( await findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit) ).filter((c) => c.sha !== baseSha); - process.stdout.write( - `Got ${commitsToCheck.length} total commits to check:\n`, - ); + process.stdout.write(`Got ${commits.length} total commits to check:\n`); + const sortedCommits = commits.sort((a, b) => a.date.localeCompare(b.date)); let newBaseSha = baseSha; - for (const commit of commitsToCheck) { + for (const commit of sortedCommits) { const containsAnySkipMessages = messagesToSkip.some( (m) => commit.message.indexOf(m) >= 0, ); From 8e88f73247ea81602e5cd1048d6842b347d602ea Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Wed, 27 Dec 2023 07:39:59 +0200 Subject: [PATCH 06/14] chore(skip-ci): creating input, cleanup --- action.yml | 3 + dist/index.js | 24 ++++- find-successful-workflow.ts | 180 ++++++------------------------------ 3 files changed, 53 insertions(+), 154 deletions(-) diff --git a/action.yml b/action.yml index 1bfdd97..e9391b9 100644 --- a/action.yml +++ b/action.yml @@ -23,6 +23,9 @@ inputs: default: "." workflow-id: description: "The ID of the workflow to track or name of the file name. E.g. ci.yml. Defaults to current workflow" + get-last-skip-ci-commit: + default: false + description: "Specifies if the NX_BASE SHA should be set to the last [skip-ci] commit that occurred after the original NX_BASE. Useful if your GitHub action commits tags to the repository which should not be included" outputs: base: diff --git a/dist/index.js b/dist/index.js index 4501475..eb838a9 100644 --- a/dist/index.js +++ b/dist/index.js @@ -37867,7 +37867,11 @@ const errorOnNoSuccessfulWorkflow = process.argv[4]; const lastSuccessfulEvent = process.argv[5]; const workingDirectory = process.argv[6]; const workflowId = process.argv[7]; +<<<<<<< HEAD const fallbackSHA = process.argv[8]; +======= +const getLastSkippedCommitAfterBase = process.argv[8]; +>>>>>>> e270a80 (chore(skip-ci): creating input, cleanup) const defaultWorkingDirectory = "."; const ProxifiedClient = action_1.Octokit.plugin(proxyPlugin); let BASE_SHA; @@ -37907,10 +37911,21 @@ let BASE_SHA; return; } //todo move to inputs - const getLastSkippedCommitAfterBase = true; - const messagesToSkip = ["[skip ci]"]; + const messagesToSkip = [ + "[skip ci]", + "[ci skip]", + "[no ci]", + "[skip actions]", + "[actions skip]", + ]; if (getLastSkippedCommitAfterBase && BASE_SHA) { - BASE_SHA = yield findLastSkippedCommitAfterSha(stripNewLineEndings(BASE_SHA), stripNewLineEndings(HEAD_SHA), messagesToSkip, mainBranchName); + try { + BASE_SHA = yield findLastSkippedCommitAfterSha(stripNewLineEndings(BASE_SHA), stripNewLineEndings(HEAD_SHA), messagesToSkip, mainBranchName); + } + catch (e) { + core.setFailed(e.message); + return; + } } if (!BASE_SHA) { if (errorOnNoSuccessfulWorkflow === "true") { @@ -38108,6 +38123,9 @@ function findLastSkippedCommitAfterSha(baseSha, headSha, messagesToSkip = [], br return newBaseSha; }); } +/** + * Finds all commits between two provided commits + */ function findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit, page = 1) { return __awaiter(this, void 0, void 0, function* () { process.stdout.write(`Finding all commits on branch "${branchName}" between ${baseCommit.sha}|${baseCommit.date} and ${headCommit.sha}|${headCommit.date}, page: ${page}\n`); diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index b3be788..f70042b 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -18,6 +18,7 @@ const lastSuccessfulEvent = process.argv[5]; const workingDirectory = process.argv[6]; const workflowId = process.argv[7]; const fallbackSHA = process.argv[8]; +const getLastSkippedCommitAfterBase = process.argv[9]; const defaultWorkingDirectory = "."; const ProxifiedClient = Octokit.plugin(proxyPlugin); @@ -72,15 +73,25 @@ let BASE_SHA: string; return; } //todo move to inputs - const getLastSkippedCommitAfterBase = true; - const messagesToSkip = ["[skip ci]"]; + const messagesToSkip = [ + "[skip ci]", + "[ci skip]", + "[no ci]", + "[skip actions]", + "[actions skip]", + ]; if (getLastSkippedCommitAfterBase && BASE_SHA) { - BASE_SHA = await findLastSkippedCommitAfterSha( - stripNewLineEndings(BASE_SHA), - stripNewLineEndings(HEAD_SHA), - messagesToSkip, - mainBranchName, - ); + try { + BASE_SHA = await findLastSkippedCommitAfterSha( + stripNewLineEndings(BASE_SHA), + stripNewLineEndings(HEAD_SHA), + messagesToSkip, + mainBranchName, + ); + } catch (e) { + core.setFailed(e.message); + return; + } } if (!BASE_SHA) { @@ -346,6 +357,9 @@ async function findLastSkippedCommitAfterSha( return newBaseSha; } +/** + * Finds all commits between two provided commits + */ async function findAllCommitsBetweenShas( octokit: Octokit, branchName: string, @@ -386,7 +400,10 @@ async function findAllCommitsBetweenShas( /** * Gets the specified commit by its SHA */ -async function getCommit(octokit: Octokit, commitSha: string) { +async function getCommit( + octokit: Octokit, + commitSha: string, +): Promise { process.stdout.write(`Getting commit for sha: ${commitSha}\n`); const fullCommit = ( await octokit.request("GET /repos/{owner}/{repo}/commits/{commit_sha}", { @@ -398,159 +415,20 @@ async function getCommit(octokit: Octokit, commitSha: string) { process.stdout.write(`SHA get succeeded: ${commitSha}\n`); return getSimplifiedCommit(fullCommit); } + /** * strips out properties from the GitHub commit object to a simplified version for working with */ -function getSimplifiedCommit(commit: Commit): SimplifiedCommit { +function getSimplifiedCommit(commit: any): SimplifiedCommit { return { sha: commit.sha, message: commit.commit.message, date: commit.commit.committer.date, }; } + interface SimplifiedCommit { sha: string; message: string; date: string; } - -export interface Commit { - url: string; - sha: string; - node_id: string; - html_url: string; - comments_url: string; - commit: { - url: string; - author: null | GitUser; - committer: null | GitUser1; - message: string; - comment_count: number; - tree: { - sha: string; - url: string; - [k: string]: unknown; - }; - verification?: Verification; - [k: string]: unknown; - }; - author: null | SimpleUser; - committer: null | SimpleUser1; - parents: { - sha: string; - url: string; - html_url?: string; - [k: string]: unknown; - }[]; - stats?: { - additions?: number; - deletions?: number; - total?: number; - [k: string]: unknown; - }; - files?: DiffEntry[]; - [k: string]: unknown; -} -/** - * Metaproperties for Git author/committer information. - */ -export interface GitUser { - name?: string; - email?: string; - date?: string; - [k: string]: unknown; -} -/** - * Metaproperties for Git author/committer information. - */ -export interface GitUser1 { - name?: string; - email?: string; - date?: string; - [k: string]: unknown; -} -export interface Verification { - verified: boolean; - reason: string; - payload: string | null; - signature: string | null; - [k: string]: unknown; -} -/** - * A GitHub user. - */ -export interface SimpleUser { - name?: string | null; - email?: string | null; - login: string; - id: number; - node_id: string; - avatar_url: string; - gravatar_id: string | null; - url: string; - html_url: string; - followers_url: string; - following_url: string; - gists_url: string; - starred_url: string; - subscriptions_url: string; - organizations_url: string; - repos_url: string; - events_url: string; - received_events_url: string; - type: string; - site_admin: boolean; - starred_at?: string; - [k: string]: unknown; -} -/** - * A GitHub user. - */ -export interface SimpleUser1 { - name?: string | null; - email?: string | null; - login: string; - id: number; - node_id: string; - avatar_url: string; - gravatar_id: string | null; - url: string; - html_url: string; - followers_url: string; - following_url: string; - gists_url: string; - starred_url: string; - subscriptions_url: string; - organizations_url: string; - repos_url: string; - events_url: string; - received_events_url: string; - type: string; - site_admin: boolean; - starred_at?: string; - [k: string]: unknown; -} -/** - * Diff Entry - */ -export interface DiffEntry { - sha: string; - filename: string; - status: - | "added" - | "removed" - | "modified" - | "renamed" - | "copied" - | "changed" - | "unchanged"; - additions: number; - deletions: number; - changes: number; - blob_url: string; - raw_url: string; - contents_url: string; - patch?: string; - previous_filename?: string; - [k: string]: unknown; -} From 2ff251eac805b9999e28a99216c3c550eeb5a62b Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Wed, 27 Dec 2023 07:46:06 +0200 Subject: [PATCH 07/14] chore(documentation): Updating readme and action --- README.md | 4 ++++ action.yml | 2 +- dist/index.js | 15 +++++++-------- find-successful-workflow.ts | 15 +++++++-------- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 8ef4964..789d2d9 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,10 @@ jobs: # E.g. 'ci.yml'. If not provided, current workflow id will be used # workflow-id: "" + + # Specifies if the NX_BASE SHA should be set to the last [skip-ci] commit that occurred after the original NX_BASE. Includes [ci skip], [no ci], [skip actions], [actions skip]. Useful if your GitHub action commits tags, version numbers, etc to the repository which should not be included + # Default: false + get-last-skip-ci-commit: false ``` diff --git a/action.yml b/action.yml index e9391b9..a3206ef 100644 --- a/action.yml +++ b/action.yml @@ -25,7 +25,7 @@ inputs: description: "The ID of the workflow to track or name of the file name. E.g. ci.yml. Defaults to current workflow" get-last-skip-ci-commit: default: false - description: "Specifies if the NX_BASE SHA should be set to the last [skip-ci] commit that occurred after the original NX_BASE. Useful if your GitHub action commits tags to the repository which should not be included" + description: "Specifies if the NX_BASE SHA should be set to the last [skip-ci] commit that occurred after the original NX_BASE. Useful if your GitHub action commits tags, version numbers, etc to the repository which should not be included" outputs: base: diff --git a/dist/index.js b/dist/index.js index eb838a9..d94d1c0 100644 --- a/dist/index.js +++ b/dist/index.js @@ -37874,6 +37874,13 @@ const getLastSkippedCommitAfterBase = process.argv[8]; >>>>>>> e270a80 (chore(skip-ci): creating input, cleanup) const defaultWorkingDirectory = "."; const ProxifiedClient = action_1.Octokit.plugin(proxyPlugin); +const messagesToSkip = [ + "[skip ci]", + "[ci skip]", + "[no ci]", + "[skip actions]", + "[actions skip]", +]; let BASE_SHA; (() => __awaiter(void 0, void 0, void 0, function* () { if (workingDirectory !== defaultWorkingDirectory) { @@ -37910,14 +37917,6 @@ let BASE_SHA; core.setFailed(e.message); return; } - //todo move to inputs - const messagesToSkip = [ - "[skip ci]", - "[ci skip]", - "[no ci]", - "[skip actions]", - "[actions skip]", - ]; if (getLastSkippedCommitAfterBase && BASE_SHA) { try { BASE_SHA = yield findLastSkippedCommitAfterSha(stripNewLineEndings(BASE_SHA), stripNewLineEndings(HEAD_SHA), messagesToSkip, mainBranchName); diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index f70042b..0dc1966 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -22,6 +22,13 @@ const getLastSkippedCommitAfterBase = process.argv[9]; const defaultWorkingDirectory = "."; const ProxifiedClient = Octokit.plugin(proxyPlugin); +const messagesToSkip = [ + "[skip ci]", + "[ci skip]", + "[no ci]", + "[skip actions]", + "[actions skip]", +]; let BASE_SHA: string; (async () => { @@ -72,14 +79,6 @@ let BASE_SHA: string; core.setFailed(e.message); return; } - //todo move to inputs - const messagesToSkip = [ - "[skip ci]", - "[ci skip]", - "[no ci]", - "[skip actions]", - "[actions skip]", - ]; if (getLastSkippedCommitAfterBase && BASE_SHA) { try { BASE_SHA = await findLastSkippedCommitAfterSha( From f61ddfa11cae16ea5147ddc715343236d23951de Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Wed, 27 Dec 2023 07:48:25 +0200 Subject: [PATCH 08/14] chore(skip-ci): removing nvmrc file --- .nvmrc | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index eb800ed..0000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v18.19.0 From ea8240aae92540d2c6a610fec9e2cb63b6e0b973 Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Wed, 27 Dec 2023 07:50:50 +0200 Subject: [PATCH 09/14] fix(skip-ci): removing extra stdout writes --- dist/index.js | 5 ----- find-successful-workflow.ts | 9 --------- 2 files changed, 14 deletions(-) diff --git a/dist/index.js b/dist/index.js index d94d1c0..d96e484 100644 --- a/dist/index.js +++ b/dist/index.js @@ -38107,12 +38107,10 @@ function findLastSkippedCommitAfterSha(baseSha, headSha, messagesToSkip = [], br const baseCommit = yield getCommit(octokit, baseSha); const headCommit = yield getCommit(octokit, headSha); const commits = (yield findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit)).filter((c) => c.sha !== baseSha); - process.stdout.write(`Got ${commits.length} total commits to check:\n`); const sortedCommits = commits.sort((a, b) => a.date.localeCompare(b.date)); let newBaseSha = baseSha; for (const commit of sortedCommits) { const containsAnySkipMessages = messagesToSkip.some((m) => commit.message.indexOf(m) >= 0); - process.stdout.write(`[${commit.sha}][${containsAnySkipMessages}]: ${commit.message}\n`); if (containsAnySkipMessages) { newBaseSha = commit.sha; continue; @@ -38127,7 +38125,6 @@ function findLastSkippedCommitAfterSha(baseSha, headSha, messagesToSkip = [], br */ function findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit, page = 1) { return __awaiter(this, void 0, void 0, function* () { - process.stdout.write(`Finding all commits on branch "${branchName}" between ${baseCommit.sha}|${baseCommit.date} and ${headCommit.sha}|${headCommit.date}, page: ${page}\n`); let commits = (yield octokit.request("GET /repos/{owner}/{repo}/commits", { owner, repo, @@ -38150,13 +38147,11 @@ function findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit, */ function getCommit(octokit, commitSha) { return __awaiter(this, void 0, void 0, function* () { - process.stdout.write(`Getting commit for sha: ${commitSha}\n`); const fullCommit = (yield octokit.request("GET /repos/{owner}/{repo}/commits/{commit_sha}", { owner, repo, commit_sha: commitSha, })).data; - process.stdout.write(`SHA get succeeded: ${commitSha}\n`); return getSimplifiedCommit(fullCommit); }); } diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index 0dc1966..0a28051 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -336,7 +336,6 @@ async function findLastSkippedCommitAfterSha( const commits = ( await findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit) ).filter((c) => c.sha !== baseSha); - process.stdout.write(`Got ${commits.length} total commits to check:\n`); const sortedCommits = commits.sort((a, b) => a.date.localeCompare(b.date)); let newBaseSha = baseSha; @@ -344,9 +343,6 @@ async function findLastSkippedCommitAfterSha( const containsAnySkipMessages = messagesToSkip.some( (m) => commit.message.indexOf(m) >= 0, ); - process.stdout.write( - `[${commit.sha}][${containsAnySkipMessages}]: ${commit.message}\n`, - ); if (containsAnySkipMessages) { newBaseSha = commit.sha; continue; @@ -366,9 +362,6 @@ async function findAllCommitsBetweenShas( headCommit: SimplifiedCommit, page = 1, ): Promise { - process.stdout.write( - `Finding all commits on branch "${branchName}" between ${baseCommit.sha}|${baseCommit.date} and ${headCommit.sha}|${headCommit.date}, page: ${page}\n`, - ); let commits = ( await octokit.request("GET /repos/{owner}/{repo}/commits", { owner, @@ -403,7 +396,6 @@ async function getCommit( octokit: Octokit, commitSha: string, ): Promise { - process.stdout.write(`Getting commit for sha: ${commitSha}\n`); const fullCommit = ( await octokit.request("GET /repos/{owner}/{repo}/commits/{commit_sha}", { owner, @@ -411,7 +403,6 @@ async function getCommit( commit_sha: commitSha, }) ).data; - process.stdout.write(`SHA get succeeded: ${commitSha}\n`); return getSimplifiedCommit(fullCommit); } From 6fa79719223360c8c0659b6ee15eb5cfe4372b96 Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Wed, 27 Dec 2023 08:16:35 +0200 Subject: [PATCH 10/14] fix(skip-ci): updating command and bool parsing --- action.yml | 3 ++- dist/index.js | 4 ++++ find-successful-workflow.ts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/action.yml b/action.yml index a3206ef..5f9be51 100644 --- a/action.yml +++ b/action.yml @@ -51,7 +51,8 @@ runs: last_successful_event: ${{ inputs.last-successful-event }} working_directory: ${{ inputs.working-directory }} working_id: ${{ inputs.workflow-id }} - run: node "$GITHUB_ACTION_PATH/dist/index.js" "$gh_token" "$main_branch_name" "$error_on_no_successful_workflow" "$last_successful_event" "$working_directory" "$working_id" + get_last_skip_ci_commit: ${{inputs.get-last-skip-ci-commit}} + run: node "$GITHUB_ACTION_PATH/dist/index.js" "$gh_token" "$main_branch_name" "$error_on_no_successful_workflow" "$last_successful_event" "$working_directory" "$working_id" "$get_last_skip_ci_commit" - name: Log base and head SHAs used for nx affected shell: bash diff --git a/dist/index.js b/dist/index.js index d96e484..49b49e2 100644 --- a/dist/index.js +++ b/dist/index.js @@ -37868,10 +37868,14 @@ const lastSuccessfulEvent = process.argv[5]; const workingDirectory = process.argv[6]; const workflowId = process.argv[7]; <<<<<<< HEAD +<<<<<<< HEAD const fallbackSHA = process.argv[8]; ======= const getLastSkippedCommitAfterBase = process.argv[8]; >>>>>>> e270a80 (chore(skip-ci): creating input, cleanup) +======= +const getLastSkippedCommitAfterBase = process.argv[8] === "true" ? true : false; +>>>>>>> 2ac45cf (fix(skip-ci): updating command and bool parsing) const defaultWorkingDirectory = "."; const ProxifiedClient = action_1.Octokit.plugin(proxyPlugin); const messagesToSkip = [ diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index 0a28051..d4bf9c3 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -18,7 +18,7 @@ const lastSuccessfulEvent = process.argv[5]; const workingDirectory = process.argv[6]; const workflowId = process.argv[7]; const fallbackSHA = process.argv[8]; -const getLastSkippedCommitAfterBase = process.argv[9]; +const getLastSkippedCommitAfterBase = process.argv[9] === "true" ? true : false; const defaultWorkingDirectory = "."; const ProxifiedClient = Octokit.plugin(proxyPlugin); From d81d63e76a530d4e1ad13c3d6d7ea65697f772b9 Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Wed, 27 Dec 2023 09:44:11 +0200 Subject: [PATCH 11/14] fix(skip-ci): updating code to use the next SHA, not the one with skip-ci --- dist/index.js | 11 +++++++---- find-successful-workflow.ts | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/dist/index.js b/dist/index.js index 49b49e2..e9b28a3 100644 --- a/dist/index.js +++ b/dist/index.js @@ -38112,16 +38112,19 @@ function findLastSkippedCommitAfterSha(baseSha, headSha, messagesToSkip = [], br const headCommit = yield getCommit(octokit, headSha); const commits = (yield findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit)).filter((c) => c.sha !== baseSha); const sortedCommits = commits.sort((a, b) => a.date.localeCompare(b.date)); - let newBaseSha = baseSha; + let useNext = false; for (const commit of sortedCommits) { + if (useNext) { + return commit.sha; + } const containsAnySkipMessages = messagesToSkip.some((m) => commit.message.indexOf(m) >= 0); if (containsAnySkipMessages) { - newBaseSha = commit.sha; + useNext = true; continue; } - return newBaseSha; + return commit.sha; } - return newBaseSha; + return baseSha; }); } /** diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index d4bf9c3..e54bcdf 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -338,18 +338,21 @@ async function findLastSkippedCommitAfterSha( ).filter((c) => c.sha !== baseSha); const sortedCommits = commits.sort((a, b) => a.date.localeCompare(b.date)); - let newBaseSha = baseSha; + let useNext = false; for (const commit of sortedCommits) { + if (useNext) { + return commit.sha; + } const containsAnySkipMessages = messagesToSkip.some( (m) => commit.message.indexOf(m) >= 0, ); if (containsAnySkipMessages) { - newBaseSha = commit.sha; + useNext = true; continue; } - return newBaseSha; + return commit.sha; } - return newBaseSha; + return baseSha; } /** From a2fc346c5461480061f62deecacd3df2e93e7193 Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Wed, 27 Dec 2023 09:52:04 +0200 Subject: [PATCH 12/14] Revert "fix(skip-ci): updating code to use the next SHA, not the one with skip-ci" This reverts commit 7361db9273852e300ef52f33499198b0487e5ffe. --- dist/index.js | 11 ++++------- find-successful-workflow.ts | 11 ++++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/dist/index.js b/dist/index.js index e9b28a3..49b49e2 100644 --- a/dist/index.js +++ b/dist/index.js @@ -38112,19 +38112,16 @@ function findLastSkippedCommitAfterSha(baseSha, headSha, messagesToSkip = [], br const headCommit = yield getCommit(octokit, headSha); const commits = (yield findAllCommitsBetweenShas(octokit, branchName, baseCommit, headCommit)).filter((c) => c.sha !== baseSha); const sortedCommits = commits.sort((a, b) => a.date.localeCompare(b.date)); - let useNext = false; + let newBaseSha = baseSha; for (const commit of sortedCommits) { - if (useNext) { - return commit.sha; - } const containsAnySkipMessages = messagesToSkip.some((m) => commit.message.indexOf(m) >= 0); if (containsAnySkipMessages) { - useNext = true; + newBaseSha = commit.sha; continue; } - return commit.sha; + return newBaseSha; } - return baseSha; + return newBaseSha; }); } /** diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index e54bcdf..d4bf9c3 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -338,21 +338,18 @@ async function findLastSkippedCommitAfterSha( ).filter((c) => c.sha !== baseSha); const sortedCommits = commits.sort((a, b) => a.date.localeCompare(b.date)); - let useNext = false; + let newBaseSha = baseSha; for (const commit of sortedCommits) { - if (useNext) { - return commit.sha; - } const containsAnySkipMessages = messagesToSkip.some( (m) => commit.message.indexOf(m) >= 0, ); if (containsAnySkipMessages) { - useNext = true; + newBaseSha = commit.sha; continue; } - return commit.sha; + return newBaseSha; } - return baseSha; + return newBaseSha; } /** From 58575e7223cf8067d72f811bb2d3daf8788ae127 Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Wed, 27 Dec 2023 09:53:33 +0200 Subject: [PATCH 13/14] fix(skip-ci): returning next SHA instead of the last skip-ci one --- dist/index.js | 2 +- find-successful-workflow.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/index.js b/dist/index.js index 49b49e2..607ab12 100644 --- a/dist/index.js +++ b/dist/index.js @@ -38119,7 +38119,7 @@ function findLastSkippedCommitAfterSha(baseSha, headSha, messagesToSkip = [], br newBaseSha = commit.sha; continue; } - return newBaseSha; + return commit.sha; } return newBaseSha; }); diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index d4bf9c3..b00de99 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -347,7 +347,7 @@ async function findLastSkippedCommitAfterSha( newBaseSha = commit.sha; continue; } - return newBaseSha; + return commit.sha; } return newBaseSha; } From 342ca1bd47f7243c592aae7cf57a0d89b2061574 Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Tue, 28 May 2024 17:47:20 +0300 Subject: [PATCH 14/14] fix(skip-ci): catering for the case where base === head --- dist/index.js | 16 ++++++---------- find-successful-workflow.ts | 4 ++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/dist/index.js b/dist/index.js index 607ab12..77c4a0e 100644 --- a/dist/index.js +++ b/dist/index.js @@ -37867,15 +37867,8 @@ const errorOnNoSuccessfulWorkflow = process.argv[4]; const lastSuccessfulEvent = process.argv[5]; const workingDirectory = process.argv[6]; const workflowId = process.argv[7]; -<<<<<<< HEAD -<<<<<<< HEAD const fallbackSHA = process.argv[8]; -======= -const getLastSkippedCommitAfterBase = process.argv[8]; ->>>>>>> e270a80 (chore(skip-ci): creating input, cleanup) -======= -const getLastSkippedCommitAfterBase = process.argv[8] === "true" ? true : false; ->>>>>>> 2ac45cf (fix(skip-ci): updating command and bool parsing) +const getLastSkippedCommitAfterBase = process.argv[9] === "true" ? true : false; const defaultWorkingDirectory = "."; const ProxifiedClient = action_1.Octokit.plugin(proxyPlugin); const messagesToSkip = [ @@ -37938,6 +37931,9 @@ let BASE_SHA; else { process.stdout.write("\n"); process.stdout.write(`WARNING: Unable to find a successful workflow run on 'origin/${mainBranchName}', or the latest successful workflow was connected to a commit which no longer exists on that branch (e.g. if that branch was rebased)\n`); + process.stdout.write(`We are therefore defaulting to use HEAD~1 on 'origin/${mainBranchName}'\n`); + process.stdout.write("\n"); + process.stdout.write(`NOTE: You can instead make this a hard error by setting 'error-on-no-successful-workflow' on the action in your workflow.\n`); if (fallbackSHA) { BASE_SHA = fallbackSHA; process.stdout.write(`Using provided fallback SHA: ${fallbackSHA}\n`); @@ -38119,9 +38115,9 @@ function findLastSkippedCommitAfterSha(baseSha, headSha, messagesToSkip = [], br newBaseSha = commit.sha; continue; } - return commit.sha; + return commit.sha === headSha ? baseSha : commit.sha; } - return newBaseSha; + return newBaseSha === headSha ? baseSha : newBaseSha; }); } /** diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index b00de99..2fe47e9 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -347,9 +347,9 @@ async function findLastSkippedCommitAfterSha( newBaseSha = commit.sha; continue; } - return commit.sha; + return commit.sha === headSha ? baseSha : commit.sha; } - return newBaseSha; + return newBaseSha === headSha ? baseSha : newBaseSha; } /**