From 5d409989df1dc13e4a6fd2a9a3fd43c6196eb634 Mon Sep 17 00:00:00 2001 From: Thomas Birke Date: Fri, 28 Aug 2026 22:33:27 +0200 Subject: [PATCH] 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 --- .gitignore | 5 + Gemfile | 14 ++ README.md | 189 ++++++++++++++++++ .../admin/page_header_component.html.erb | 31 +++ .../admin/page_header_component.rb | 42 ++++ .../admin/settings_controller.rb | 65 ++++++ .../gitea_integration/admin/settings_form.rb | 69 +++++++ app/models/gitea_pull_request.rb | 76 +++++++ app/models/gitea_user.rb | 39 ++++ .../admin/settings/show.html.erb | 41 ++++ .../cron/clear_old_gitea_pull_requests_job.rb | 39 ++++ config/locales/en.yml | 92 +++++++++ config/locales/js-en.yml | 55 +++++ config/routes.rb | 37 ++++ .../1016015_aggregated_gitea_migrations.rb | 44 ++++ db/migrate/tables/gitea_pull_requests.rb | 55 +++++ .../gitea_pull_requests_work_packages.rb | 42 ++++ db/migrate/tables/gitea_users.rb | 44 ++++ .../module/gitea-tab/gitea-tab.component.ts | 46 +++++ .../module/gitea-tab/gitea-tab.template.html | 1 + frontend/module/main.ts | 93 +++++++++ .../pull-request/pull-request.component.html | 50 +++++ .../pull-request/pull-request.component.ts | 66 ++++++ frontend/module/tab-prs/tab-prs.component.ts | 69 +++++++ frontend/module/tab-prs/tab-prs.template.html | 10 + frontend/module/typings.d.ts | 59 ++++++ ...tea_pull_request_collection_representer.rb | 37 ++++ .../gitea_pull_request_representer.rb | 98 +++++++++ ...gitea_pull_requests_by_work_package_api.rb | 52 +++++ .../gitea_user_representer.rb | 52 +++++ lib/open_project/gitea_integration.rb | 33 +++ lib/open_project/gitea_integration/engine.rb | 123 ++++++++++++ .../gitea_integration/hook_handler.rb | 87 ++++++++ .../notification_handler/helper.rb | 104 ++++++++++ .../notification_handler/issue_comment.rb | 69 +++++++ .../notification_handler/pull_request.rb | 94 +++++++++ .../notification_handlers.rb | 58 ++++++ .../patches/api/work_package_representer.rb | 60 ++++++ .../patches/work_package_patch.rb | 13 ++ .../gitea_integration/services.rb | 31 +++ .../services/params_helper.rb | 50 +++++ .../services/upsert_gitea_user.rb | 60 ++++++ .../services/upsert_pull_request.rb | 93 +++++++++ lib/openproject-gitea_integration.rb | 29 +++ openproject-gitea_integration.gemspec | 45 +++++ 45 files changed, 2561 insertions(+) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 README.md create mode 100644 app/components/gitea_integration/admin/page_header_component.html.erb create mode 100644 app/components/gitea_integration/admin/page_header_component.rb create mode 100644 app/controllers/gitea_integration/admin/settings_controller.rb create mode 100644 app/forms/gitea_integration/admin/settings_form.rb create mode 100644 app/models/gitea_pull_request.rb create mode 100644 app/models/gitea_user.rb create mode 100644 app/views/gitea_integration/admin/settings/show.html.erb create mode 100644 app/workers/cron/clear_old_gitea_pull_requests_job.rb create mode 100644 config/locales/en.yml create mode 100644 config/locales/js-en.yml create mode 100644 config/routes.rb create mode 100644 db/migrate/1016015_aggregated_gitea_migrations.rb create mode 100644 db/migrate/tables/gitea_pull_requests.rb create mode 100644 db/migrate/tables/gitea_pull_requests_work_packages.rb create mode 100644 db/migrate/tables/gitea_users.rb create mode 100644 frontend/module/gitea-tab/gitea-tab.component.ts create mode 100644 frontend/module/gitea-tab/gitea-tab.template.html create mode 100644 frontend/module/main.ts create mode 100644 frontend/module/pull-request/pull-request.component.html create mode 100644 frontend/module/pull-request/pull-request.component.ts create mode 100644 frontend/module/tab-prs/tab-prs.component.ts create mode 100644 frontend/module/tab-prs/tab-prs.template.html create mode 100644 frontend/module/typings.d.ts create mode 100644 lib/api/v3/gitea_pull_requests/gitea_pull_request_collection_representer.rb create mode 100644 lib/api/v3/gitea_pull_requests/gitea_pull_request_representer.rb create mode 100644 lib/api/v3/gitea_pull_requests/gitea_pull_requests_by_work_package_api.rb create mode 100644 lib/api/v3/gitea_pull_requests/gitea_user_representer.rb create mode 100644 lib/open_project/gitea_integration.rb create mode 100644 lib/open_project/gitea_integration/engine.rb create mode 100644 lib/open_project/gitea_integration/hook_handler.rb create mode 100644 lib/open_project/gitea_integration/notification_handler/helper.rb create mode 100644 lib/open_project/gitea_integration/notification_handler/issue_comment.rb create mode 100644 lib/open_project/gitea_integration/notification_handler/pull_request.rb create mode 100644 lib/open_project/gitea_integration/notification_handlers.rb create mode 100644 lib/open_project/gitea_integration/patches/api/work_package_representer.rb create mode 100644 lib/open_project/gitea_integration/patches/work_package_patch.rb create mode 100644 lib/open_project/gitea_integration/services.rb create mode 100644 lib/open_project/gitea_integration/services/params_helper.rb create mode 100644 lib/open_project/gitea_integration/services/upsert_gitea_user.rb create mode 100644 lib/open_project/gitea_integration/services/upsert_pull_request.rb create mode 100644 lib/openproject-gitea_integration.rb create mode 100644 openproject-gitea_integration.gemspec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a2b439 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +Gemfile.lock +*.gem +.bundle/ +vendor/bundle/ +node_modules/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..03888fc --- /dev/null +++ b/Gemfile @@ -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" diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae30f61 --- /dev/null +++ b/README.md @@ -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#`), 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=` | +| 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 `` 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= + │ + 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). diff --git a/app/components/gitea_integration/admin/page_header_component.html.erb b/app/components/gitea_integration/admin/page_header_component.html.erb new file mode 100644 index 0000000..4b29be3 --- /dev/null +++ b/app/components/gitea_integration/admin/page_header_component.html.erb @@ -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 %> diff --git a/app/components/gitea_integration/admin/page_header_component.rb b/app/components/gitea_integration/admin/page_header_component.rb new file mode 100644 index 0000000..8f3506e --- /dev/null +++ b/app/components/gitea_integration/admin/page_header_component.rb @@ -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 diff --git a/app/controllers/gitea_integration/admin/settings_controller.rb b/app/controllers/gitea_integration/admin/settings_controller.rb new file mode 100644 index 0000000..05bbb54 --- /dev/null +++ b/app/controllers/gitea_integration/admin/settings_controller.rb @@ -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 diff --git a/app/forms/gitea_integration/admin/settings_form.rb b/app/forms/gitea_integration/admin/settings_form.rb new file mode 100644 index 0000000..d7967ac --- /dev/null +++ b/app/forms/gitea_integration/admin/settings_form.rb @@ -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 diff --git a/app/models/gitea_pull_request.rb b/app/models/gitea_pull_request.rb new file mode 100644 index 0000000..7370476 --- /dev/null +++ b/app/models/gitea_pull_request.rb @@ -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 diff --git a/app/models/gitea_user.rb b/app/models/gitea_user.rb new file mode 100644 index 0000000..bcb4f75 --- /dev/null +++ b/app/models/gitea_user.rb @@ -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 diff --git a/app/views/gitea_integration/admin/settings/show.html.erb b/app/views/gitea_integration/admin/settings/show.html.erb new file mode 100644 index 0000000..892c780 --- /dev/null +++ b/app/views/gitea_integration/admin/settings/show.html.erb @@ -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 %> diff --git a/app/workers/cron/clear_old_gitea_pull_requests_job.rb b/app/workers/cron/clear_old_gitea_pull_requests_job.rb new file mode 100644 index 0000000..413135b --- /dev/null +++ b/app/workers/cron/clear_old_gitea_pull_requests_job.rb @@ -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 diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 0000000..8c16c90 --- /dev/null +++ b/config/locales/en.yml @@ -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. diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml new file mode 100644 index 0000000..99fa6de --- /dev/null +++ b/config/locales/js-en.yml @@ -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 OP#%{wp_id} in the PR title/description or create a new PR. + updated_on: Updated on + work_packages: + tab_name: "Gitea" + work_packages: + tabs: + gitea: "Gitea" diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000..538573d --- /dev/null +++ b/config/routes.rb @@ -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 diff --git a/db/migrate/1016015_aggregated_gitea_migrations.rb b/db/migrate/1016015_aggregated_gitea_migrations.rb new file mode 100644 index 0000000..ddd1ae1 --- /dev/null +++ b/db/migrate/1016015_aggregated_gitea_migrations.rb @@ -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 diff --git a/db/migrate/tables/gitea_pull_requests.rb b/db/migrate/tables/gitea_pull_requests.rb new file mode 100644 index 0000000..66e1313 --- /dev/null +++ b/db/migrate/tables/gitea_pull_requests.rb @@ -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 diff --git a/db/migrate/tables/gitea_pull_requests_work_packages.rb b/db/migrate/tables/gitea_pull_requests_work_packages.rb new file mode 100644 index 0000000..bd3090e --- /dev/null +++ b/db/migrate/tables/gitea_pull_requests_work_packages.rb @@ -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 diff --git a/db/migrate/tables/gitea_users.rb b/db/migrate/tables/gitea_users.rb new file mode 100644 index 0000000..2011845 --- /dev/null +++ b/db/migrate/tables/gitea_users.rb @@ -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 diff --git a/frontend/module/gitea-tab/gitea-tab.component.ts b/frontend/module/gitea-tab/gitea-tab.component.ts new file mode 100644 index 0000000..f2d3148 --- /dev/null +++ b/frontend/module/gitea-tab/gitea-tab.component.ts @@ -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; +} diff --git a/frontend/module/gitea-tab/gitea-tab.template.html b/frontend/module/gitea-tab/gitea-tab.template.html new file mode 100644 index 0000000..e5734c6 --- /dev/null +++ b/frontend/module/gitea-tab/gitea-tab.template.html @@ -0,0 +1 @@ + diff --git a/frontend/module/main.ts b/frontend/module/main.ts new file mode 100644 index 0000000..bb2e93e --- /dev/null +++ b/frontend/module/main.ts @@ -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 { + const halResourceService = injector.get(HalResourceService); + const apiV3Service = injector.get(ApiV3Service); + const basePath = apiV3Service.work_packages.id(workPackage.id as string).path; + + return halResourceService + .get(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); + } +} diff --git a/frontend/module/pull-request/pull-request.component.html b/frontend/module/pull-request/pull-request.component.html new file mode 100644 index 0000000..1981e0d --- /dev/null +++ b/frontend/module/pull-request/pull-request.component.html @@ -0,0 +1,50 @@ +
+ + + {{ state }} | + + + +@if (pullRequest.labels && pullRequest.labels.length > 0) { + +} + +
+ {{ text.label_created_by }} + + @if (pullRequest.giteaUser) { + + } + + + {{ text.label_last_updated_on }} + + . +
+ + diff --git a/frontend/module/pull-request/pull-request.component.ts b/frontend/module/pull-request/pull-request.component.ts new file mode 100644 index 0000000..d428454 --- /dev/null +++ b/frontend/module/pull-request/pull-request.component.ts @@ -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'; + } + } +} diff --git a/frontend/module/tab-prs/tab-prs.component.ts b/frontend/module/tab-prs/tab-prs.component.ts new file mode 100644 index 0000000..816be7c --- /dev/null +++ b/frontend/module/tab-prs/tab-prs.component.ts @@ -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>(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 }); + } +} diff --git a/frontend/module/tab-prs/tab-prs.template.html b/frontend/module/tab-prs/tab-prs.template.html new file mode 100644 index 0000000..9d49e5d --- /dev/null +++ b/frontend/module/tab-prs/tab-prs.template.html @@ -0,0 +1,10 @@ +@if (pullRequests.length === 0) { +

