-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3211328
commit 3b7601d
Showing
12 changed files
with
502 additions
and
176 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import * as GitHub from '@octokit/rest' | ||
import * as Actions from '@actions/core' | ||
import * as Commander from 'commander' | ||
import checkDiskSpace from 'check-disk-space' | ||
|
||
const Program = new Commander.Command() | ||
|
||
Program.option('--debug', 'output extra debugging', false) | ||
.option('--gh-token <TOKEN>', 'GitHub token', '') | ||
.option('--repo <REPO>', 'A GitHub repository. eg: owner/repo', '') | ||
.option('--ci-workspace-path <PATH>', 'A path to the CI workspace.', '') | ||
|
||
Program.parse() | ||
|
||
type ProgramOptionsType = { | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
debug: boolean; | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
ghToken: string; | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
repo: string; | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
ciWorkspacePath: string; | ||
} | ||
const ProgramOptions: ProgramOptionsType = Program.opts() | ||
|
||
const GitHubInstance = new GitHub.Octokit({auth: ProgramOptions.ghToken}) | ||
const RepoSize = GitHubInstance.repos.get({owner: ProgramOptions.repo.split('/')[0], repo: ProgramOptions.repo.split('/')[1]}) | ||
.then(Response => Response.data.size) | ||
const DiskFreeSize = checkDiskSpace(ProgramOptions.ciWorkspacePath).then(DiskInfo => DiskInfo.free) | ||
|
||
await Promise.all([RepoSize, DiskFreeSize]).then(([RepoSizeVaule, DiskFreeSizeVaule]) => { | ||
Actions.info(`calc-repo-size: RepoSize: ${RepoSizeVaule}; DiskFreeSize: ${DiskFreeSizeVaule}`) | ||
if (RepoSizeVaule * 1000 < DiskFreeSizeVaule) { | ||
Actions.setOutput('should_use_api', 'false') | ||
} else { | ||
Actions.setOutput('should_use_api', 'true') | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import * as Commander from 'commander' | ||
import type * as Types from './sources/types' | ||
import {ExportArgs, IsDebug} from './sources/debug' | ||
import {ReplaceStringWithBooleanInObject} from './sources/utility' | ||
import {GetLatestWorkflowTime} from './sources/actions' | ||
import {ListBranches} from './sources/branches' | ||
import {GetChangedFilesFromSHAToHead, GetCommitSHAFromLatestWorkflowTime} from './sources/commits' | ||
import {PurgeRequestManager} from './sources/requests' | ||
|
||
const Program = new Commander.Command() | ||
|
||
// Set options. | ||
Program.option('--debug', 'output extra debugging', false) | ||
.option('--gh-token <TOKEN>', 'GitHub token', '') | ||
.option('--repo <REPO>', 'A GitHub repository. eg: owner/repo', '') | ||
.option('--workflow-ref <WORKFLOW_REF>', 'A GitHub workflow ref. eg: refs/heads/master', '') | ||
.option('--branch <BRANCH>', 'A GitHub branch. eg: master', '') | ||
.option('--ci-workspace-path <PATH>', 'A path to the CI workspace.', '') | ||
.option('--ci-action-path <PATH>', 'A path to the CI action.', '') | ||
.option('--should-use-api <TRUE_OR_FALSE>', 'Should use GitHub API?', 'false') | ||
|
||
// Initialize Input of the options and export them. | ||
Program.parse() | ||
|
||
// Declare the options and print them if the debugging mode is enabled. | ||
const ProgramRawOptions: Types.ProgramOptionsRawType = Program.opts() | ||
if (IsDebug(ProgramRawOptions)) { | ||
ExportArgs(ProgramRawOptions) | ||
} | ||
|
||
// Redefine with boolean. | ||
const ProgramOptions = ReplaceStringWithBooleanInObject(ProgramRawOptions) as Types.ProgramOptionsType | ||
|
||
// Workflow | ||
const LatestWorkflowRunTime = await GetLatestWorkflowTime(ProgramOptions).then(LatestWorkflowRunTime => LatestWorkflowRunTime) | ||
const Branches = await ListBranches(ProgramOptions).then(Branches => Branches) | ||
const PurgeRequest = new PurgeRequestManager(ProgramOptions) | ||
for (const Branch of Branches) { | ||
// eslint-disable-next-line no-await-in-loop | ||
const CommitSHA = await GetCommitSHAFromLatestWorkflowTime(ProgramOptions, LatestWorkflowRunTime, Branch).then(CommitSHA => CommitSHA) | ||
// eslint-disable-next-line no-await-in-loop | ||
const ChangedFiles = await GetChangedFilesFromSHAToHead(ProgramOptions, CommitSHA, Branch).then(ChangedFiles => ChangedFiles) | ||
PurgeRequest.AddURLs(ChangedFiles, Branch) | ||
} | ||
|
||
PurgeRequest.Start() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import * as GitHub from '@octokit/rest' | ||
import {DateTime} from 'luxon' | ||
import type {ProgramOptionsType} from './types' | ||
|
||
/** | ||
* @name GetLatestWorkflowTime | ||
* @description Get the latest workflow time. | ||
* @param {ProgramOptionsType} ProgramOptions The program options. | ||
* @returns {Promise<number>} The latest workflow time in milliseconds. | ||
*/ | ||
export async function GetLatestWorkflowTime(ProgramOptions: ProgramOptionsType): Promise<number> { | ||
const GitHubInstance = new GitHub.Octokit({auth: ProgramOptions.ghToken}) | ||
const [RepoOwner, RepoName] = ProgramOptions.repo.split('/') | ||
var LatestWorkflowRunTime = Number.MIN_SAFE_INTEGER | ||
const WorkflowRuns = await GitHubInstance.actions.listWorkflowRuns({ | ||
owner: RepoOwner, repo: RepoName, | ||
workflow_id: /(?<=^[A-z0-9]+\/[A-z0-9]+\/\.github\/workflows\/).+\.yml(?=@refs\/)/.exec(ProgramOptions.workflowRef)[0], | ||
Check warning Code scanning / CodeQL Overly permissive regular expression range Medium
Suspicious character range that is equivalent to [A-Z[]^_`a-z].
Check warning Code scanning / CodeQL Overly permissive regular expression range Medium
Suspicious character range that is equivalent to [A-Z[]^_`a-z].
|
||
}).then(WorkflowRuns => WorkflowRuns.data.workflow_runs) | ||
for (const WorkflowRun of WorkflowRuns) { | ||
if (WorkflowRun.status === 'completed' && WorkflowRun.conclusion === 'success' | ||
&& DateTime.fromISO(WorkflowRun.updated_at).toMillis() > LatestWorkflowRunTime) { | ||
LatestWorkflowRunTime = DateTime.fromISO(WorkflowRun.updated_at).toMillis() | ||
} | ||
} | ||
|
||
return LatestWorkflowRunTime | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import * as Git from 'simple-git' | ||
import * as GitHub from '@octokit/rest' | ||
import * as Actions from '@actions/core' | ||
import * as Os from 'node:os' | ||
import type * as Types from './types' | ||
import {IsDebug} from './debug' | ||
|
||
function CreateGitHubInstance(ProgramOptions: Types.ProgramOptionsType): GitHub.Octokit { | ||
const GitHubInstance = new GitHub.Octokit({auth: ProgramOptions.ghToken}) | ||
return GitHubInstance | ||
} | ||
|
||
function CreateGitInstance(BasePath: string): Git.SimpleGit { | ||
const GitInstance = Git.simpleGit(BasePath, {maxConcurrentProcesses: Os.cpus().length}) | ||
return GitInstance | ||
} | ||
|
||
/** | ||
* @name ListBranches | ||
* @description List all branches that should be purged. | ||
* @param {Types.ProgramOptions} ProgramOptions The program options. | ||
* @returns {string[]} A list of branches. The list always contains 'latest' and the current/default branch. | ||
*/ | ||
export async function ListBranches(ProgramOptions: Types.ProgramOptionsType): Promise<string[]> { | ||
const Branches: string[] = ['latest'] | ||
if (ProgramOptions.shouldUseApi) { | ||
const GitHubInstance = CreateGitHubInstance(ProgramOptions) | ||
const [RepoOwner, RepoName] = ProgramOptions.repo.split('/') | ||
Branches.push((await GitHubInstance.repos.get({owner: RepoOwner, repo: RepoName})).data.default_branch) | ||
const OtherBranches = (await GitHubInstance.repos.listBranches({owner: RepoOwner, repo: RepoName}).then(Branches => Branches.data)) | ||
.map(Item => Item.name) | ||
OtherBranches.forEach(Item => Branches.push(ProgramOptions.branch.split(' ').find(Branch => Branch === Item))) | ||
} | ||
|
||
if (!ProgramOptions.shouldUseApi) { | ||
const GitInstance = CreateGitInstance(`${ProgramOptions.ciWorkspacePath}/${ProgramOptions.repo.split('/')[1]}`) | ||
Branches.push(await GitInstance.branchLocal().then(Branches => Branches.current)) | ||
// Branches[1] is always the current/default branch. | ||
const OtherBranches = (await GitInstance.branchLocal().then(Branches => Branches.all)).filter(Branch => Branch !== Branches[1]) | ||
OtherBranches.forEach(Item => Branches.push(ProgramOptions.branch.split(' ').find(Branch => Branch === Item))) | ||
} | ||
|
||
if (IsDebug(ProgramOptions)) { | ||
Actions.debug(`ListBranches in branches.ts called: ${JSON.stringify(Branches)}`) | ||
} | ||
|
||
return Branches | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import * as Git from 'simple-git' | ||
import * as GitHub from '@octokit/rest' | ||
import * as Os from 'node:os' | ||
import {DateTime} from 'luxon' | ||
import type * as Types from './types' | ||
|
||
function CreateGitHubInstance(ProgramOptions: Types.ProgramOptionsType): GitHub.Octokit { | ||
const GitHubInstance = new GitHub.Octokit({auth: ProgramOptions.ghToken}) | ||
return GitHubInstance | ||
} | ||
|
||
function CreateGitInstance(BasePath: string): Git.SimpleGit { | ||
const GitInstance = Git.simpleGit(BasePath, {maxConcurrentProcesses: Os.cpus().length}) | ||
return GitInstance | ||
} | ||
|
||
/** | ||
* @name ListCommitsFromLatestWorkflowTime | ||
* @description List commits using GitHub Octokit or simple-git. | ||
* @param {Types.ProgramOptionsType} ProgramOptions The program options. | ||
* @param {number} LatestWorkflowRunTime The latest workflow time in milliseconds. | ||
* @param {string} Branch The branch or tag name. | ||
* @returns {Promise<string>} SHA of the latest commit. | ||
*/ | ||
export async function GetCommitSHAFromLatestWorkflowTime(ProgramOptions: Types.ProgramOptionsType, LatestWorkflowRunTime: number, Branch: string): Promise<string> { | ||
var MatchedCommitTimeAddress = 0 | ||
if (ProgramOptions.shouldUseApi) { | ||
const GitHubInstance = CreateGitHubInstance(ProgramOptions) | ||
const GitHubListCommitsRaw = await GitHubInstance.repos.listCommits({ | ||
owner: ProgramOptions.repo.split('/')[0], | ||
repo: ProgramOptions.repo.split('/')[1], | ||
sha: Branch, | ||
since: DateTime.fromMillis(LatestWorkflowRunTime).toISO(), | ||
}).then(Response => Response.data) | ||
for (const CommitRaw of GitHubListCommitsRaw) { | ||
if (DateTime.fromISO(CommitRaw.commit.author.date).toMillis() < LatestWorkflowRunTime) { | ||
break | ||
} | ||
|
||
MatchedCommitTimeAddress++ | ||
} | ||
|
||
return GitHubListCommitsRaw[MatchedCommitTimeAddress].sha | ||
} | ||
|
||
if (!ProgramOptions.shouldUseApi) { | ||
const GitInstance = CreateGitInstance(`${ProgramOptions.ciWorkspacePath}/${ProgramOptions.repo.split('/')[1]}`) | ||
const GitLogRaw = (await GitInstance.log(['--date=iso-strict', `--since=${DateTime.fromMillis(LatestWorkflowRunTime).toISO()}`])).all | ||
for (const CommitRaw of GitLogRaw) { | ||
if (DateTime.fromISO(CommitRaw.date).toMillis() < LatestWorkflowRunTime) { | ||
break | ||
} | ||
|
||
MatchedCommitTimeAddress++ | ||
} | ||
|
||
return GitLogRaw[MatchedCommitTimeAddress].hash | ||
} | ||
} | ||
|
||
/** | ||
* @name GetChangedFilesFromSHAToBranchLatestCommits | ||
* @description Get changed files from a commit to the latest commit in a branch. | ||
* @param {Types.ProgramOptionsType} ProgramOptions The program options. | ||
* @param {stirng} CommitSHA The commit SHA. | ||
* @param {string} Branch The branch name. | ||
* @returns {Promise<string[]>} A list of changed files. | ||
*/ | ||
export async function GetChangedFilesFromSHAToHead(ProgramOptions: Types.ProgramOptionsType, CommitSHA: string, Branch: string): Promise<string[]> { | ||
if (ProgramOptions.shouldUseApi) { | ||
const GitHubInstance = CreateGitHubInstance(ProgramOptions) | ||
const GitHubComparingRaw = await GitHubInstance.repos.compareCommits({ | ||
owner: ProgramOptions.repo.split('/')[0], | ||
repo: ProgramOptions.repo.split('/')[1], | ||
head: Branch, | ||
base: CommitSHA, | ||
}).then(Response => Response.data) | ||
return GitHubComparingRaw.files.map(File => File.filename) | ||
} | ||
|
||
if (!ProgramOptions.shouldUseApi) { | ||
const GitInstance = CreateGitInstance(`${ProgramOptions.ciWorkspacePath}/${ProgramOptions.repo.split('/')[1]}`) | ||
const ChangedFiles = (await GitInstance.diff(['--name-only', `${CommitSHA}...${Branch}`])).split('\n') | ||
return ChangedFiles | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import * as Actions from '@actions/core' | ||
import type * as Types from './types' | ||
|
||
export function IsDebug(Args: Types.ProgramOptionsType | Types.ProgramOptionsRawType) { | ||
const ArgsDebug = typeof Args.debug === 'string' ? Args.debug === 'true' : Args.debug | ||
return Actions.isDebug() || ArgsDebug | ||
} | ||
|
||
export function ExportArgs(Args: Types.ProgramOptionsType | Types.ProgramOptionsRawType) { | ||
Actions.debug(`ProgramOptions: ${JSON.stringify(Args).replace(/(?<=,"ghToken":")[^"]+/, '***')}`) | ||
} |
Oops, something went wrong.