feat: add support for GHES/Gitea instances with new input parameter and API handling
This commit is contained in:
parent
450b15d522
commit
3727904230
22
README.md
22
README.md
@ -2,7 +2,7 @@
|
|||||||
[](https://github.com/peter-evans/create-pull-request/actions?query=workflow%3ACI)
|
[](https://github.com/peter-evans/create-pull-request/actions?query=workflow%3ACI)
|
||||||
[](https://github.com/marketplace/actions/create-pull-request)
|
[](https://github.com/marketplace/actions/create-pull-request)
|
||||||
|
|
||||||
A GitHub action to create a pull request for changes to your repository in the actions workspace.
|
A GitHub action to create a pull request for changes to your repository in the actions workspace. This action also supports GHES and Gitea instances.
|
||||||
|
|
||||||
Changes to a repository in the Actions workspace persist between steps in a workflow.
|
Changes to a repository in the Actions workspace persist between steps in a workflow.
|
||||||
This action is designed to be used in conjunction with other steps that modify or add files to your repository.
|
This action is designed to be used in conjunction with other steps that modify or add files to your repository.
|
||||||
@ -35,6 +35,25 @@ Create Pull Request action will:
|
|||||||
uses: peter-evans/create-pull-request@v7
|
uses: peter-evans/create-pull-request@v7
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Usage with Gitea or GHES
|
||||||
|
|
||||||
|
If you're using this action with Gitea, you need to specify the Gitea hostname using the `github-server-url` parameter:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
- name: Create Pull Request
|
||||||
|
continue-on-error: true
|
||||||
|
uses: peter-evans/create-pull-request@v7
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
github-server-url: gitea.mediatek.inc
|
||||||
|
base: master
|
||||||
|
branch: chore/pre-commit-hooks
|
||||||
|
title: "chore: Update pre-commit hooks"
|
||||||
|
body: "Update versions of pre-commit hooks to latest version."
|
||||||
|
commit-message: "chore: update pre-commit hooks"
|
||||||
|
delete-branch: true
|
||||||
|
```
|
||||||
|
|
||||||
You can also pin to a [specific release](https://github.com/peter-evans/create-pull-request/releases) version in the format `@v7.x.x`
|
You can also pin to a [specific release](https://github.com/peter-evans/create-pull-request/releases) version in the format `@v7.x.x`
|
||||||
|
|
||||||
### Workflow permissions
|
### Workflow permissions
|
||||||
@ -74,6 +93,7 @@ All inputs are **optional**. If not set, sensible defaults will be used.
|
|||||||
| `milestone` | The number of the milestone to associate this pull request with. | |
|
| `milestone` | The number of the milestone to associate this pull request with. | |
|
||||||
| `draft` | Create a [draft pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests). Valid values are `true` (only on create), `always-true` (on create and update), and `false`. | `false` |
|
| `draft` | Create a [draft pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests). Valid values are `true` (only on create), `always-true` (on create and update), and `false`. | `false` |
|
||||||
| `maintainer-can-modify` | Indicates whether [maintainers can modify](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) the pull request. | `true` |
|
| `maintainer-can-modify` | Indicates whether [maintainers can modify](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) the pull request. | `true` |
|
||||||
|
| `github-server-url` | A comma-separated list of GHES/Gitea hostnames (e.g., 'gitea.example.com,gitea.company.org'). Required when using this action with Gitea instances. | |
|
||||||
|
|
||||||
#### token
|
#### token
|
||||||
|
|
||||||
|
|||||||
@ -82,6 +82,10 @@ inputs:
|
|||||||
maintainer-can-modify:
|
maintainer-can-modify:
|
||||||
description: 'Indicates whether maintainers can modify the pull request.'
|
description: 'Indicates whether maintainers can modify the pull request.'
|
||||||
default: true
|
default: true
|
||||||
|
github-server-url:
|
||||||
|
description: >
|
||||||
|
A comma-separated list of Gitea hostnames (e.g., 'gitea.example.com,gitea.company.org').
|
||||||
|
Required when using this action with Gitea instances.
|
||||||
outputs:
|
outputs:
|
||||||
pull-request-number:
|
pull-request-number:
|
||||||
description: 'The pull request number'
|
description: 'The pull request number'
|
||||||
|
|||||||
233
dist/index.js
vendored
233
dist/index.js
vendored
@ -1327,11 +1327,12 @@ class GitHubHelper {
|
|||||||
if (token) {
|
if (token) {
|
||||||
options.auth = `${token}`;
|
options.auth = `${token}`;
|
||||||
}
|
}
|
||||||
if (githubServerHostname !== 'github.com') {
|
// Check if this is a Gitea instance
|
||||||
options.baseUrl = `https://${githubServerHostname}/api/v3`;
|
this.isGiteaInstance = (0, octokit_client_1.isGitea)(githubServerHostname);
|
||||||
}
|
// Set the appropriate API base URL for GitHub or Gitea
|
||||||
else {
|
options.baseUrl = (0, octokit_client_1.getApiBaseUrl)(githubServerHostname);
|
||||||
options.baseUrl = 'https://api.github.com';
|
if (this.isGiteaInstance) {
|
||||||
|
core.info(`Detected Gitea instance at ${githubServerHostname}. Using API endpoint ${options.baseUrl}`);
|
||||||
}
|
}
|
||||||
options.throttle = octokit_client_1.throttleOptions;
|
options.throttle = octokit_client_1.throttleOptions;
|
||||||
this.octokit = new octokit_client_1.Octokit(options);
|
this.octokit = new octokit_client_1.Octokit(options);
|
||||||
@ -1347,10 +1348,21 @@ class GitHubHelper {
|
|||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const [headOwner] = headRepository.split('/');
|
const [headOwner] = headRepository.split('/');
|
||||||
const headBranch = `${headOwner}:${inputs.branch}`;
|
const headBranch = `${headOwner}:${inputs.branch}`;
|
||||||
|
// For Gitea, the head branch format is different - it's just the branch name
|
||||||
|
const giteaHeadBranch = this.isGiteaInstance ? inputs.branch : headBranch;
|
||||||
// Try to create the pull request
|
// Try to create the pull request
|
||||||
try {
|
try {
|
||||||
core.info(`Attempting creation of pull request`);
|
core.info(`Attempting creation of pull request`);
|
||||||
const { data: pull } = yield this.octokit.rest.pulls.create(Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { title: inputs.title, head: headBranch, head_repo: headRepository, base: inputs.base, body: inputs.body, draft: inputs.draft.value, maintainer_can_modify: inputs.maintainerCanModify }));
|
const createParams = Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { title: inputs.title, head: this.isGiteaInstance ? giteaHeadBranch : headBranch, base: inputs.base, body: inputs.body, maintainer_can_modify: inputs.maintainerCanModify });
|
||||||
|
// Add draft parameter only for GitHub (Gitea doesn't support draft PRs via the API)
|
||||||
|
if (!this.isGiteaInstance) {
|
||||||
|
Object.assign(createParams, { draft: inputs.draft.value });
|
||||||
|
}
|
||||||
|
// For Gitea, if using fork, we need to specify the head_repo
|
||||||
|
if (this.isGiteaInstance && inputs.pushToFork) {
|
||||||
|
Object.assign(createParams, { head_repo: headRepository });
|
||||||
|
}
|
||||||
|
const { data: pull } = yield this.octokit.rest.pulls.create(createParams);
|
||||||
core.info(`Created pull request #${pull.number} (${headBranch} => ${inputs.base})`);
|
core.info(`Created pull request #${pull.number} (${headBranch} => ${inputs.base})`);
|
||||||
return {
|
return {
|
||||||
number: pull.number,
|
number: pull.number,
|
||||||
@ -1376,7 +1388,7 @@ class GitHubHelper {
|
|||||||
}
|
}
|
||||||
// Update the pull request that exists for this branch and base
|
// Update the pull request that exists for this branch and base
|
||||||
core.info(`Fetching existing pull request`);
|
core.info(`Fetching existing pull request`);
|
||||||
const { data: pulls } = yield this.octokit.rest.pulls.list(Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { state: 'open', head: headBranch, base: inputs.base }));
|
const { data: pulls } = yield this.octokit.rest.pulls.list(Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { state: 'open', head: this.isGiteaInstance ? giteaHeadBranch : headBranch, base: inputs.base }));
|
||||||
core.info(`Attempting update of pull request`);
|
core.info(`Attempting update of pull request`);
|
||||||
const { data: pull } = yield this.octokit.rest.pulls.update(Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { pull_number: pulls[0].number, title: inputs.title, body: inputs.body }));
|
const { data: pull } = yield this.octokit.rest.pulls.update(Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { pull_number: pulls[0].number, title: inputs.title, body: inputs.body }));
|
||||||
core.info(`Updated pull request #${pull.number} (${headBranch} => ${inputs.base})`);
|
core.info(`Updated pull request #${pull.number} (${headBranch} => ${inputs.base})`);
|
||||||
@ -1391,11 +1403,22 @@ class GitHubHelper {
|
|||||||
}
|
}
|
||||||
getRepositoryParent(headRepository) {
|
getRepositoryParent(headRepository) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const { data: headRepo } = yield this.octokit.rest.repos.get(Object.assign({}, this.parseRepository(headRepository)));
|
try {
|
||||||
if (!headRepo.parent) {
|
const { data: headRepo } = yield this.octokit.rest.repos.get(Object.assign({}, this.parseRepository(headRepository)));
|
||||||
return null;
|
if (!headRepo.parent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return headRepo.parent.full_name;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
// Gitea may not have the same parent repository structure
|
||||||
|
// Fall back to null if this fails
|
||||||
|
if (this.isGiteaInstance) {
|
||||||
|
core.warning(`Unable to determine parent repository for ${headRepository}. This is expected for Gitea.`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
return headRepo.parent.full_name;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
createOrUpdatePullRequest(inputs, baseRepository, headRepository) {
|
createOrUpdatePullRequest(inputs, baseRepository, headRepository) {
|
||||||
@ -1415,35 +1438,73 @@ class GitHubHelper {
|
|||||||
// Apply assignees
|
// Apply assignees
|
||||||
if (inputs.assignees.length > 0) {
|
if (inputs.assignees.length > 0) {
|
||||||
core.info(`Applying assignees '${inputs.assignees}'`);
|
core.info(`Applying assignees '${inputs.assignees}'`);
|
||||||
yield this.octokit.rest.issues.addAssignees(Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { issue_number: pull.number, assignees: inputs.assignees }));
|
// Gitea has different assignee handling
|
||||||
}
|
if (this.isGiteaInstance) {
|
||||||
// Request reviewers and team reviewers
|
try {
|
||||||
const requestReviewersParams = {};
|
for (const assignee of inputs.assignees) {
|
||||||
if (inputs.reviewers.length > 0) {
|
yield this.octokit.request('POST /repos/{owner}/{repo}/issues/{issue_number}/assignees', Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { issue_number: pull.number, assignees: [assignee] }));
|
||||||
requestReviewersParams['reviewers'] = inputs.reviewers;
|
}
|
||||||
core.info(`Requesting reviewers '${inputs.reviewers}'`);
|
}
|
||||||
}
|
catch (error) {
|
||||||
if (inputs.teamReviewers.length > 0) {
|
core.warning(`Error assigning users in Gitea: ${utils.getErrorMessage(error)}`);
|
||||||
const teams = utils.stripOrgPrefixFromTeams(inputs.teamReviewers);
|
|
||||||
requestReviewersParams['team_reviewers'] = teams;
|
|
||||||
core.info(`Requesting team reviewers '${teams}'`);
|
|
||||||
}
|
|
||||||
if (Object.keys(requestReviewersParams).length > 0) {
|
|
||||||
try {
|
|
||||||
yield this.octokit.rest.pulls.requestReviewers(Object.assign(Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { pull_number: pull.number }), requestReviewersParams));
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
if (utils.getErrorMessage(e).includes(ERROR_PR_REVIEW_TOKEN_SCOPE)) {
|
|
||||||
core.error(`Unable to request reviewers. If requesting team reviewers a 'repo' scoped PAT is required.`);
|
|
||||||
}
|
}
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// GitHub standard API
|
||||||
|
yield this.octokit.rest.issues.addAssignees(Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { issue_number: pull.number, assignees: inputs.assignees }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Skip reviewers functionality for Gitea as it might not be compatible
|
||||||
|
if (!this.isGiteaInstance &&
|
||||||
|
(inputs.reviewers.length > 0 || inputs.teamReviewers.length > 0)) {
|
||||||
|
const requestReviewersParams = {};
|
||||||
|
if (inputs.reviewers.length > 0) {
|
||||||
|
requestReviewersParams['reviewers'] = inputs.reviewers;
|
||||||
|
core.info(`Requesting reviewers '${inputs.reviewers}'`);
|
||||||
|
}
|
||||||
|
if (inputs.teamReviewers.length > 0) {
|
||||||
|
const teams = utils.stripOrgPrefixFromTeams(inputs.teamReviewers);
|
||||||
|
requestReviewersParams['team_reviewers'] = teams;
|
||||||
|
core.info(`Requesting team reviewers '${teams}'`);
|
||||||
|
}
|
||||||
|
if (Object.keys(requestReviewersParams).length > 0) {
|
||||||
|
try {
|
||||||
|
yield this.octokit.rest.pulls.requestReviewers(Object.assign(Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { pull_number: pull.number }), requestReviewersParams));
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
if (utils.getErrorMessage(e).includes(ERROR_PR_REVIEW_TOKEN_SCOPE)) {
|
||||||
|
core.error(`Unable to request reviewers. If requesting team reviewers a 'repo' scoped PAT is required.`);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (this.isGiteaInstance &&
|
||||||
|
(inputs.reviewers.length > 0 || inputs.teamReviewers.length > 0)) {
|
||||||
|
core.warning('Reviewer assignment is not supported for Gitea instances');
|
||||||
}
|
}
|
||||||
return pull;
|
return pull;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
pushSignedCommits(git, branchCommits, baseCommit, repoPath, branchRepository, branch) {
|
pushSignedCommits(git, branchCommits, baseCommit, repoPath, branchRepository, branch) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
var _a, _b;
|
||||||
|
// For Gitea, fall back to standard Git push if signed commits are not supported
|
||||||
|
if (this.isGiteaInstance) {
|
||||||
|
core.warning('Signed commits via API may not be fully supported in Gitea. Falling back to standard Git push.');
|
||||||
|
yield git.push([
|
||||||
|
'--force-with-lease',
|
||||||
|
'origin',
|
||||||
|
`${branch}:refs/heads/${branch}`
|
||||||
|
]);
|
||||||
|
// Return a simplified commit response
|
||||||
|
return {
|
||||||
|
sha: ((_a = branchCommits[branchCommits.length - 1]) === null || _a === void 0 ? void 0 : _a.sha) || baseCommit.sha,
|
||||||
|
tree: ((_b = branchCommits[branchCommits.length - 1]) === null || _b === void 0 ? void 0 : _b.tree) || baseCommit.tree,
|
||||||
|
verified: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Original GitHub implementation
|
||||||
let headCommit = {
|
let headCommit = {
|
||||||
sha: baseCommit.sha,
|
sha: baseCommit.sha,
|
||||||
tree: baseCommit.tree,
|
tree: baseCommit.tree,
|
||||||
@ -1511,43 +1572,89 @@ class GitHubHelper {
|
|||||||
}
|
}
|
||||||
const { data: remoteCommit } = yield this.octokit.rest.git.createCommit(Object.assign(Object.assign({}, repository), { parents: [parentCommit.sha], tree: treeSha, message: `${commit.subject}\n\n${commit.body}` }));
|
const { data: remoteCommit } = yield this.octokit.rest.git.createCommit(Object.assign(Object.assign({}, repository), { parents: [parentCommit.sha], tree: treeSha, message: `${commit.subject}\n\n${commit.body}` }));
|
||||||
core.info(`Created commit ${remoteCommit.sha} for local commit ${commit.sha}`);
|
core.info(`Created commit ${remoteCommit.sha} for local commit ${commit.sha}`);
|
||||||
core.info(`Commit verified: ${remoteCommit.verification.verified}; reason: ${remoteCommit.verification.reason}`);
|
// Gitea might not have the same verification structure
|
||||||
|
let verified = false;
|
||||||
|
if (remoteCommit.verification &&
|
||||||
|
typeof remoteCommit.verification.verified !== 'undefined') {
|
||||||
|
verified = remoteCommit.verification.verified;
|
||||||
|
core.info(`Commit verified: ${verified}; reason: ${remoteCommit.verification.reason || 'unknown'}`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.info('Commit verification information not available');
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
sha: remoteCommit.sha,
|
sha: remoteCommit.sha,
|
||||||
tree: remoteCommit.tree.sha,
|
tree: remoteCommit.tree.sha,
|
||||||
verified: remoteCommit.verification.verified
|
verified: verified
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
getCommit(sha, branchRepository) {
|
getCommit(sha, branchRepository) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const repository = this.parseRepository(branchRepository);
|
const repository = this.parseRepository(branchRepository);
|
||||||
const { data: remoteCommit } = yield this.octokit.rest.git.getCommit(Object.assign(Object.assign({}, repository), { commit_sha: sha }));
|
try {
|
||||||
return {
|
const { data: remoteCommit } = yield this.octokit.rest.git.getCommit(Object.assign(Object.assign({}, repository), { commit_sha: sha }));
|
||||||
sha: remoteCommit.sha,
|
// Handle different verification structure between GitHub and Gitea
|
||||||
tree: remoteCommit.tree.sha,
|
let verified = false;
|
||||||
verified: remoteCommit.verification.verified
|
if (remoteCommit.verification &&
|
||||||
};
|
typeof remoteCommit.verification.verified !== 'undefined') {
|
||||||
|
verified = remoteCommit.verification.verified;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
sha: remoteCommit.sha,
|
||||||
|
tree: remoteCommit.tree.sha,
|
||||||
|
verified: verified
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (this.isGiteaInstance) {
|
||||||
|
core.warning(`Unable to get commit details from Gitea. This might be expected: ${utils.getErrorMessage(error)}`);
|
||||||
|
// Return a placeholder response
|
||||||
|
return {
|
||||||
|
sha: sha,
|
||||||
|
tree: '', // We don't know the tree SHA
|
||||||
|
verified: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
createOrUpdateRef(branchRepository, branch, newHead) {
|
createOrUpdateRef(branchRepository, branch, newHead) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const repository = this.parseRepository(branchRepository);
|
const repository = this.parseRepository(branchRepository);
|
||||||
const branchExists = yield this.octokit.rest.repos
|
// Check if branch exists
|
||||||
.getBranch(Object.assign(Object.assign({}, repository), { branch: branch }))
|
let branchExists = false;
|
||||||
.then(() => true, () => false);
|
try {
|
||||||
|
yield this.octokit.rest.repos.getBranch(Object.assign(Object.assign({}, repository), { branch: branch }));
|
||||||
|
branchExists = true;
|
||||||
|
}
|
||||||
|
catch (_a) {
|
||||||
|
branchExists = false;
|
||||||
|
}
|
||||||
if (branchExists) {
|
if (branchExists) {
|
||||||
core.info(`Branch ${branch} exists; Updating ref`);
|
core.info(`Branch ${branch} exists; Updating ref`);
|
||||||
yield this.octokit.rest.git.updateRef(Object.assign(Object.assign({}, repository), { sha: newHead, ref: `heads/${branch}`, force: true }));
|
yield this.octokit.rest.git.updateRef(Object.assign(Object.assign({}, repository), { sha: newHead, ref: `heads/${branch}`, force: true }));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.info(`Branch ${branch} does not exist; Creating ref`);
|
core.info(`Branch ${branch} does not exist; Creating ref`);
|
||||||
yield this.octokit.rest.git.createRef(Object.assign(Object.assign({}, repository), { sha: newHead, ref: `refs/heads/${branch}` }));
|
try {
|
||||||
|
yield this.octokit.rest.git.createRef(Object.assign(Object.assign({}, repository), { sha: newHead, ref: `refs/heads/${branch}` }));
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
core.error(`Failed to create branch: ${utils.getErrorMessage(error)}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
convertToDraft(id) {
|
convertToDraft(id) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
// Skip for Gitea since GraphQL API likely isn't compatible
|
||||||
|
if (this.isGiteaInstance) {
|
||||||
|
core.warning('Draft pull requests are not supported in Gitea via the GraphQL API');
|
||||||
|
return;
|
||||||
|
}
|
||||||
core.info(`Converting pull request to draft`);
|
core.info(`Converting pull request to draft`);
|
||||||
yield this.octokit.graphql({
|
yield this.octokit.graphql({
|
||||||
query: `mutation($pullRequestId: ID!) {
|
query: `mutation($pullRequestId: ID!) {
|
||||||
@ -1627,9 +1734,26 @@ function getDraftInput() {
|
|||||||
return { value: core.getBooleanInput('draft'), always: false };
|
return { value: core.getBooleanInput('draft'), always: false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Set Gitea instances from environment variable or input
|
||||||
|
function configureGiteaInstances() {
|
||||||
|
// First check if there's already an environment variable
|
||||||
|
if (!process.env.GITEA_INSTANCES) {
|
||||||
|
// If not, check if it was provided as input
|
||||||
|
const giteaInstancesInput = core.getInput('github-server-url');
|
||||||
|
if (giteaInstancesInput) {
|
||||||
|
core.info(`Setting GITEA_INSTANCES environment variable to: ${giteaInstancesInput}`);
|
||||||
|
process.env.GITEA_INSTANCES = giteaInstancesInput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (process.env.GITEA_INSTANCES) {
|
||||||
|
core.info(`Configured Gitea instances: ${process.env.GITEA_INSTANCES}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
function run() {
|
function run() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
|
// Configure Gitea instances before anything else
|
||||||
|
configureGiteaInstances();
|
||||||
const inputs = {
|
const inputs = {
|
||||||
token: core.getInput('token'),
|
token: core.getInput('token'),
|
||||||
branchToken: core.getInput('branch-token'),
|
branchToken: core.getInput('branch-token'),
|
||||||
@ -1727,6 +1851,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|||||||
})();
|
})();
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.throttleOptions = exports.Octokit = void 0;
|
exports.throttleOptions = exports.Octokit = void 0;
|
||||||
|
exports.isGitea = isGitea;
|
||||||
|
exports.getApiBaseUrl = getApiBaseUrl;
|
||||||
const core = __importStar(__nccwpck_require__(7484));
|
const core = __importStar(__nccwpck_require__(7484));
|
||||||
const core_1 = __nccwpck_require__(767);
|
const core_1 = __nccwpck_require__(767);
|
||||||
const plugin_paginate_rest_1 = __nccwpck_require__(3779);
|
const plugin_paginate_rest_1 = __nccwpck_require__(3779);
|
||||||
@ -1754,6 +1880,25 @@ function autoProxyAgent(octokit) {
|
|||||||
options.request.fetch = proxy_1.fetch;
|
options.request.fetch = proxy_1.fetch;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Determine if a hostname is a Gitea instance
|
||||||
|
function isGitea(hostname) {
|
||||||
|
return process.env.GITEA_INSTANCES
|
||||||
|
? process.env.GITEA_INSTANCES.split(',').includes(hostname)
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
// Get the API base URL for a given hostname
|
||||||
|
function getApiBaseUrl(hostname) {
|
||||||
|
// For GitHub, we'll use their standard API endpoint
|
||||||
|
if (hostname === 'github.com') {
|
||||||
|
return 'https://api.github.com';
|
||||||
|
}
|
||||||
|
// For Gitea, we need to modify the API path
|
||||||
|
if (isGitea(hostname)) {
|
||||||
|
return `https://${hostname}/api/v1`;
|
||||||
|
}
|
||||||
|
// For GitHub Enterprise or other GitHub-compatible APIs
|
||||||
|
return `https://${hostname}/api/v3`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {Inputs} from './create-pull-request'
|
import {Inputs} from './create-pull-request'
|
||||||
import {Commit, GitCommandManager} from './git-command-manager'
|
import {Commit, GitCommandManager} from './git-command-manager'
|
||||||
import {Octokit, OctokitOptions, throttleOptions} from './octokit-client'
|
import {
|
||||||
|
Octokit,
|
||||||
|
OctokitOptions,
|
||||||
|
throttleOptions,
|
||||||
|
isGitea,
|
||||||
|
getApiBaseUrl
|
||||||
|
} from './octokit-client'
|
||||||
import pLimit from 'p-limit'
|
import pLimit from 'p-limit'
|
||||||
import * as utils from './utils'
|
import * as utils from './utils'
|
||||||
|
|
||||||
@ -9,7 +15,6 @@ const ERROR_PR_ALREADY_EXISTS = 'A pull request already exists for'
|
|||||||
const ERROR_PR_REVIEW_TOKEN_SCOPE =
|
const ERROR_PR_REVIEW_TOKEN_SCOPE =
|
||||||
'Validation Failed: "Could not resolve to a node with the global id of'
|
'Validation Failed: "Could not resolve to a node with the global id of'
|
||||||
const ERROR_PR_FORK_COLLAB = `Fork collab can't be granted by someone without permission`
|
const ERROR_PR_FORK_COLLAB = `Fork collab can't be granted by someone without permission`
|
||||||
|
|
||||||
const blobCreationLimit = pLimit(8)
|
const blobCreationLimit = pLimit(8)
|
||||||
|
|
||||||
interface Repository {
|
interface Repository {
|
||||||
@ -40,17 +45,26 @@ type TreeObject = {
|
|||||||
|
|
||||||
export class GitHubHelper {
|
export class GitHubHelper {
|
||||||
private octokit: InstanceType<typeof Octokit>
|
private octokit: InstanceType<typeof Octokit>
|
||||||
|
private isGiteaInstance: boolean
|
||||||
|
|
||||||
constructor(githubServerHostname: string, token: string) {
|
constructor(githubServerHostname: string, token: string) {
|
||||||
const options: OctokitOptions = {}
|
const options: OctokitOptions = {}
|
||||||
if (token) {
|
if (token) {
|
||||||
options.auth = `${token}`
|
options.auth = `${token}`
|
||||||
}
|
}
|
||||||
if (githubServerHostname !== 'github.com') {
|
|
||||||
options.baseUrl = `https://${githubServerHostname}/api/v3`
|
// Check if this is a Gitea instance
|
||||||
} else {
|
this.isGiteaInstance = isGitea(githubServerHostname)
|
||||||
options.baseUrl = 'https://api.github.com'
|
|
||||||
|
// Set the appropriate API base URL for GitHub or Gitea
|
||||||
|
options.baseUrl = getApiBaseUrl(githubServerHostname)
|
||||||
|
|
||||||
|
if (this.isGiteaInstance) {
|
||||||
|
core.info(
|
||||||
|
`Detected Gitea instance at ${githubServerHostname}. Using API endpoint ${options.baseUrl}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
options.throttle = throttleOptions
|
options.throttle = throttleOptions
|
||||||
this.octokit = new Octokit(options)
|
this.octokit = new Octokit(options)
|
||||||
}
|
}
|
||||||
@ -71,19 +85,33 @@ export class GitHubHelper {
|
|||||||
const [headOwner] = headRepository.split('/')
|
const [headOwner] = headRepository.split('/')
|
||||||
const headBranch = `${headOwner}:${inputs.branch}`
|
const headBranch = `${headOwner}:${inputs.branch}`
|
||||||
|
|
||||||
|
// For Gitea, the head branch format is different - it's just the branch name
|
||||||
|
const giteaHeadBranch = this.isGiteaInstance ? inputs.branch : headBranch
|
||||||
|
|
||||||
// Try to create the pull request
|
// Try to create the pull request
|
||||||
try {
|
try {
|
||||||
core.info(`Attempting creation of pull request`)
|
core.info(`Attempting creation of pull request`)
|
||||||
const {data: pull} = await this.octokit.rest.pulls.create({
|
const createParams = {
|
||||||
...this.parseRepository(baseRepository),
|
...this.parseRepository(baseRepository),
|
||||||
title: inputs.title,
|
title: inputs.title,
|
||||||
head: headBranch,
|
head: this.isGiteaInstance ? giteaHeadBranch : headBranch,
|
||||||
head_repo: headRepository,
|
|
||||||
base: inputs.base,
|
base: inputs.base,
|
||||||
body: inputs.body,
|
body: inputs.body,
|
||||||
draft: inputs.draft.value,
|
|
||||||
maintainer_can_modify: inputs.maintainerCanModify
|
maintainer_can_modify: inputs.maintainerCanModify
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// Add draft parameter only for GitHub (Gitea doesn't support draft PRs via the API)
|
||||||
|
if (!this.isGiteaInstance) {
|
||||||
|
Object.assign(createParams, {draft: inputs.draft.value})
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Gitea, if using fork, we need to specify the head_repo
|
||||||
|
if (this.isGiteaInstance && inputs.pushToFork) {
|
||||||
|
Object.assign(createParams, {head_repo: headRepository})
|
||||||
|
}
|
||||||
|
|
||||||
|
const {data: pull} = await this.octokit.rest.pulls.create(createParams)
|
||||||
|
|
||||||
core.info(
|
core.info(
|
||||||
`Created pull request #${pull.number} (${headBranch} => ${inputs.base})`
|
`Created pull request #${pull.number} (${headBranch} => ${inputs.base})`
|
||||||
)
|
)
|
||||||
@ -116,9 +144,10 @@ export class GitHubHelper {
|
|||||||
const {data: pulls} = await this.octokit.rest.pulls.list({
|
const {data: pulls} = await this.octokit.rest.pulls.list({
|
||||||
...this.parseRepository(baseRepository),
|
...this.parseRepository(baseRepository),
|
||||||
state: 'open',
|
state: 'open',
|
||||||
head: headBranch,
|
head: this.isGiteaInstance ? giteaHeadBranch : headBranch,
|
||||||
base: inputs.base
|
base: inputs.base
|
||||||
})
|
})
|
||||||
|
|
||||||
core.info(`Attempting update of pull request`)
|
core.info(`Attempting update of pull request`)
|
||||||
const {data: pull} = await this.octokit.rest.pulls.update({
|
const {data: pull} = await this.octokit.rest.pulls.update({
|
||||||
...this.parseRepository(baseRepository),
|
...this.parseRepository(baseRepository),
|
||||||
@ -126,9 +155,11 @@ export class GitHubHelper {
|
|||||||
title: inputs.title,
|
title: inputs.title,
|
||||||
body: inputs.body
|
body: inputs.body
|
||||||
})
|
})
|
||||||
|
|
||||||
core.info(
|
core.info(
|
||||||
`Updated pull request #${pull.number} (${headBranch} => ${inputs.base})`
|
`Updated pull request #${pull.number} (${headBranch} => ${inputs.base})`
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
number: pull.number,
|
number: pull.number,
|
||||||
html_url: pull.html_url,
|
html_url: pull.html_url,
|
||||||
@ -139,13 +170,27 @@ export class GitHubHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getRepositoryParent(headRepository: string): Promise<string | null> {
|
async getRepositoryParent(headRepository: string): Promise<string | null> {
|
||||||
const {data: headRepo} = await this.octokit.rest.repos.get({
|
try {
|
||||||
...this.parseRepository(headRepository)
|
const {data: headRepo} = await this.octokit.rest.repos.get({
|
||||||
})
|
...this.parseRepository(headRepository)
|
||||||
if (!headRepo.parent) {
|
})
|
||||||
return null
|
|
||||||
|
if (!headRepo.parent) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return headRepo.parent.full_name
|
||||||
|
} catch (error) {
|
||||||
|
// Gitea may not have the same parent repository structure
|
||||||
|
// Fall back to null if this fails
|
||||||
|
if (this.isGiteaInstance) {
|
||||||
|
core.warning(
|
||||||
|
`Unable to determine parent repository for ${headRepository}. This is expected for Gitea.`
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
return headRepo.parent.full_name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async createOrUpdatePullRequest(
|
async createOrUpdatePullRequest(
|
||||||
@ -169,6 +214,7 @@ export class GitHubHelper {
|
|||||||
milestone: inputs.milestone
|
milestone: inputs.milestone
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply labels
|
// Apply labels
|
||||||
if (inputs.labels.length > 0) {
|
if (inputs.labels.length > 0) {
|
||||||
core.info(`Applying labels '${inputs.labels}'`)
|
core.info(`Applying labels '${inputs.labels}'`)
|
||||||
@ -178,44 +224,76 @@ export class GitHubHelper {
|
|||||||
labels: inputs.labels
|
labels: inputs.labels
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply assignees
|
// Apply assignees
|
||||||
if (inputs.assignees.length > 0) {
|
if (inputs.assignees.length > 0) {
|
||||||
core.info(`Applying assignees '${inputs.assignees}'`)
|
core.info(`Applying assignees '${inputs.assignees}'`)
|
||||||
await this.octokit.rest.issues.addAssignees({
|
// Gitea has different assignee handling
|
||||||
...this.parseRepository(baseRepository),
|
if (this.isGiteaInstance) {
|
||||||
issue_number: pull.number,
|
try {
|
||||||
assignees: inputs.assignees
|
for (const assignee of inputs.assignees) {
|
||||||
})
|
await this.octokit.request(
|
||||||
}
|
'POST /repos/{owner}/{repo}/issues/{issue_number}/assignees',
|
||||||
|
{
|
||||||
// Request reviewers and team reviewers
|
...this.parseRepository(baseRepository),
|
||||||
const requestReviewersParams = {}
|
issue_number: pull.number,
|
||||||
if (inputs.reviewers.length > 0) {
|
assignees: [assignee]
|
||||||
requestReviewersParams['reviewers'] = inputs.reviewers
|
}
|
||||||
core.info(`Requesting reviewers '${inputs.reviewers}'`)
|
)
|
||||||
}
|
}
|
||||||
if (inputs.teamReviewers.length > 0) {
|
} catch (error) {
|
||||||
const teams = utils.stripOrgPrefixFromTeams(inputs.teamReviewers)
|
core.warning(
|
||||||
requestReviewersParams['team_reviewers'] = teams
|
`Error assigning users in Gitea: ${utils.getErrorMessage(error)}`
|
||||||
core.info(`Requesting team reviewers '${teams}'`)
|
|
||||||
}
|
|
||||||
if (Object.keys(requestReviewersParams).length > 0) {
|
|
||||||
try {
|
|
||||||
await this.octokit.rest.pulls.requestReviewers({
|
|
||||||
...this.parseRepository(baseRepository),
|
|
||||||
pull_number: pull.number,
|
|
||||||
...requestReviewersParams
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
if (utils.getErrorMessage(e).includes(ERROR_PR_REVIEW_TOKEN_SCOPE)) {
|
|
||||||
core.error(
|
|
||||||
`Unable to request reviewers. If requesting team reviewers a 'repo' scoped PAT is required.`
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
throw e
|
} else {
|
||||||
|
// GitHub standard API
|
||||||
|
await this.octokit.rest.issues.addAssignees({
|
||||||
|
...this.parseRepository(baseRepository),
|
||||||
|
issue_number: pull.number,
|
||||||
|
assignees: inputs.assignees
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip reviewers functionality for Gitea as it might not be compatible
|
||||||
|
if (
|
||||||
|
!this.isGiteaInstance &&
|
||||||
|
(inputs.reviewers.length > 0 || inputs.teamReviewers.length > 0)
|
||||||
|
) {
|
||||||
|
const requestReviewersParams = {}
|
||||||
|
if (inputs.reviewers.length > 0) {
|
||||||
|
requestReviewersParams['reviewers'] = inputs.reviewers
|
||||||
|
core.info(`Requesting reviewers '${inputs.reviewers}'`)
|
||||||
|
}
|
||||||
|
if (inputs.teamReviewers.length > 0) {
|
||||||
|
const teams = utils.stripOrgPrefixFromTeams(inputs.teamReviewers)
|
||||||
|
requestReviewersParams['team_reviewers'] = teams
|
||||||
|
core.info(`Requesting team reviewers '${teams}'`)
|
||||||
|
}
|
||||||
|
if (Object.keys(requestReviewersParams).length > 0) {
|
||||||
|
try {
|
||||||
|
await this.octokit.rest.pulls.requestReviewers({
|
||||||
|
...this.parseRepository(baseRepository),
|
||||||
|
pull_number: pull.number,
|
||||||
|
...requestReviewersParams
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
if (utils.getErrorMessage(e).includes(ERROR_PR_REVIEW_TOKEN_SCOPE)) {
|
||||||
|
core.error(
|
||||||
|
`Unable to request reviewers. If requesting team reviewers a 'repo' scoped PAT is required.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
this.isGiteaInstance &&
|
||||||
|
(inputs.reviewers.length > 0 || inputs.teamReviewers.length > 0)
|
||||||
|
) {
|
||||||
|
core.warning('Reviewer assignment is not supported for Gitea instances')
|
||||||
|
}
|
||||||
|
|
||||||
return pull
|
return pull
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,11 +305,32 @@ export class GitHubHelper {
|
|||||||
branchRepository: string,
|
branchRepository: string,
|
||||||
branch: string
|
branch: string
|
||||||
): Promise<CommitResponse> {
|
): Promise<CommitResponse> {
|
||||||
|
// For Gitea, fall back to standard Git push if signed commits are not supported
|
||||||
|
if (this.isGiteaInstance) {
|
||||||
|
core.warning(
|
||||||
|
'Signed commits via API may not be fully supported in Gitea. Falling back to standard Git push.'
|
||||||
|
)
|
||||||
|
await git.push([
|
||||||
|
'--force-with-lease',
|
||||||
|
'origin',
|
||||||
|
`${branch}:refs/heads/${branch}`
|
||||||
|
])
|
||||||
|
|
||||||
|
// Return a simplified commit response
|
||||||
|
return {
|
||||||
|
sha: branchCommits[branchCommits.length - 1]?.sha || baseCommit.sha,
|
||||||
|
tree: branchCommits[branchCommits.length - 1]?.tree || baseCommit.tree,
|
||||||
|
verified: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Original GitHub implementation
|
||||||
let headCommit: CommitResponse = {
|
let headCommit: CommitResponse = {
|
||||||
sha: baseCommit.sha,
|
sha: baseCommit.sha,
|
||||||
tree: baseCommit.tree,
|
tree: baseCommit.tree,
|
||||||
verified: false
|
verified: false
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const commit of branchCommits) {
|
for (const commit of branchCommits) {
|
||||||
headCommit = await this.createCommit(
|
headCommit = await this.createCommit(
|
||||||
git,
|
git,
|
||||||
@ -241,6 +340,7 @@ export class GitHubHelper {
|
|||||||
branchRepository
|
branchRepository
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.createOrUpdateRef(branchRepository, branch, headCommit.sha)
|
await this.createOrUpdateRef(branchRepository, branch, headCommit.sha)
|
||||||
return headCommit
|
return headCommit
|
||||||
}
|
}
|
||||||
@ -255,6 +355,7 @@ export class GitHubHelper {
|
|||||||
const repository = this.parseRepository(branchRepository)
|
const repository = this.parseRepository(branchRepository)
|
||||||
// In the case of an empty commit, the tree references the parent's tree
|
// In the case of an empty commit, the tree references the parent's tree
|
||||||
let treeSha = parentCommit.tree
|
let treeSha = parentCommit.tree
|
||||||
|
|
||||||
if (commit.changes.length > 0) {
|
if (commit.changes.length > 0) {
|
||||||
core.info(`Creating tree objects for local commit ${commit.sha}`)
|
core.info(`Creating tree objects for local commit ${commit.sha}`)
|
||||||
const treeObjects = await Promise.all(
|
const treeObjects = await Promise.all(
|
||||||
@ -329,16 +430,29 @@ export class GitHubHelper {
|
|||||||
tree: treeSha,
|
tree: treeSha,
|
||||||
message: `${commit.subject}\n\n${commit.body}`
|
message: `${commit.subject}\n\n${commit.body}`
|
||||||
})
|
})
|
||||||
|
|
||||||
core.info(
|
core.info(
|
||||||
`Created commit ${remoteCommit.sha} for local commit ${commit.sha}`
|
`Created commit ${remoteCommit.sha} for local commit ${commit.sha}`
|
||||||
)
|
)
|
||||||
core.info(
|
|
||||||
`Commit verified: ${remoteCommit.verification.verified}; reason: ${remoteCommit.verification.reason}`
|
// Gitea might not have the same verification structure
|
||||||
)
|
let verified = false
|
||||||
|
if (
|
||||||
|
remoteCommit.verification &&
|
||||||
|
typeof remoteCommit.verification.verified !== 'undefined'
|
||||||
|
) {
|
||||||
|
verified = remoteCommit.verification.verified
|
||||||
|
core.info(
|
||||||
|
`Commit verified: ${verified}; reason: ${remoteCommit.verification.reason || 'unknown'}`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
core.info('Commit verification information not available')
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sha: remoteCommit.sha,
|
sha: remoteCommit.sha,
|
||||||
tree: remoteCommit.tree.sha,
|
tree: remoteCommit.tree.sha,
|
||||||
verified: remoteCommit.verification.verified
|
verified: verified
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,14 +461,40 @@ export class GitHubHelper {
|
|||||||
branchRepository: string
|
branchRepository: string
|
||||||
): Promise<CommitResponse> {
|
): Promise<CommitResponse> {
|
||||||
const repository = this.parseRepository(branchRepository)
|
const repository = this.parseRepository(branchRepository)
|
||||||
const {data: remoteCommit} = await this.octokit.rest.git.getCommit({
|
|
||||||
...repository,
|
try {
|
||||||
commit_sha: sha
|
const {data: remoteCommit} = await this.octokit.rest.git.getCommit({
|
||||||
})
|
...repository,
|
||||||
return {
|
commit_sha: sha
|
||||||
sha: remoteCommit.sha,
|
})
|
||||||
tree: remoteCommit.tree.sha,
|
|
||||||
verified: remoteCommit.verification.verified
|
// Handle different verification structure between GitHub and Gitea
|
||||||
|
let verified = false
|
||||||
|
if (
|
||||||
|
remoteCommit.verification &&
|
||||||
|
typeof remoteCommit.verification.verified !== 'undefined'
|
||||||
|
) {
|
||||||
|
verified = remoteCommit.verification.verified
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sha: remoteCommit.sha,
|
||||||
|
tree: remoteCommit.tree.sha,
|
||||||
|
verified: verified
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (this.isGiteaInstance) {
|
||||||
|
core.warning(
|
||||||
|
`Unable to get commit details from Gitea. This might be expected: ${utils.getErrorMessage(error)}`
|
||||||
|
)
|
||||||
|
// Return a placeholder response
|
||||||
|
return {
|
||||||
|
sha: sha,
|
||||||
|
tree: '', // We don't know the tree SHA
|
||||||
|
verified: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,15 +504,18 @@ export class GitHubHelper {
|
|||||||
newHead: string
|
newHead: string
|
||||||
) {
|
) {
|
||||||
const repository = this.parseRepository(branchRepository)
|
const repository = this.parseRepository(branchRepository)
|
||||||
const branchExists = await this.octokit.rest.repos
|
|
||||||
.getBranch({
|
// Check if branch exists
|
||||||
|
let branchExists = false
|
||||||
|
try {
|
||||||
|
await this.octokit.rest.repos.getBranch({
|
||||||
...repository,
|
...repository,
|
||||||
branch: branch
|
branch: branch
|
||||||
})
|
})
|
||||||
.then(
|
branchExists = true
|
||||||
() => true,
|
} catch {
|
||||||
() => false
|
branchExists = false
|
||||||
)
|
}
|
||||||
|
|
||||||
if (branchExists) {
|
if (branchExists) {
|
||||||
core.info(`Branch ${branch} exists; Updating ref`)
|
core.info(`Branch ${branch} exists; Updating ref`)
|
||||||
@ -384,15 +527,28 @@ export class GitHubHelper {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
core.info(`Branch ${branch} does not exist; Creating ref`)
|
core.info(`Branch ${branch} does not exist; Creating ref`)
|
||||||
await this.octokit.rest.git.createRef({
|
try {
|
||||||
...repository,
|
await this.octokit.rest.git.createRef({
|
||||||
sha: newHead,
|
...repository,
|
||||||
ref: `refs/heads/${branch}`
|
sha: newHead,
|
||||||
})
|
ref: `refs/heads/${branch}`
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
core.error(`Failed to create branch: ${utils.getErrorMessage(error)}`)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async convertToDraft(id: string): Promise<void> {
|
async convertToDraft(id: string): Promise<void> {
|
||||||
|
// Skip for Gitea since GraphQL API likely isn't compatible
|
||||||
|
if (this.isGiteaInstance) {
|
||||||
|
core.warning(
|
||||||
|
'Draft pull requests are not supported in Gitea via the GraphQL API'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
core.info(`Converting pull request to draft`)
|
core.info(`Converting pull request to draft`)
|
||||||
await this.octokit.graphql({
|
await this.octokit.graphql({
|
||||||
query: `mutation($pullRequestId: ID!) {
|
query: `mutation($pullRequestId: ID!) {
|
||||||
|
|||||||
25
src/main.ts
25
src/main.ts
@ -11,8 +11,30 @@ function getDraftInput(): {value: boolean; always: boolean} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set Gitea instances from environment variable or input
|
||||||
|
function configureGiteaInstances() {
|
||||||
|
// First check if there's already an environment variable
|
||||||
|
if (!process.env.GITEA_INSTANCES) {
|
||||||
|
// If not, check if it was provided as input
|
||||||
|
const giteaInstancesInput = core.getInput('github-server-url')
|
||||||
|
if (giteaInstancesInput) {
|
||||||
|
core.info(
|
||||||
|
`Setting GITEA_INSTANCES environment variable to: ${giteaInstancesInput}`
|
||||||
|
)
|
||||||
|
process.env.GITEA_INSTANCES = giteaInstancesInput
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.GITEA_INSTANCES) {
|
||||||
|
core.info(`Configured Gitea instances: ${process.env.GITEA_INSTANCES}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function run(): Promise<void> {
|
async function run(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
// Configure Gitea instances before anything else
|
||||||
|
configureGiteaInstances()
|
||||||
|
|
||||||
const inputs: Inputs = {
|
const inputs: Inputs = {
|
||||||
token: core.getInput('token'),
|
token: core.getInput('token'),
|
||||||
branchToken: core.getInput('branch-token'),
|
branchToken: core.getInput('branch-token'),
|
||||||
@ -44,9 +66,11 @@ async function run(): Promise<void> {
|
|||||||
if (!inputs.token) {
|
if (!inputs.token) {
|
||||||
throw new Error(`Input 'token' not supplied. Unable to continue.`)
|
throw new Error(`Input 'token' not supplied. Unable to continue.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inputs.branchToken) {
|
if (!inputs.branchToken) {
|
||||||
inputs.branchToken = inputs.token
|
inputs.branchToken = inputs.token
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputs.bodyPath) {
|
if (inputs.bodyPath) {
|
||||||
if (!utils.fileExistsSync(inputs.bodyPath)) {
|
if (!utils.fileExistsSync(inputs.bodyPath)) {
|
||||||
throw new Error(`File '${inputs.bodyPath}' does not exist.`)
|
throw new Error(`File '${inputs.bodyPath}' does not exist.`)
|
||||||
@ -54,6 +78,7 @@ async function run(): Promise<void> {
|
|||||||
// Update the body input with the contents of the file
|
// Update the body input with the contents of the file
|
||||||
inputs.body = utils.readFile(inputs.bodyPath)
|
inputs.body = utils.readFile(inputs.bodyPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 65536 characters is the maximum allowed for the pull request body.
|
// 65536 characters is the maximum allowed for the pull request body.
|
||||||
if (inputs.body.length > 65536) {
|
if (inputs.body.length > 65536) {
|
||||||
core.warning(
|
core.warning(
|
||||||
|
|||||||
@ -38,3 +38,26 @@ function autoProxyAgent(octokit: OctokitCore) {
|
|||||||
options.request.fetch = fetch
|
options.request.fetch = fetch
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine if a hostname is a Gitea instance
|
||||||
|
export function isGitea(hostname: string): boolean {
|
||||||
|
return process.env.GITEA_INSTANCES
|
||||||
|
? process.env.GITEA_INSTANCES.split(',').includes(hostname)
|
||||||
|
: false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the API base URL for a given hostname
|
||||||
|
export function getApiBaseUrl(hostname: string): string {
|
||||||
|
// For GitHub, we'll use their standard API endpoint
|
||||||
|
if (hostname === 'github.com') {
|
||||||
|
return 'https://api.github.com'
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Gitea, we need to modify the API path
|
||||||
|
if (isGitea(hostname)) {
|
||||||
|
return `https://${hostname}/api/v1`
|
||||||
|
}
|
||||||
|
|
||||||
|
// For GitHub Enterprise or other GitHub-compatible APIs
|
||||||
|
return `https://${hostname}/api/v3`
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user