+} + +@for (pullRequest of pullRequests; track pullRequest.id || pullRequest.htmlUrl) { + +} diff --git a/frontend/module/typings.d.ts b/frontend/module/typings.d.ts new file mode 100644 index 0000000..f32ed79 --- /dev/null +++ b/frontend/module/typings.d.ts @@ -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; +} diff --git a/lib/api/v3/gitea_pull_requests/gitea_pull_request_collection_representer.rb b/lib/api/v3/gitea_pull_requests/gitea_pull_request_collection_representer.rb new file mode 100644 index 0000000..9ed1aff --- /dev/null +++ b/lib/api/v3/gitea_pull_requests/gitea_pull_request_collection_representer.rb @@ -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 diff --git a/lib/api/v3/gitea_pull_requests/gitea_pull_request_representer.rb b/lib/api/v3/gitea_pull_requests/gitea_pull_request_representer.rb new file mode 100644 index 0000000..f8987e2 --- /dev/null +++ b/lib/api/v3/gitea_pull_requests/gitea_pull_request_representer.rb @@ -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 diff --git a/lib/api/v3/gitea_pull_requests/gitea_pull_requests_by_work_package_api.rb b/lib/api/v3/gitea_pull_requests/gitea_pull_requests_by_work_package_api.rb new file mode 100644 index 0000000..2994bf0 --- /dev/null +++ b/lib/api/v3/gitea_pull_requests/gitea_pull_requests_by_work_package_api.rb @@ -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 diff --git a/lib/api/v3/gitea_pull_requests/gitea_user_representer.rb b/lib/api/v3/gitea_pull_requests/gitea_user_representer.rb new file mode 100644 index 0000000..d448078 --- /dev/null +++ b/lib/api/v3/gitea_pull_requests/gitea_user_representer.rb @@ -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 diff --git a/lib/open_project/gitea_integration.rb b/lib/open_project/gitea_integration.rb new file mode 100644 index 0000000..d7dbb3f --- /dev/null +++ b/lib/open_project/gitea_integration.rb @@ -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 diff --git a/lib/open_project/gitea_integration/engine.rb b/lib/open_project/gitea_integration/engine.rb new file mode 100644 index 0000000..90b75b8 --- /dev/null +++ b/lib/open_project/gitea_integration/engine.rb @@ -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 diff --git a/lib/open_project/gitea_integration/hook_handler.rb b/lib/open_project/gitea_integration/hook_handler.rb new file mode 100644 index 0000000..3c52e88 --- /dev/null +++ b/lib/open_project/gitea_integration/hook_handler.rb @@ -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 diff --git a/lib/open_project/gitea_integration/notification_handler/helper.rb b/lib/open_project/gitea_integration/notification_handler/helper.rb new file mode 100644 index 0000000..61f9e7f --- /dev/null +++ b/lib/open_project/gitea_integration/notification_handler/helper.rb @@ -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 diff --git a/lib/open_project/gitea_integration/notification_handler/issue_comment.rb b/lib/open_project/gitea_integration/notification_handler/issue_comment.rb new file mode 100644 index 0000000..e078430 --- /dev/null +++ b/lib/open_project/gitea_integration/notification_handler/issue_comment.rb @@ -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 diff --git a/lib/open_project/gitea_integration/notification_handler/pull_request.rb b/lib/open_project/gitea_integration/notification_handler/pull_request.rb new file mode 100644 index 0000000..264a2c2 --- /dev/null +++ b/lib/open_project/gitea_integration/notification_handler/pull_request.rb @@ -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 diff --git a/lib/open_project/gitea_integration/notification_handlers.rb b/lib/open_project/gitea_integration/notification_handlers.rb new file mode 100644 index 0000000..b3ff365 --- /dev/null +++ b/lib/open_project/gitea_integration/notification_handlers.rb @@ -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 diff --git a/lib/open_project/gitea_integration/patches/api/work_package_representer.rb b/lib/open_project/gitea_integration/patches/api/work_package_representer.rb new file mode 100644 index 0000000..438a379 --- /dev/null +++ b/lib/open_project/gitea_integration/patches/api/work_package_representer.rb @@ -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 diff --git a/lib/open_project/gitea_integration/patches/work_package_patch.rb b/lib/open_project/gitea_integration/patches/work_package_patch.rb new file mode 100644 index 0000000..fffc3a7 --- /dev/null +++ b/lib/open_project/gitea_integration/patches/work_package_patch.rb @@ -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 diff --git a/lib/open_project/gitea_integration/services.rb b/lib/open_project/gitea_integration/services.rb new file mode 100644 index 0000000..f21d6b2 --- /dev/null +++ b/lib/open_project/gitea_integration/services.rb @@ -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" diff --git a/lib/open_project/gitea_integration/services/params_helper.rb b/lib/open_project/gitea_integration/services/params_helper.rb new file mode 100644 index 0000000..8fa033f --- /dev/null +++ b/lib/open_project/gitea_integration/services/params_helper.rb @@ -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 diff --git a/lib/open_project/gitea_integration/services/upsert_gitea_user.rb b/lib/open_project/gitea_integration/services/upsert_gitea_user.rb new file mode 100644 index 0000000..d933a42 --- /dev/null +++ b/lib/open_project/gitea_integration/services/upsert_gitea_user.rb @@ -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 diff --git a/lib/open_project/gitea_integration/services/upsert_pull_request.rb b/lib/open_project/gitea_integration/services/upsert_pull_request.rb new file mode 100644 index 0000000..5ae31c1 --- /dev/null +++ b/lib/open_project/gitea_integration/services/upsert_pull_request.rb @@ -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 diff --git a/lib/openproject-gitea_integration.rb b/lib/openproject-gitea_integration.rb new file mode 100644 index 0000000..c576f1c --- /dev/null +++ b/lib/openproject-gitea_integration.rb @@ -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" diff --git a/openproject-gitea_integration.gemspec b/openproject-gitea_integration.gemspec new file mode 100644 index 0000000..e7f52e6 --- /dev/null +++ b/openproject-gitea_integration.gemspec @@ -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