Add OpenProject Gitea integration plugin

Implements a Gitea integration for OpenProject following the same
patterns as the bundled GitHub and GitLab integrations.

- Webhook endpoint at /webhooks/gitea with HMAC-SHA256 verification
- Handles pull_request and issue_comment events
- Links Gitea PRs to work packages via OP# references or URLs
- Posts journal comments on WPs for PR lifecycle events
- Gitea tab on the work package split view
- V3 API endpoint: GET /api/v3/work_packages/:id/gitea_pull_requests
- Admin settings UI for bot user and webhook secret
- Nightly cron job to clean up unlinked PR records
This commit is contained in:
quafzi committed 2026-08-28 22:33:27 +02:00
commit 5d409989df
45 files changed
+2561

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
Gemfile.lock
*.gem
.bundle/
vendor/bundle/
node_modules/
+14
View File
@@ -0,0 +1,14 @@
# frozen_string_literal: true
source "https://rubygems.org"
gemspec
# Point at a local OpenProject checkout for development, or use a released gem.
# Uncomment and adjust the path as needed:
#
# gem "openproject", path: "../../"
#
# Or pin to a specific released version:
#
# gem "openproject", "~> 14.0"
+189
View File
@@ -0,0 +1,189 @@
# OpenProject Gitea Integration
An OpenProject module that integrates [Gitea](https://gitea.com) pull requests with OpenProject work packages — following the same patterns as the bundled GitHub and GitLab integrations.
## Overview
When a pull request in Gitea references an OpenProject work package (via a URL or `OP#<id>`), this plugin:
- Records the pull request in OpenProject and links it to the work package
- Posts automated journal comments on the work package when the PR is opened, merged, closed, or reopened
- Shows a **Gitea** tab on the work package split-view listing all linked pull requests with their state and labels
- Reacts to PR comments that mention a work package
## Supported webhook events
| Gitea event | Effect in OpenProject |
|---|---|
| `pull_request` — opened | Links PR to work package; posts "PR Opened" comment |
| `pull_request` — closed (not merged) | Updates PR state; posts "PR Closed" comment |
| `pull_request` — closed (merged) | Updates PR state to merged; posts "PR Merged" comment |
| `pull_request` — reopened | Updates PR state; posts "PR Reopened" comment |
| `pull_request` — edited / synchronized | Updates the stored PR record (no comment) |
| `issue_comment` — created | Posts a comment on any work package referenced in the comment body |
## Referencing work packages
In a pull request title, description, or comment, reference a work package using:
- A full URL: `https://openproject.example.com/work_packages/42`
- A short reference: `OP#42` or `OP#PROJ-42`
Multiple references in the same text are all picked up.
## Requirements
- OpenProject ≥ 13
- Ruby 3.4+
- The `openproject-webhooks` gem (bundled with OpenProject)
## Installation
### Packaged (DEB/RPM) installation
Create a custom Gemfile (e.g. `/etc/openproject/Gemfile.custom`):
```ruby
group :opf_plugins do
gem 'openproject-gitea_integration', '~> 1.0'
end
```
Tell OpenProject to use it and trigger a recompile of frontend assets:
```shell
openproject config:set CUSTOM_PLUGIN_GEMFILE=/etc/openproject/Gemfile.custom
openproject config:set RECOMPILE_ANGULAR_ASSETS=true
sudo openproject configure
```
### Docker installation
Follow the [OpenProject Docker plugin guide](https://www.openproject.org/docs/installation-and-operations/installation/docker/#openproject-plugins). Add the gem to your custom Gemfile and rebuild the image.
### Manual / monorepo installation
1. Clone this repository next to your OpenProject checkout, or place it at `modules/gitea_integration/` inside the OpenProject source tree.
2. Add the gem to your `Gemfile.plugins` (or `Gemfile.modules`):
```ruby
group :opf_plugins do
# from RubyGems:
gem 'openproject-gitea_integration', '~> 1.0'
# or from a local path:
# gem 'openproject-gitea_integration', path: 'modules/gitea_integration'
end
```
3. Install dependencies:
```bash
bundle install
```
4. Run the database migration:
```bash
bundle exec rails db:migrate
```
5. Rebuild the frontend:
```bash
cd frontend && npm ci && npm run build
```
## Configuration
### 1. Create a bot user in OpenProject
Create a dedicated OpenProject user that will post automated comments on work packages. Add this user to every project where you want the integration to be active, with a role that allows:
- Viewing work packages
- Adding work package comments
Generate an **API token** for this user:
1. Log in as the bot user.
2. Go to **My Account → Access Tokens**.
3. Click **Generate** in the API row and copy the token.
### 2. Configure the plugin in OpenProject
Go to **Administration → Integrations → Gitea** and set:
- **Gitea actor** — the bot user created above. When set, only webhook requests authenticated with this user's API token are accepted.
- **Webhook secret** — a shared secret used to verify the HMAC-SHA256 signature on incoming requests. Leaving this blank disables signature verification (not recommended).
### 3. Create a webhook in Gitea
In each Gitea repository (or at the organisation level), go to **Settings → Webhooks → Add Webhook → Gitea**.
| Field | Value |
|---|---|
| Target URL | `https://your-openproject.example.com/webhooks/gitea?key=<api_token>` |
| HTTP Method | POST |
| Content Type | `application/json` |
| Secret | The same value entered as the **Webhook secret** in OpenProject |
| Trigger | **Custom events** → check **Pull Request** and **Issue Comment** |
Replace `<api_token>` with the API token generated for the bot user.
> **Same-host installs:** If Gitea and OpenProject run on the same machine you may need to allow local network requests in Gitea's configuration (`[webhook] ALLOWED_HOST_LIST`).
### 4. Enable the Gitea module per project
In each OpenProject project go to **Project Settings → Modules** and enable the **Gitea** module. This makes the Gitea tab visible on work packages.
## How it works
```
Gitea ──webhook──▶ /webhooks/gitea?key=<token>
│
HookHandler
• verifies HMAC-SHA256 signature
• checks user matches configured bot
• dispatches OpenProject::Notifications event
│
┌────────────┴────────────┐
▼ ▼
NotificationHandler NotificationHandler
::PullRequest ::IssueComment
│
UpsertPullRequest / UpsertGiteaUser
│
work_package.gitea_pull_requests (HABTM)
│
WorkPackages::UpdateService (posts journal comment)
```
The work package split-view tab fetches linked pull requests via:
```
GET /api/v3/work_packages/:id/gitea_pull_requests
```
## Development
Run the OpenProject test suite against this module:
```bash
bundle exec rspec modules/gitea_integration/spec
```
Lint Ruby:
```bash
bundle exec rubocop modules/gitea_integration
```
Lint TypeScript:
```bash
cd frontend && npx eslint src/
```
## License
GNU General Public License v3 — see [LICENSE](../../LICENSE).
@@ -0,0 +1,31 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) the OpenProject GmbH
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2013 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See COPYRIGHT and LICENSE files for more details.
++#%>
<%= render(Primer::OpenProject::PageHeader.new) do |header| %>
<% header.with_title { t(:label_gitea_integration) } %>
<% header.with_breadcrumbs(breadcrumb_items) %>
<% end %>
@@ -0,0 +1,42 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module GiteaIntegration
module Admin
class PageHeaderComponent < ApplicationComponent
def breadcrumb_items
[
{ href: admin_index_path, text: t("label_administration") },
t(:label_gitea_integration)
]
end
end
end
end
@@ -0,0 +1,65 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module GiteaIntegration
module Admin
class SettingsController < ApplicationController
layout "admin"
menu_item :admin_gitea_integration
before_action :require_admin
def show
settings = plugin_settings
user_id = settings[:gitea_user_id].presence
@gitea_comment_user = user_id ? User.find_by(id: user_id) : nil
@webhook_secret = settings[:webhook_secret]
end
def update
merged = plugin_settings.merge(permitted_params)
Setting.plugin_openproject_gitea_integration = merged
flash[:notice] = I18n.t(:notice_successful_update)
redirect_to gitea_integration_admin_settings_path
end
private
def permitted_params
params.permit(:gitea_user_id, :webhook_secret).to_h
end
def plugin_settings
Hash(Setting.plugin_openproject_gitea_integration).with_indifferent_access
end
end
end
end
@@ -0,0 +1,69 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module GiteaIntegration
module Admin
class SettingsForm < ApplicationForm
form do |f|
f.autocompleter(
name: :gitea_user_id,
label: I18n.t(:label_gitea_actor),
caption: I18n.t(:text_gitea_actor_info),
autocomplete_options: {
component: "opce-user-autocompleter",
allowEmpty: true,
defaultData: false,
model: @comment_user_model
}
)
f.text_field(
name: :webhook_secret,
label: I18n.t(:label_gitea_webhook_secret),
caption: I18n.t(:text_gitea_webhook_secret_info),
value: @webhook_secret,
input_width: :xxlarge
)
f.submit(
name: :submit,
label: I18n.t(:button_save),
scheme: :primary
)
end
def initialize(comment_user: nil, webhook_secret: nil)
super()
@comment_user_model = comment_user.present? ? { id: comment_user.id, name: comment_user.name } : nil
@webhook_secret = webhook_secret
end
end
end
end
+76
View File
@@ -0,0 +1,76 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class GiteaPullRequest < ApplicationRecord
LABEL_KEYS = %w[color name].freeze
has_and_belongs_to_many :work_packages
belongs_to :gitea_user, optional: true
belongs_to :merged_by, optional: true, class_name: "GiteaUser"
enum :state, {
open: "open",
closed: "closed",
merged: "merged"
}
validates :gitea_html_url,
:number,
:repository,
:state,
:title,
:gitea_updated_at,
presence: true
validates :body,
presence: { unless: :partial? }
validate :validate_labels_schema
scope :without_work_package, -> { where.missing(:work_packages) }
def self.find_by_gitea_identifiers(url:, id: nil, initialize: false)
raise ArgumentError, "needs a url" if url.blank?
found = find_by(gitea_html_url: url)
found || (new(gitea_id: id, gitea_html_url: url) if initialize)
end
def partial?
[body].all?(&:nil?)
end
private
def validate_labels_schema
return if labels.nil?
return if labels.all? { |label| label.keys.sort == LABEL_KEYS }
errors.add(:labels, :invalid_schema)
end
end
+39
View File
@@ -0,0 +1,39 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class GiteaUser < ApplicationRecord
has_many :gitea_pull_requests
validates :gitea_id,
:gitea_name,
:gitea_login,
:gitea_avatar_url,
presence: true
end
@@ -0,0 +1,41 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) the OpenProject GmbH
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2013 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See COPYRIGHT and LICENSE files for more details.
++#%>
<% html_title t(:label_administration), t(:label_gitea_integration), t(:label_setting_plural) %>
<%= render(GiteaIntegration::Admin::PageHeaderComponent.new) %>
<%= settings_primer_form_with(url: gitea_integration_admin_settings_path, method: :patch) do |f| %>
<% if @webhook_secret.blank? %>
<%= render(Primer::Alpha::Banner.new(scheme: :warning, icon: :alert, mb: 4)) do %>
<%= t(:text_gitea_webhook_secret_missing_warning) %>
<% end %>
<% end %>
<%= render GiteaIntegration::Admin::SettingsForm.new(f,
comment_user: @gitea_comment_user,
webhook_secret: @webhook_secret) %>
<% end %>
@@ -0,0 +1,39 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module Cron
class ClearOldGiteaPullRequestsJob < CronJob
self.cron_expression = "25 1 * * *"
def perform
GiteaPullRequest.without_work_package.destroy_all
end
end
end
+92
View File
@@ -0,0 +1,92 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
---
en:
activerecord:
attributes:
gitea_pull_request:
labels: "Labels"
errors:
models:
gitea_pull_request:
attributes:
labels:
invalid_schema: "must be an array of hashes with keys: color, name"
models:
gitea_pull_request:
one: "Gitea pull request"
other: "Gitea pull requests"
gitea_user:
one: "Gitea user"
other: "Gitea users"
attributes:
gitea: "Gitea identifier"
gitea_avatar_url: "Avatar URL"
gitea_html_url: "Gitea HTML URL"
gitea_login: "Gitea login"
gitea_name: "Gitea name"
gitea_pull_request: "Pull Request"
gitea_updated_at: "Updated at"
gitea_integration:
pull_request_opened_comment: >
**PR Opened:** Pull request %{pr_number} [%{pr_title}](%{pr_url}) for [%{repository}](%{repository_url})
has been opened by [%{gitea_user}](%{gitea_user_url}).
pull_request_closed_comment: >
**PR Closed:** Pull request %{pr_number} [%{pr_title}](%{pr_url}) for [%{repository}](%{repository_url})
has been closed by [%{gitea_user}](%{gitea_user_url}).
pull_request_merged_comment: >
**PR Merged:** Pull request %{pr_number} [%{pr_title}](%{pr_url}) for [%{repository}](%{repository_url})
has been merged by [%{gitea_user}](%{gitea_user_url}).
pull_request_reopened_comment: >
**PR Reopened:** Pull request %{pr_number} [%{pr_title}](%{pr_url}) for [%{repository}](%{repository_url})
has been reopened by [%{gitea_user}](%{gitea_user_url}).
issue_comment_referenced_comment: >
**Referenced in Comment:** [%{gitea_user}](%{gitea_user_url}) referenced this work package in a
[comment](%{comment_url}) on issue %{issue_number} [%{issue_title}](%{comment_url})
in [%{repository}](%{repository_url}):
%{comment_body}
label_gitea_actor: "Gitea actor"
label_gitea_integration: "Gitea Integration"
label_gitea_webhook_secret: "Webhook secret"
permission_show_gitea_content: "Show Gitea content"
project_module_gitea: "Gitea"
text_gitea_actor_info: >
The OpenProject user whose API key must be used to authenticate incoming webhook requests.
When set, requests authenticated with any other user's credentials are rejected.
This user also posts automated comments on work packages. Defaults to the system user when not set.
text_gitea_webhook_secret_info: >
A secret token shared with Gitea when configuring the webhook.
When set, OpenProject verifies the X-Gitea-Signature header on every incoming request,
rejecting payloads that do not match. Leave blank to skip verification (not recommended).
text_gitea_webhook_secret_missing_warning: >
No webhook secret is configured. Any request to the Gitea webhook endpoint will be accepted
without verification, which may allow unauthorized actors to forge events. It is strongly
recommended to set a secret.
+55
View File
@@ -0,0 +1,55 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
---
en:
js:
gitea_integration:
tab_header_pr:
copy_menu:
description: Copy git snippets to clipboard
label: Git snippets
create_pr:
description: Create a Pull Request
label: Create PR
git_actions:
branch_name: Branch name
cmd: Create branch with empty commit
commit_message: Commit message
copy_error: Copy failed!
copy_success: Copied!
title: Quick snippets for Git
title: "Pull requests"
tab_prs:
empty: There are no pull requests linked yet. Link an existing PR by using the code <code>OP#%{wp_id}</code> in the PR title/description or create a new PR.
updated_on: Updated on
work_packages:
tab_name: "Gitea"
work_packages:
tabs:
gitea: "Gitea"
+37
View File
@@ -0,0 +1,37 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
Rails.application.routes.draw do
namespace "gitea_integration" do
namespace "admin" do
resource :settings, only: %i[show update]
end
end
end
@@ -0,0 +1,44 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require Rails.root.join("db/migrate/migration_utils/squashed_migration").to_s
require_relative "tables/gitea_pull_requests"
require_relative "tables/gitea_users"
require_relative "tables/gitea_pull_requests_work_packages"
class AggregatedGiteaMigrations < SquashedMigration
squashed_migrations *%w[
20240101000000_gitea_integration_models
]
tables Tables::GiteaPullRequests,
Tables::GiteaUsers,
Tables::GiteaPullRequestsWorkPackages
end
+55
View File
@@ -0,0 +1,55 @@
# frozen_string_literal: true
# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
require Rails.root.join("db/migrate/tables/base").to_s
class Tables::GiteaPullRequests < Tables::Base
def self.table(migration)
create_table migration do |t|
t.references :gitea_user
t.references :merged_by
t.bigint :gitea_id
t.integer :number, null: false
t.string :gitea_html_url, null: false
t.string :state, null: false
t.string :repository, null: false
t.datetime :gitea_updated_at, precision: nil
t.string :title
t.text :body
t.boolean :draft
t.boolean :merged
t.datetime :merged_at, precision: nil
t.json :labels
t.timestamps precision: nil
end
end
end
@@ -0,0 +1,42 @@
# frozen_string_literal: true
# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
require Rails.root.join("db/migrate/tables/base").to_s
class Tables::GiteaPullRequestsWorkPackages < Tables::Base
def self.table(migration)
migration.create_join_table :gitea_pull_requests, :work_packages do |t|
t.index :gitea_pull_request_id, name: "gitea_pr_wp_pr_id"
t.index %i[gitea_pull_request_id work_package_id],
unique: true,
name: "unique_index_gitea_prs_wps_on_gitea_pr_id_and_wp_id"
end
end
end
+44
View File
@@ -0,0 +1,44 @@
# frozen_string_literal: true
# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
require Rails.root.join("db/migrate/tables/base").to_s
class Tables::GiteaUsers < Tables::Base
def self.table(migration)
create_table migration do |t|
t.bigint :gitea_id, null: false
t.string :gitea_name, null: false
t.string :gitea_login, null: false
t.string :gitea_avatar_url, null: false
t.timestamps precision: nil
end
end
end
@@ -0,0 +1,46 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) the OpenProject GmbH
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
import { Component, Input, inject, ChangeDetectionStrategy } from '@angular/core';
import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource';
import { TabComponent } from 'core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab';
import { I18nService } from 'core-app/core/i18n/i18n.service';
import { PathHelperService } from 'core-app/core/path-helper/path-helper.service';
@Component({
selector: 'gitea-tab',
templateUrl: './gitea-tab.template.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false,
})
export class GiteaTabComponent implements TabComponent {
readonly PathHelper = inject(PathHelperService);
readonly I18n = inject(I18nService);
@Input() public workPackage:WorkPackageResource;
}
@@ -0,0 +1 @@
<tab-prs [workPackage]="workPackage" />
+93
View File
@@ -0,0 +1,93 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) the OpenProject GmbH
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
import { Injector, NgModule, inject } from '@angular/core';
import { OpSharedModule } from 'core-app/shared/shared.module';
import { OpenprojectTabsModule } from 'core-app/shared/components/tabs/openproject-tabs.module';
import { WorkPackageTabsService } from 'core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service';
import { GiteaTabComponent } from './gitea-tab/gitea-tab.component';
import { TabPrsComponent } from './tab-prs/tab-prs.component';
import { PullRequestComponent } from './pull-request/pull-request.component';
import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service';
import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service';
import { CollectionResource } from 'core-app/features/hal/resources/collection-resource';
export function workPackageGiteaCount(
workPackage:WorkPackageResource,
injector:Injector,
):Observable<number> {
const halResourceService = injector.get(HalResourceService);
const apiV3Service = injector.get(ApiV3Service);
const basePath = apiV3Service.work_packages.id(workPackage.id as string).path;
return halResourceService
.get<CollectionResource>(basePath + '/gitea_pull_requests')
.pipe(map((collection) => collection.count));
}
export function initializeGiteaIntegrationPlugin(injector:Injector) {
const wpTabService = injector.get(WorkPackageTabsService);
wpTabService.registerBefore(
'watchers',
{
component: GiteaTabComponent,
name: I18n.t('js.gitea_integration.work_packages.tab_name'),
id: 'gitea',
displayable: (workPackage) => !!workPackage.gitea,
count: workPackageGiteaCount,
},
);
}
@NgModule({
imports: [
OpSharedModule,
OpenprojectTabsModule,
],
declarations: [
GiteaTabComponent,
TabPrsComponent,
PullRequestComponent,
],
exports: [
GiteaTabComponent,
TabPrsComponent,
PullRequestComponent,
],
})
export class PluginModule {
constructor() {
const injector = inject(Injector);
initializeGiteaIntegrationPlugin(injector);
}
}
@@ -0,0 +1,50 @@
<div
class="op-pull-request--title"
[textContent]="pullRequest.title"
></div>
<span class="op-pull-request--state" [ngClass]="'op-pull-request--state_' + state">
{{ state }} |
<a
[ngClass]="'op-pull-request--link_' + state"
[href]="pullRequest.htmlUrl"
target="_blank"
rel="noopener noreferrer"
[textContent]="pullRequest.repository + '#' + pullRequest.number"
></a>
</span>
@if (pullRequest.labels && pullRequest.labels.length > 0) {
<button (click)="toggleLabels(pullRequest.number)" class="op-pull-request--label_icon" title="Show/Hide labels">
Labels
</button>
}
<div class="op-pull-request--info_created">
{{ text.label_created_by }}
<span class="op-pull-request--user">
@if (pullRequest.giteaUser) {
<span
class="op-pull-request--username"
[textContent]="pullRequest.giteaUser.login"
></span>
}
</span>
<span class="op-pull-request--date">
{{ text.label_last_updated_on }}
<op-date-time [dateTimeValue]="pullRequest.gitea_updated_at" />
</span>.
</div>
<div
class="op-pull-request--labels"
[ngClass]="'op-pull-request--labels-' + pullRequest.number"
style="display: none;">
@for (label of pullRequest.labels; track label) {
<span>
<span class="op-pull-request--label" [ngStyle]="{'background-color': label.color}">
{{ label.name }}
</span>
</span>
}
</div>
@@ -0,0 +1,66 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) the OpenProject GmbH
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
import { Component, Input, inject, ChangeDetectionStrategy } from '@angular/core';
import { PathHelperService } from 'core-app/core/path-helper/path-helper.service';
import { I18nService } from 'core-app/core/i18n/i18n.service';
import { IGiteaPullRequestResource } from 'core-app/features/plugins/linked/openproject-gitea_integration/typings';
@Component({
selector: 'gitea-pull-request',
templateUrl: './pull-request.component.html',
host: { class: 'op-pull-request' },
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false,
})
export class PullRequestComponent {
readonly PathHelper = inject(PathHelperService);
readonly I18n = inject(I18nService);
@Input() public pullRequest:IGiteaPullRequestResource;
public text = {
label_created_by: this.I18n.t('js.label_created_by'),
label_last_updated_on: this.I18n.t('js.gitea_integration.updated_on'),
};
get state():string {
if (this.pullRequest.state === 'open') {
return this.pullRequest.draft ? 'draft' : 'open';
}
return this.pullRequest.merged ? 'merged' : 'closed';
}
toggleLabels(identifier:number|undefined):void {
if (identifier === undefined) return;
const el = document.querySelector(`.op-pull-request--labels-${identifier}`) as HTMLElement|null;
if (el) {
el.style.display = el.style.display === 'none' ? 'block' : 'none';
}
}
}
@@ -0,0 +1,69 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) the OpenProject GmbH
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
import { ChangeDetectorRef, Component, Input, OnInit, inject, ChangeDetectionStrategy } from '@angular/core';
import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource';
import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service';
import { CollectionResource } from 'core-app/features/hal/resources/collection-resource';
import { I18nService } from 'core-app/core/i18n/i18n.service';
import { IGiteaPullRequestResource } from 'core-app/features/plugins/linked/openproject-gitea_integration/typings';
import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service';
@Component({
selector: 'tab-prs',
templateUrl: './tab-prs.template.html',
host: { class: 'op-prs' },
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false,
})
export class TabPrsComponent implements OnInit {
readonly I18n = inject(I18nService);
readonly apiV3Service = inject(ApiV3Service);
readonly halResourceService = inject(HalResourceService);
readonly changeDetector = inject(ChangeDetectorRef);
@Input() public workPackage:WorkPackageResource;
public pullRequests:IGiteaPullRequestResource[] = [];
ngOnInit():void {
const basePath = this.apiV3Service.work_packages.id(this.workPackage.id as string).path;
const prsPath = `${basePath}/gitea_pull_requests`;
this.halResourceService
.get<CollectionResource<IGiteaPullRequestResource>>(prsPath)
.subscribe((value) => {
this.pullRequests = value.elements;
this.changeDetector.detectChanges();
});
}
public getEmptyText():string {
return this.I18n.t('js.gitea_integration.tab_prs.empty', { wp_id: this.workPackage.displayId });
}
}
@@ -0,0 +1,10 @@
@if (pullRequests.length === 0) {
<p
class="op-tab-content--text-box"
[innerHTML]="getEmptyText()"
></p>
}
@for (pullRequest of pullRequests; track pullRequest.id || pullRequest.htmlUrl) {
<gitea-pull-request [pullRequest]="pullRequest" />
}
+59
View File
@@ -0,0 +1,59 @@
//-- copyright
// OpenProject is an open source project management software.
// Copyright (C) the OpenProject GmbH
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See docs/COPYRIGHT.rdoc for more details.
//++
import { HalResourceClass } from 'core-app/modules/hal/resources/hal-resource';
export interface IGiteaUserResource {
avatarUrl:string;
login:string;
name:string;
}
export interface IGiteaPullRequestResource extends HalResourceClass {
body?:{
format?:string;
raw?:string;
html?:string;
};
createdAt?:string;
draft?:boolean;
gitea_updated_at?:string;
gitea_html_url?:string;
htmlUrl?:string;
id?:number;
labels?:Array<{ name:string; color:string }>;
merged?:boolean;
mergedAt?:string;
mergedBy?:IGiteaUserResource;
number?:number;
repository?:string;
state?:string;
title?:string;
updatedAt?:string;
giteaUser?:IGiteaUserResource;
}
@@ -0,0 +1,37 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module API
module V3
module GiteaPullRequests
class GiteaPullRequestCollectionRepresenter < ::API::Decorators::UnpaginatedCollection
element_representer ::API::V3::GiteaPullRequests::GiteaPullRequestRepresenter
end
end
end
end
@@ -0,0 +1,98 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require "roar/decorator"
require "roar/json/hal"
module API
module V3
module GiteaPullRequests
class GiteaPullRequestRepresenter < ::API::Decorators::Single
include API::Caching::CachedRepresenter
include API::Decorators::DateProperty
include API::Decorators::FormattableProperty
include API::Decorators::LinkedResource
def initialize(model, current_user:, **_opts)
super(model, current_user:, embed_links: true)
end
cached_representer key_parts: %i[gitea_user merged_by]
property :id
property :number
property :gitea_html_url, as: :htmlUrl
property :state,
render_nil: true
property :repository,
render_nil: true
date_time_property :gitea_updated_at,
render_nil: true,
setter: ->(*) {}
property :title,
render_nil: true
formattable_property :body,
render_nil: true
property :draft,
render_nil: true
property :merged,
render_nil: true
property :labels
associated_resource :gitea_user,
representer: ::API::V3::GiteaPullRequests::GiteaUserRepresenter,
link_title_attribute: :gitea_login
associated_resource :merged_by,
representer: ::API::V3::GiteaPullRequests::GiteaUserRepresenter,
v3_path: :gitea_user,
link_title_attribute: :gitea_login
date_time_property :created_at
date_time_property :updated_at
def _type
"GiteaPullRequest"
end
self.to_eager_load = %i[gitea_user merged_by]
end
end
end
end
@@ -0,0 +1,52 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module API
module V3
module GiteaPullRequests
class GiteaPullRequestsByWorkPackageAPI < ::API::OpenProjectAPI
after_validation do
authorize_in_work_package(:show_gitea_content, work_package: @work_package)
@gitea_pull_requests = @work_package.gitea_pull_requests
end
resources :gitea_pull_requests do
get do
path = api_v3_paths.gitea_pull_requests_by_work_package(@work_package.id)
GiteaPullRequestCollectionRepresenter.new(@gitea_pull_requests,
@gitea_pull_requests.count,
self_link: path,
current_user:)
end
end
end
end
end
end
@@ -0,0 +1,52 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require "roar/decorator"
require "roar/json/hal"
module API
module V3
module GiteaPullRequests
class GiteaUserRepresenter < ::API::Decorators::Single
property :id
property :gitea_id, as: :giteaId
property :gitea_name, as: :name
property :gitea_login, as: :login
property :gitea_avatar_url, as: :avatarUrl
def _type
"GiteaUser"
end
end
end
end
end
+33
View File
@@ -0,0 +1,33 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module OpenProject
module GiteaIntegration
require "open_project/gitea_integration/engine"
end
end
@@ -0,0 +1,123 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require "open_project/plugins"
require_relative "patches/api/work_package_representer"
require_relative "notification_handlers"
require_relative "hook_handler"
require_relative "services"
module OpenProject::GiteaIntegration
class Engine < ::Rails::Engine
engine_name :openproject_gitea_integration
include OpenProject::Plugins::ActsAsOpEngine
def self.settings
{
default: {
"gitea_user_id" => nil,
"webhook_secret" => nil
}
}
end
register "openproject-gitea_integration",
author_url: "https://www.openproject.org",
bundled: false,
settings: do
::Redmine::MenuManager.map(:admin_menu) do |menu|
menu.push :admin_gitea_integration,
{ controller: "/gitea_integration/admin/settings", action: "show" },
parent: :admin_integrations,
if: ->(_) { User.current.admin? },
caption: "Gitea",
icon: :"op-logo-gitea"
end
project_module(:gitea, dependencies: :work_package_tracking) do
permission(:show_gitea_content,
{},
permissible_on: %i[work_package project])
end
menu :work_package_split_view,
:gitea,
{ tab: :gitea },
if: ->(project) {
User.current.allowed_in_project?(:show_gitea_content, project)
},
skip_permissions_check: true,
badge: ->(work_package:, **) {
work_package.gitea_pull_requests.count
},
before: :watchers,
caption: :project_module_gitea
end
patches %w[WorkPackage]
initializer "gitea.register_hook" do
::OpenProject::Webhooks.register_hook "gitea" do |hook, environment, params, user|
HookHandler.new.process(hook, environment, params, user)
end
end
initializer "gitea.subscribe_to_notifications" do
::OpenProject::Notifications.subscribe("gitea.pull_request",
&NotificationHandlers.method(:pull_request))
::OpenProject::Notifications.subscribe("gitea.issue_comment",
&NotificationHandlers.method(:issue_comment))
end
extend_api_response(:v3, :work_packages, :work_package,
&::OpenProject::GiteaIntegration::Patches::API::WorkPackageRepresenter.extension)
add_api_path :gitea_pull_requests_by_work_package do |id|
"#{work_package(id)}/gitea_pull_requests"
end
add_api_path :gitea_user do |id|
"gitea_users/#{id}"
end
add_api_endpoint "API::V3::WorkPackages::WorkPackagesAPI", :id do
mount ::API::V3::GiteaPullRequests::GiteaPullRequestsByWorkPackageAPI
end
add_cron_jobs do
{
"Cron::ClearOldGiteaPullRequestsJob": {
cron: "25 1 * * *",
class: ::Cron::ClearOldGiteaPullRequestsJob.name
}
}
end
end
end
@@ -0,0 +1,87 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module OpenProject::GiteaIntegration
class HookHandler
# Gitea webhook events we handle.
KNOWN_EVENTS = %w[
pull_request
issue_comment
].freeze
def process(_hook, request, params, user)
event_type = request.env["HTTP_X_GITEA_EVENT"].to_s.downcase.tr(" ", "_")
Rails.logger.debug { "Received Gitea webhook #{event_type}" }
return 404 unless KNOWN_EVENTS.include?(event_type)
return 403 unless authorized?(request, user)
notify(params, user, event_type)
200
end
private
def authorized?(request, user)
valid_signature?(request) &&
user.present? &&
(configured_user_id.blank? || user.id == configured_user_id)
end
def notify(params, user, event_type)
payload = params[:payload]
.permit!
.to_h
.merge("open_project_user_id" => user.id,
"gitea_event" => event_type)
OpenProject::Notifications.send(:"gitea.#{event_type}", payload)
end
# Gitea uses HMAC-SHA256, same as GitHub (X-Gitea-Signature header).
def valid_signature?(request)
secret = plugin_settings[:webhook_secret].presence
return true if secret.blank?
signature_header = request.env["HTTP_X_GITEA_SIGNATURE"]
return false if signature_header.blank?
expected = "sha256=#{OpenSSL::HMAC.hexdigest("SHA256", secret, request.raw_post)}"
ActiveSupport::SecurityUtils.secure_compare(expected, signature_header)
end
def configured_user_id
plugin_settings[:gitea_user_id].presence&.to_i
end
def plugin_settings
Hash(Setting.plugin_openproject_gitea_integration).with_indifferent_access
end
end
end
@@ -0,0 +1,104 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module OpenProject::GiteaIntegration
module NotificationHandler
module Helper
def extract_work_package_ids(text)
host_name = Regexp.escape(Setting.host_name)
wp_id = WorkPackage::SemanticIdentifier::ID_ROUTE_CONSTRAINT
url_part = /http(?:s?):\/\/#{host_name}\/(?:\S+?\/)*(?:work_packages|wp)\/(#{wp_id})/
wp_regex = /OP#(#{wp_id})|#{url_part}/
String(text)
.scan(wp_regex)
.filter_map { |groups| groups.compact.first }
.uniq
end
def find_visible_work_packages(ids, user)
WorkPackage
.includes(:project)
.where_display_id_in(ids)
.select { |wp| user.allowed_in_work_package?(:add_work_package_comments, wp) }
end
def find_mentioned_work_packages(text, user)
find_visible_work_packages(extract_work_package_ids(text), user)
end
def comment_on_referenced_work_packages(work_packages, user, notes)
return if notes.nil?
work_packages.each do |work_package|
::WorkPackages::UpdateService
.new(user:, model: work_package)
.call(journal_notes: notes, send_notifications: false)
end
end
##
# A wrapper around a ruby Hash to access webhook payloads.
# All methods called on it are converted to `.fetch` hash-access, raising an error if the key does not exist.
# If the method ends with a question mark, no error is raised if the key does not exist.
# If the fetched value is again a hash, the value is wrapped into a new Payload object.
class Payload
def initialize(payload)
@payload = payload
end
def to_h
@payload.dup
end
def method_missing(name, *args, &block)
super unless args.empty? && block.nil?
value = if name.end_with?("?")
@payload.fetch(name.to_s[..-2], nil)
else
@payload.fetch(name.to_s)
end
return Payload.new(value) if value.is_a?(Hash)
value
end
def respond_to_missing?(_method_name, _include_private = false)
true
end
end
def wrap_payload(payload)
Payload.new(payload)
end
end
end
end
@@ -0,0 +1,69 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module OpenProject::GiteaIntegration
module NotificationHandler
##
# Handles Gitea issue/PR comment notifications.
# Gitea sends issue_comment events for comments on both issues and pull requests.
class IssueComment
include OpenProject::GiteaIntegration::NotificationHandler::Helper
def process(payload_params)
@payload = wrap_payload(payload_params)
return unless payload.action == "created"
user = User.find_by(id: payload.open_project_user_id)
text = payload.comment.body
work_packages = find_mentioned_work_packages(text, user)
return if work_packages.empty?
notes = generate_notes(payload)
comment_on_referenced_work_packages(work_packages, user, notes)
end
private
attr_reader :payload
def generate_notes(payload)
I18n.t("gitea_integration.issue_comment_referenced_comment",
comment_url: payload.comment.html_url,
comment_body: payload.comment.body,
issue_number: payload.issue.number,
issue_title: payload.issue.title,
repository: payload.repository.full_name,
repository_url: payload.repository.html_url,
gitea_user: payload.sender.login,
gitea_user_url: payload.sender.avatar_url)
end
end
end
end
@@ -0,0 +1,94 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module OpenProject::GiteaIntegration
module NotificationHandler
##
# Handles Gitea pull request notifications.
class PullRequest
include OpenProject::GiteaIntegration::NotificationHandler::Helper
COMMENT_ACTIONS = %w[opened closed reopened].freeze
TRACKED_ACTIONS = %w[opened closed reopened edited synchronized].freeze
def process(payload_params)
@payload = wrap_payload(payload_params)
return unless TRACKED_ACTIONS.include?(payload.action)
user = User.find_by(id: payload.open_project_user_id)
text = [payload.pull_request.title, payload.pull_request.body]
.compact_blank
.join(" - ")
work_packages = find_mentioned_work_packages(text, user)
notes = generate_notes(payload)
if COMMENT_ACTIONS.include?(payload.action)
comment_on_referenced_work_packages(work_packages, user, notes)
end
upsert_pull_request(work_packages)
end
private
attr_reader :payload
def generate_notes(payload)
key = {
"opened" => "opened",
"closed" => (payload.pull_request.merged? ? "merged" : "closed"),
"reopened" => "reopened"
}[payload.action]
return nil unless key
I18n.t("gitea_integration.pull_request_#{key}_comment",
pr_number: payload.pull_request.number,
pr_title: payload.pull_request.title,
pr_url: payload.pull_request.html_url,
repository: payload.repository.full_name,
repository_url: payload.repository.html_url,
gitea_user: payload.sender.login,
gitea_user_url: payload.sender.avatar_url)
end
def pull_request_record
return @pull_request_record if defined?(@pull_request_record)
@pull_request_record = GiteaPullRequest.find_by(gitea_html_url: payload.pull_request.html_url)
end
def upsert_pull_request(work_packages)
return if work_packages.empty? && pull_request_record.nil?
OpenProject::GiteaIntegration::Services::UpsertPullRequest.new.call(payload, work_packages:)
end
end
end
end
@@ -0,0 +1,58 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require_relative "notification_handler/helper"
require_relative "notification_handler/pull_request"
require_relative "notification_handler/issue_comment"
module OpenProject::GiteaIntegration
module NotificationHandlers
class << self
def pull_request(payload)
with_logging("pull_request") do
OpenProject::GiteaIntegration::NotificationHandler::PullRequest.new.process(payload)
end
end
def issue_comment(payload)
with_logging("issue_comment") do
OpenProject::GiteaIntegration::NotificationHandler::IssueComment.new.process(payload)
end
end
private
def with_logging(event_hook)
yield if block_given?
rescue StandardError => e
Rails.logger.error "Failed to handle #{event_hook} from Gitea: #{e} #{e.message}"
raise e
end
end
end
end
@@ -0,0 +1,60 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module OpenProject::GiteaIntegration
module Patches
module API
module WorkPackageRepresenter
module_function
def extension
->(*) do
link :gitea,
cache_if: -> { current_user.allowed_in_work_package?(:show_gitea_content, represented) } do
next if represented.new_record?
{
href: "#{work_package_path(id: represented.id)}/tabs/gitea",
title: "gitea"
}
end
link :gitea_pull_requests do
next if represented.new_record?
{
href: api_v3_paths.gitea_pull_requests_by_work_package(represented.id),
title: "Gitea pull requests"
}
end
end
end
end
end
end
end
@@ -0,0 +1,13 @@
module OpenProject::GiteaIntegration
module Patches
module WorkPackagePatch
extend ActiveSupport::Concern
included do
has_and_belongs_to_many :gitea_pull_requests
end
end
end
end
WorkPackage.include OpenProject::GiteaIntegration::Patches::WorkPackagePatch
@@ -0,0 +1,31 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require_relative "services/params_helper"
require_relative "services/upsert_gitea_user"
require_relative "services/upsert_pull_request"
@@ -0,0 +1,50 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module OpenProject
module GiteaIntegration
module Services
module ParamsHelper
private
EMPTY_AVATAR_URL = "https://www.gravatar.com/avatar/?d=mp"
EMPTY_DESCRIPTION = "No description provided"
def avatar_url(raw_url)
raw_url.presence || EMPTY_AVATAR_URL
end
def description(raw_description)
raw_description.presence || EMPTY_DESCRIPTION
end
end
end
end
end
@@ -0,0 +1,60 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module OpenProject
module GiteaIntegration
module Services
##
# Takes user data from a Gitea webhook payload and stores it as a GiteaUser.
# If the GiteaUser already exists, it is updated.
class UpsertGiteaUser
include ParamsHelper
def call(payload)
GiteaUser.find_or_initialize_by(gitea_id: payload.id)
.tap do |gitea_user|
gitea_user.update!(extract_params(payload))
end
end
private
def extract_params(payload)
{
gitea_id: payload.id,
gitea_name: payload.full_name.presence || payload.login,
gitea_login: payload.login,
gitea_avatar_url: avatar_url(payload.avatar_url)
}
end
end
end
end
end
@@ -0,0 +1,93 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module OpenProject
module GiteaIntegration
module Services
class UpsertPullRequest
include ParamsHelper
def call(payload, work_packages: [])
find_or_initialize(payload).tap do |pr|
pr.update!(work_packages: pr.work_packages | work_packages, **extract_params(payload))
end
end
private
def find_or_initialize(payload)
GiteaPullRequest.find_by_gitea_identifiers(
id: payload.pull_request.number,
url: payload.pull_request.html_url,
initialize: true
)
end
# rubocop:disable Metrics/AbcSize
def extract_params(payload)
merged = payload.pull_request.merged?
{
gitea_id: payload.pull_request.number,
gitea_user: gitea_user_record(payload.pull_request.user),
number: payload.pull_request.number,
gitea_html_url: payload.pull_request.html_url,
gitea_updated_at: payload.pull_request.updated_at,
state: pr_state(payload),
title: payload.pull_request.title,
body: description(payload.pull_request.body),
repository: payload.repository.full_name,
draft: payload.pull_request.draft?,
merged:,
merged_by: merged ? gitea_user_record(payload.pull_request.merged_by?) : nil,
merged_at: merged ? payload.pull_request.merged_at? : nil,
labels: Array(payload.pull_request.labels?).map { |l| extract_label(l) }
}
end
# rubocop:enable Metrics/AbcSize
def pr_state(payload)
return "merged" if payload.pull_request.merged?
payload.pull_request.state
end
def extract_label(label)
{ "name" => label["name"], "color" => "##{label["color"]}" }
end
def gitea_user_record(payload)
return if payload.blank?
::OpenProject::GiteaIntegration::Services::UpsertGiteaUser.new.call(payload)
end
end
end
end
end
+29
View File
@@ -0,0 +1,29 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require "open_project/gitea_integration"
+45
View File
@@ -0,0 +1,45 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
Gem::Specification.new do |s|
s.name = "openproject-gitea_integration"
s.version = "1.0.0"
s.authors = "OpenProject GmbH"
s.email = "info@openproject.com"
s.homepage = "https://www.openproject.org/docs/system-admin-guide/integrations/gitea-integration/"
s.summary = "OpenProject Gitea Integration"
s.description = "Integrates OpenProject and Gitea for a better workflow"
s.license = "GPLv3"
s.files = Dir["{app,config,db,frontend,lib}/**/*"]
s.add_dependency "openproject-webhooks"
s.metadata["rubygems_mfa_required"] = "true"
end