diff --git a/README.md b/README.md index 51a609f98..8b641cbfa 100644 --- a/README.md +++ b/README.md @@ -764,6 +764,24 @@ jobs: exempt-all-pr-assignees: true ``` +Close stale issues and PRs in different repository: + +```yaml +name: 'Close stale issues and PRs' +on: + schedule: + - cron: '30 1 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + repo-token: # A PAT that has a minimum of triage permissions on my-org/my-repo + repository: my-org/my-repo +``` + ### Debugging **Logs:** diff --git a/__tests__/constants/default-processor-options.ts b/__tests__/constants/default-processor-options.ts index 0265b6446..77c56e1b2 100644 --- a/__tests__/constants/default-processor-options.ts +++ b/__tests__/constants/default-processor-options.ts @@ -55,5 +55,7 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({ ignorePrUpdates: undefined, exemptDraftPr: false, closeIssueReason: 'not_planned', - includeOnlyAssigned: false + includeOnlyAssigned: false, + owner: 'dummy-owner', + repo: 'dummy-repo' }); diff --git a/action.yml b/action.yml index d55f8547c..9f9ff81b9 100644 --- a/action.yml +++ b/action.yml @@ -204,6 +204,9 @@ inputs: description: 'Only the issues or the pull requests with an assignee will be marked as stale automatically.' default: 'false' required: false + repository: + description: 'Repository to process PRs from. Defaults to the current repository' + required: false outputs: closed-issues-prs: description: 'List of all closed issues and pull requests.' diff --git a/dist/index.js b/dist/index.js index 50ec5fed2..3ab31263f 100644 --- a/dist/index.js +++ b/dist/index.js @@ -659,8 +659,8 @@ class IssuesProcessor { this._consumeIssueOperation(issue); (_a = this.statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedItemsCommentsCount(); const comments = yield this.client.rest.issues.listComments({ - owner: github_1.context.repo.owner, - repo: github_1.context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, since: sinceDate }); @@ -679,8 +679,8 @@ class IssuesProcessor { try { this.operations.consumeOperation(); const issueResult = yield this.client.rest.issues.listForRepo({ - owner: github_1.context.repo.owner, - repo: github_1.context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, state: 'open', per_page: 100, direction: this.options.ascending ? 'asc' : 'desc', @@ -704,8 +704,8 @@ class IssuesProcessor { this._consumeIssueOperation(issue); (_a = this.statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedItemsEventsCount(); const options = this.client.rest.issues.listEvents.endpoint.merge({ - owner: github_1.context.repo.owner, - repo: github_1.context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, per_page: 100, issue_number: issue.number }); @@ -728,8 +728,8 @@ class IssuesProcessor { this._consumeIssueOperation(issue); (_a = this.statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedPullRequestsCount(); const pullRequest = yield this.client.rest.pulls.get({ - owner: github_1.context.repo.owner, - repo: github_1.context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, pull_number: issue.number }); return pullRequest.data; @@ -848,8 +848,8 @@ class IssuesProcessor { (_a = this.statistics) === null || _a === void 0 ? void 0 : _a.incrementAddedItemsComment(issue); if (!this.options.debugOnly) { yield this.client.rest.issues.createComment({ - owner: github_1.context.repo.owner, - repo: github_1.context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, body: staleMessage }); @@ -865,8 +865,8 @@ class IssuesProcessor { (_c = this.statistics) === null || _c === void 0 ? void 0 : _c.incrementStaleItemsCount(issue); if (!this.options.debugOnly) { yield this.client.rest.issues.addLabels({ - owner: github_1.context.repo.owner, - repo: github_1.context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, labels: [staleLabel] }); @@ -891,8 +891,8 @@ class IssuesProcessor { this.addedCloseCommentIssues.push(issue); if (!this.options.debugOnly) { yield this.client.rest.issues.createComment({ - owner: github_1.context.repo.owner, - repo: github_1.context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, body: closeMessage }); @@ -908,8 +908,8 @@ class IssuesProcessor { (_b = this.statistics) === null || _b === void 0 ? void 0 : _b.incrementAddedItemsLabel(issue); if (!this.options.debugOnly) { yield this.client.rest.issues.addLabels({ - owner: github_1.context.repo.owner, - repo: github_1.context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, labels: [closeLabel] }); @@ -924,8 +924,8 @@ class IssuesProcessor { (_c = this.statistics) === null || _c === void 0 ? void 0 : _c.incrementClosedItemsCount(issue); if (!this.options.debugOnly) { yield this.client.rest.issues.update({ - owner: github_1.context.repo.owner, - repo: github_1.context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, state: 'closed', state_reason: this.options.closeIssueReason || undefined @@ -955,15 +955,15 @@ class IssuesProcessor { const branch = pullRequest.head.ref; if (pullRequest.head.repo === null || pullRequest.head.repo.full_name === - `${github_1.context.repo.owner}/${github_1.context.repo.repo}`) { + `${this.options.owner}/${this.options.repo}`) { issueLogger.info(`Deleting the branch "${logger_service_1.LoggerService.cyan(branch)}" from closed $$type`); try { this._consumeIssueOperation(issue); (_a = this.statistics) === null || _a === void 0 ? void 0 : _a.incrementDeletedBranchesCount(); if (!this.options.debugOnly) { yield this.client.rest.git.deleteRef({ - owner: github_1.context.repo.owner, - repo: github_1.context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, ref: `heads/${branch}` }); } @@ -989,8 +989,8 @@ class IssuesProcessor { (_a = this.statistics) === null || _a === void 0 ? void 0 : _a.incrementDeletedItemsLabelsCount(issue); if (!this.options.debugOnly) { yield this.client.rest.issues.removeLabel({ - owner: github_1.context.repo.owner, - repo: github_1.context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, name: label }); @@ -1089,8 +1089,8 @@ class IssuesProcessor { (_a = this.statistics) === null || _a === void 0 ? void 0 : _a.incrementAddedItemsLabel(issue); if (!this.options.debugOnly) { yield this.client.rest.issues.addLabels({ - owner: github_1.context.repo.owner, - repo: github_1.context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, labels: labelsToAdd }); @@ -2483,6 +2483,7 @@ const core = __importStar(__nccwpck_require__(2186)); const issues_processor_1 = __nccwpck_require__(3292); const is_valid_date_1 = __nccwpck_require__(891); const state_service_1 = __nccwpck_require__(6330); +const github_1 = __nccwpck_require__(5438); function _run() { return __awaiter(this, void 0, void 0, function* () { try { @@ -2512,7 +2513,10 @@ function _run() { }); } function _getAndValidateArgs() { + const { owner, repo } = getOwnerRepo(); const args = { + owner, + repo, repoToken: core.getInput('repo-token'), staleIssueMessage: core.getInput('stale-issue-message'), stalePrMessage: core.getInput('stale-pr-message'), @@ -2628,6 +2632,18 @@ function _toOptionalBoolean(argumentName) { } return undefined; } +function getOwnerRepo() { + let { owner, repo } = github_1.context.repo; + const repository = core.getInput('repository'); + if (repository) { + const components = repository.split('/'); + if (components.length !== 2) { + throw new Error(`Invalid repository format "${repository}". Expected "owner/repo".`); + } + [owner, repo] = components; + } + return { owner, repo }; +} void _run(); diff --git a/src/classes/issue.spec.ts b/src/classes/issue.spec.ts index a2c82e268..44671a734 100644 --- a/src/classes/issue.spec.ts +++ b/src/classes/issue.spec.ts @@ -64,7 +64,9 @@ describe('Issue', (): void => { ignorePrUpdates: undefined, exemptDraftPr: false, closeIssueReason: '', - includeOnlyAssigned: false + includeOnlyAssigned: false, + owner: 'dummy-owner', + repo: 'dummy-repo' }; issueInterface = { title: 'dummy-title', diff --git a/src/classes/issues-processor.ts b/src/classes/issues-processor.ts index 486c6a78a..fe5704e72 100644 --- a/src/classes/issues-processor.ts +++ b/src/classes/issues-processor.ts @@ -1,5 +1,5 @@ import * as core from '@actions/core'; -import {context, getOctokit} from '@actions/github'; +import {getOctokit} from '@actions/github'; import {GitHub} from '@actions/github/lib/utils'; import {Option} from '../enums/option'; import {getHumanizedDate} from '../functions/dates/get-humanized-date'; @@ -549,8 +549,8 @@ export class IssuesProcessor { this._consumeIssueOperation(issue); this.statistics?.incrementFetchedItemsCommentsCount(); const comments = await this.client.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, since: sinceDate }); @@ -566,8 +566,8 @@ export class IssuesProcessor { try { this.operations.consumeOperation(); const issueResult = await this.client.rest.issues.listForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, state: 'open', per_page: 100, direction: this.options.ascending ? 'asc' : 'desc', @@ -597,8 +597,8 @@ export class IssuesProcessor { this._consumeIssueOperation(issue); this.statistics?.incrementFetchedItemsEventsCount(); const options = this.client.rest.issues.listEvents.endpoint.merge({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, per_page: 100, issue_number: issue.number }); @@ -628,8 +628,8 @@ export class IssuesProcessor { this.statistics?.incrementFetchedPullRequestsCount(); const pullRequest = await this.client.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, pull_number: issue.number }); @@ -848,8 +848,8 @@ export class IssuesProcessor { if (!this.options.debugOnly) { await this.client.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, body: staleMessage }); @@ -866,8 +866,8 @@ export class IssuesProcessor { if (!this.options.debugOnly) { await this.client.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, labels: [staleLabel] }); @@ -896,8 +896,8 @@ export class IssuesProcessor { if (!this.options.debugOnly) { await this.client.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, body: closeMessage }); @@ -914,8 +914,8 @@ export class IssuesProcessor { if (!this.options.debugOnly) { await this.client.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, labels: [closeLabel] }); @@ -931,8 +931,8 @@ export class IssuesProcessor { if (!this.options.debugOnly) { await this.client.rest.issues.update({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, state: 'closed', state_reason: this.options.closeIssueReason || undefined @@ -968,7 +968,7 @@ export class IssuesProcessor { if ( pullRequest.head.repo === null || pullRequest.head.repo.full_name === - `${context.repo.owner}/${context.repo.repo}` + `${this.options.owner}/${this.options.repo}` ) { issueLogger.info( `Deleting the branch "${LoggerService.cyan(branch)}" from closed $$type` @@ -980,8 +980,8 @@ export class IssuesProcessor { if (!this.options.debugOnly) { await this.client.rest.git.deleteRef({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, ref: `heads/${branch}` }); } @@ -1024,8 +1024,8 @@ export class IssuesProcessor { if (!this.options.debugOnly) { await this.client.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, name: label }); @@ -1162,8 +1162,8 @@ export class IssuesProcessor { this.statistics?.incrementAddedItemsLabel(issue); if (!this.options.debugOnly) { await this.client.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: this.options.owner, + repo: this.options.repo, issue_number: issue.number, labels: labelsToAdd }); diff --git a/src/interfaces/issues-processor-options.ts b/src/interfaces/issues-processor-options.ts index 930992284..148d71aca 100644 --- a/src/interfaces/issues-processor-options.ts +++ b/src/interfaces/issues-processor-options.ts @@ -54,4 +54,6 @@ export interface IIssuesProcessorOptions { exemptDraftPr: boolean; closeIssueReason: string; includeOnlyAssigned: boolean; + owner: string; + repo: string; } diff --git a/src/main.ts b/src/main.ts index a7836c160..9f7309371 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,7 @@ import {isValidDate} from './functions/dates/is-valid-date'; import {IIssuesProcessorOptions} from './interfaces/issues-processor-options'; import {Issue} from './classes/issue'; import {getStateInstance} from './services/state.service'; +import {context} from '@actions/github'; async function _run(): Promise { try { @@ -55,7 +56,10 @@ async function _run(): Promise { } function _getAndValidateArgs(): IIssuesProcessorOptions { + const {owner, repo} = getOwnerRepo(); const args: IIssuesProcessorOptions = { + owner, + repo, repoToken: core.getInput('repo-token'), staleIssueMessage: core.getInput('stale-issue-message'), stalePrMessage: core.getInput('stale-pr-message'), @@ -198,4 +202,19 @@ function _toOptionalBoolean( return undefined; } +function getOwnerRepo(): {owner: string; repo: string} { + let {owner, repo} = context.repo; + const repository = core.getInput('repository'); + if (repository) { + const components = repository.split('/'); + if (components.length !== 2) { + throw new Error( + `Invalid repository format "${repository}". Expected "owner/repo".` + ); + } + [owner, repo] = components; + } + return {owner, repo}; +} + void _run();