├── ssh_keys
└── .placeholder
├── app
├── views
│ ├── gitolite_public_keys
│ │ ├── index.html.slim
│ │ ├── _view.html.slim
│ │ ├── _form.html.slim
│ │ └── _ssh_keys.html.slim
│ ├── go_redirector
│ │ └── index.html.slim
│ ├── settings
│ │ ├── authors.html.slim
│ │ ├── install_gitolite_hooks.js.erb
│ │ ├── redmine_git_hosting
│ │ │ ├── _sidekiq_interface.html.slim
│ │ │ ├── _gitolite_rescue.html.slim
│ │ │ ├── _install_hooks_result.html.slim
│ │ │ ├── _gitolite_recycle_bin.html.slim
│ │ │ ├── _gitolite_config_storage.html.slim
│ │ │ └── _gitolite_config_cache.html.slim
│ │ └── _redmine_git_hosting.html.slim
│ ├── repositories
│ │ ├── _git_hosting_navigation.html.slim
│ │ ├── statistics
│ │ │ └── _global.html.slim
│ │ ├── _download_revision.html.slim
│ │ ├── _readme.html.slim
│ │ ├── _edit_top.html.slim
│ │ ├── _show_top.html.slim
│ │ ├── _git_hosting_sidebar.html.slim
│ │ └── edit.html.slim
│ ├── repository_mirrors
│ │ ├── edit.js.erb
│ │ ├── new.js.erb
│ │ ├── push.js.erb
│ │ ├── _push_modal.html.slim
│ │ ├── show.api.rsb
│ │ ├── _new_modal.html.slim
│ │ ├── _edit_modal.html.slim
│ │ ├── index.api.rsb
│ │ └── _form.html.slim
│ ├── common
│ │ ├── _git_hosting_html_head.html.slim
│ │ ├── _git_hosting_js_headers.html.slim
│ │ └── _git_urls.html.slim
│ ├── hooks
│ │ └── _show_repositories_sidebar.html.slim
│ ├── repository_git_config_keys
│ │ ├── edit.js.erb
│ │ ├── new.js.erb
│ │ ├── show.api.rsb
│ │ ├── index.api.rsb
│ │ ├── _form.html.slim
│ │ ├── _new_modal.html.slim
│ │ ├── _edit_modal.html.slim
│ │ ├── _config_keys.html.slim
│ │ └── index.html.slim
│ ├── repository_git_extras
│ │ ├── sort_urls.js.erb
│ │ ├── update.js.erb
│ │ ├── _sort_urls_modal.html.slim
│ │ └── move.html.slim
│ ├── repository_post_receive_urls
│ │ ├── edit.js.erb
│ │ ├── new.js.erb
│ │ ├── show.api.rsb
│ │ ├── _new_modal.html.slim
│ │ ├── _edit_modal.html.slim
│ │ ├── index.api.rsb
│ │ └── _form.html.slim
│ ├── repository_protected_branches
│ │ ├── new.js.erb
│ │ ├── edit.js.erb
│ │ ├── show.api.rsb
│ │ ├── index.api.rsb
│ │ ├── _new_modal.html.slim
│ │ ├── _edit_modal.html.slim
│ │ └── _form.html.slim
│ ├── repository_deployment_credentials
│ │ ├── edit.js.erb
│ │ ├── new.js.erb
│ │ ├── _edit_modal.html.slim
│ │ └── _new_modal.html.slim
│ ├── dashboards
│ │ └── blocks
│ │ │ └── _git_urls.html.slim
│ └── users
│ │ └── index.api.rsb
├── models
│ ├── repository_git_config_key
│ │ ├── option.rb
│ │ └── git_config.rb
│ ├── github_issue.rb
│ ├── github_comment.rb
│ ├── concerns
│ │ ├── gitolitable.rb
│ │ └── gitolitable
│ │ │ └── notifications.rb
│ ├── git_cache.rb
│ ├── protected_branches_member.rb
│ ├── repository_git_config_key.rb
│ └── repository_protected_branche.rb
├── reports
│ ├── report_base.rb
│ ├── report_helper.rb
│ ├── report_query.rb
│ └── repository_global_stats.rb
├── helpers
│ ├── repository_git_config_keys_helper.rb
│ ├── repository_post_receive_urls_helper.rb
│ ├── gitolite_public_keys_helper.rb
│ ├── repository_deployment_credentials_helper.rb
│ ├── git_hosting_users_helper.rb
│ ├── git_hosting_helper.rb
│ └── repository_mirrors_helper.rb
├── services
│ ├── redmine_hooks
│ │ ├── http_helper.rb
│ │ ├── fetch_changesets.rb
│ │ └── base.rb
│ └── permissions_builder
│ │ ├── protected_branches.rb
│ │ └── base.rb
├── controllers
│ ├── go_redirector_controller.rb
│ ├── archived_repositories_controller.rb
│ ├── concerns
│ │ └── xitolite_repository_finder.rb
│ ├── download_git_revision_controller.rb
│ └── repository_git_extras_controller.rb
├── use_cases
│ ├── repository_mirrors
│ │ ├── base.rb
│ │ └── push.rb
│ ├── projects
│ │ ├── base.rb
│ │ ├── execute_hooks.rb
│ │ ├── update.rb
│ │ └── create_repository.rb
│ └── repositories
│ │ └── base.rb
├── overrides
│ └── repositories
│ │ ├── navigation.rb
│ │ └── show.rb
├── forms
│ ├── base_form.rb
│ ├── plugin_settings_validation
│ │ ├── cache_config.rb
│ │ ├── storage_config.rb
│ │ ├── hooks_config.rb
│ │ ├── ssh_config.rb
│ │ └── mailing_list_config.rb
│ └── move_repository_form.rb
└── workers
│ └── githosting_shell_worker.rb
├── lib
├── hrack
│ ├── version.rb
│ └── bundle.rb
└── redmine_git_hosting
│ ├── commands.rb
│ ├── auth.rb
│ ├── config.rb
│ ├── git_access_status.rb
│ ├── plugin_author.rb
│ ├── gitolite_wrappers
│ ├── global
│ │ ├── purge_recycle_bin.rb
│ │ ├── delete_from_recycle_bin.rb
│ │ └── common.rb
│ ├── users
│ │ ├── resync_ssh_keys.rb
│ │ ├── regenerate_ssh_keys.rb
│ │ ├── delete_ssh_key.rb
│ │ └── add_ssh_key.rb
│ ├── repositories
│ │ ├── move_repository.rb
│ │ ├── delete_repository.rb
│ │ └── update_repository.rb
│ └── projects
│ │ ├── move_repositories.rb
│ │ ├── move_repositories_tree.rb
│ │ └── common.rb
│ ├── plugins.rb
│ ├── utils
│ ├── crypto.rb
│ ├── git.rb
│ └── ssh.rb
│ ├── patches
│ ├── issue_patch.rb
│ ├── journal_patch.rb
│ ├── repository_patch.rb
│ ├── repositories_helper_patch.rb
│ ├── sys_controller_patch.rb
│ ├── member_patch.rb
│ ├── setting_patch.rb
│ ├── dashboard_content_project_patch.rb
│ ├── grack_server_patch.rb
│ ├── group_patch.rb
│ ├── roles_controller_patch.rb
│ └── watchers_controller_patch.rb
│ ├── file_logger.rb
│ ├── gitolite_handlers
│ ├── ssh_keys
│ │ ├── delete_ssh_key.rb
│ │ ├── add_ssh_key.rb
│ │ └── base.rb
│ └── repositories
│ │ ├── delete_repository.rb
│ │ ├── update_repository.rb
│ │ └── add_repository.rb
│ ├── markdown_renderer.rb
│ ├── config
│ ├── gitolite_cache.rb
│ ├── gitolite_storage.rb
│ ├── gitolite_notifications.rb
│ ├── mirroring.rb
│ └── redmine_config.rb
│ ├── gitolite_params
│ └── base_param.rb
│ ├── commands
│ └── base.rb
│ ├── plugins
│ ├── sweepers
│ │ ├── base_sweeper.rb
│ │ └── repository_deletor.rb
│ ├── extenders
│ │ ├── base_extender.rb
│ │ ├── config_key_deletor.rb
│ │ ├── branch_updater.rb
│ │ └── git_annex_creator.rb
│ └── gitolite_plugin.rb
│ ├── recycle_bin
│ ├── item.rb
│ ├── item_base.rb
│ └── deletable_item.rb
│ ├── recycle_bin.rb
│ ├── console_logger.rb
│ ├── error.rb
│ ├── journal_logger.rb
│ ├── gitolite_hooks.rb
│ └── redcarpet_filter.rb
├── package.json
├── config
└── sidekiq.yml
├── spec
├── factories
│ ├── group.rb
│ ├── repository_svn.rb
│ ├── project.rb
│ ├── repository_gitolite.rb
│ ├── repository_post_receive_url.rb
│ ├── repository_protected_branche.rb
│ ├── repository_deployment_credential.rb
│ ├── repository_mirror.rb
│ ├── repository_git_extra.rb
│ ├── user.rb
│ ├── protected_branches_member.rb
│ ├── gitolite_public_key.rb
│ ├── repository_git_config_key.rb
│ └── role.rb
├── models
│ ├── group_spec.rb
│ ├── repository_git_config_key_spec.rb
│ ├── setting_spec.rb
│ ├── user_spec.rb
│ ├── repository_git_config_key
│ │ ├── option_spec.rb
│ │ └── git_config_spec.rb
│ └── project_spec.rb
├── spec_helper.rb
├── lib
│ └── redmine_git_hosting
│ │ ├── config_spec.rb
│ │ └── utils
│ │ └── git_spec.rb
├── controllers
│ └── users_controller_spec.rb
└── root_spec_helper.rb
├── contrib
├── github
│ ├── database-mysql.yml
│ └── database-postgres.yml
├── scripts
│ ├── puma.rb
│ └── redmine
└── hooks
│ └── post-receive
│ ├── lib
│ └── git_hosting_hook_logger.rb
│ ├── redmine_gitolite.rb
│ └── mail_notifications.py
├── db
└── migrate
│ ├── 20111119170948_add_indexes_to_gitolite_public_key.rb
│ ├── 20150703050000_add_type_field_to_git_config_keys.rb
│ ├── 20150604051500_add_unique_index_to_fingerprint.rb
│ ├── 20120522000000_add_post_receive_url_modes.rb
│ ├── 20130909195828_rename_table_git_repository_extras.rb
│ ├── 20150410031920_add_urls_order_to_repository_git_extra.rb
│ ├── 20140618224954_add_fingerprint_to_gitolite_public_keys.rb
│ ├── 20141228193500_add_git_annex_to_git_extras.rb
│ ├── 20140523013000_add_split_payloads_to_post_receive.rb
│ ├── 20150125234500_add_public_repo_to_repository_git_extra.rb
│ ├── 20140621004300_add_protected_branch_to_repository_git_extra.rb
│ ├── 20140516224900_add_trigger_to_post_receive.rb
│ ├── 20140327015700_create_github_issues.rb
│ ├── 20140327015701_create_github_comments.rb
│ ├── 20130910195931_add_columns_to_repository_git_notification.rb
│ ├── 20091119162428_create_git_caches.rb
│ ├── 20140306002300_create_repository_git_config_keys.rb
│ ├── 20140624150200_remove_gitolite_public_keys_active_column.rb
│ ├── 20110807000000_create_repository_mirrors.rb
│ ├── 20130909195727_create_repository_git_notifications.rb
│ ├── 20120521000000_create_repository_post_receive_urls.rb
│ ├── 20140305053200_remove_notify_cia.rb
│ ├── 20091119162427_create_gitolite_public_keys.rb
│ ├── 20130910195930_add_columns_to_repository_git_extra.rb
│ ├── 20120710204007_add_repository_mirror_fields.rb
│ ├── 20130909195929_rename_table_deployment_credentials.rb
│ ├── 20150705180400_rename_git_config_keys_index.rb
│ ├── 20140621004200_create_repository_protected_branches.rb
│ ├── 20110726000000_extend_changesets_notified_cia.rb
│ ├── 20150823030000_create_protected_branches_members.rb
│ ├── 20140305083200_add_default_branch_to_repository_git_extra.rb
│ ├── 20140417004100_enforce_models_constraints.rb
│ ├── 20151127024000_shrink_git_notifications.rb
│ ├── 20150823030100_migrate_protected_branches_users.rb
│ ├── 20120708070841_add_settings_to_plugin_4.rb
│ ├── 20110817000000_move_notified_cia_to_git_cia_notifications.rb
│ ├── 20110813000000_create_git_repository_extras.rb
│ ├── 20120724211806_add_settings_to_plugin_5.rb
│ └── 20111123214911_add_settings_to_plugin.rb
├── .gitignore
├── .github
└── workflows
│ ├── stylelint.yml
│ ├── brakeman.yml
│ └── rubocop.yml
├── assets
├── images
│ ├── button.svg
│ ├── button_selected.svg
│ └── button_focus.svg
├── stylesheets
│ └── plugin.css
└── javascripts
│ ├── plugin.js
│ └── git_urls.js
├── LICENSE
├── .slim-lint.yml
└── custom_hooks.rb.example
/ssh_keys/.placeholder:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/views/gitolite_public_keys/index.html.slim:
--------------------------------------------------------------------------------
1 | = render partial: 'gitolite_public_keys/view'
2 |
--------------------------------------------------------------------------------
/lib/hrack/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Hrack
4 | module Version
5 | end
6 | VERSION = '1.0.0'
7 | end
8 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/commands.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Commands
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {},
3 | "devDependencies": {
4 | "eslint": "^7.0.0",
5 | "stylelint": "^14.0.0"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/config/sidekiq.yml:
--------------------------------------------------------------------------------
1 | ---
2 | :pidfile: tmp/pids/sidekiq_redmine_git_hosting.pid
3 | :concurrency: 1
4 | :timeout: 15
5 | :queues:
6 | - redmine_git_hosting
7 |
--------------------------------------------------------------------------------
/app/views/go_redirector/index.html.slim:
--------------------------------------------------------------------------------
1 | - content_for :header_tags do
2 | meta name="go-import" content="#{@repository.go_url} git #{@repository.go_access_url}"
3 |
--------------------------------------------------------------------------------
/app/views/settings/authors.html.slim:
--------------------------------------------------------------------------------
1 | div style='height: 50%;'
2 | ul.authors-list
3 | - @authors.each do |author|
4 | li = mail_to author.email, author.name
5 |
--------------------------------------------------------------------------------
/app/views/repositories/_git_hosting_navigation.html.slim:
--------------------------------------------------------------------------------
1 | - if User.current.allowed_to? :add_repository_xitolite_watchers, @project
2 | = watcher_link @repository, User.current
3 |
--------------------------------------------------------------------------------
/app/views/repository_mirrors/edit.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html('<%= escape_javascript(render partial: 'repository_mirrors/edit_modal') %>');
2 | showModal('ajax-modal', '600px');
3 |
--------------------------------------------------------------------------------
/app/views/repository_mirrors/new.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html('<%= escape_javascript(render partial: 'repository_mirrors/new_modal') %>');
2 | showModal('ajax-modal', '600px');
3 |
--------------------------------------------------------------------------------
/app/views/repository_mirrors/push.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html('<%= escape_javascript(render partial: 'repository_mirrors/push_modal') %>');
2 | showModal('ajax-modal', '600px');
3 |
--------------------------------------------------------------------------------
/spec/factories/group.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :group do
5 | sequence(:lastname) { |n| "GroupTest#{n}" }
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/views/common/_git_hosting_html_head.html.slim:
--------------------------------------------------------------------------------
1 | = stylesheet_link_tag 'plugin', plugin: 'redmine_git_hosting'
2 | = javascript_include_tag 'plugin', plugin: 'redmine_git_hosting'
3 |
--------------------------------------------------------------------------------
/app/views/hooks/_show_repositories_sidebar.html.slim:
--------------------------------------------------------------------------------
1 | - if @repositories.size > 1
2 | - content_for :sidebar do
3 | = call_hook :view_repositories_show_sidebar, repository: @repository
4 |
--------------------------------------------------------------------------------
/app/views/repository_git_config_keys/edit.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html('<%= escape_javascript(render partial: 'repository_git_config_keys/edit_modal') %>');
2 | showModal('ajax-modal', '600px');
3 |
--------------------------------------------------------------------------------
/app/views/repository_git_config_keys/new.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html('<%= escape_javascript(render partial: 'repository_git_config_keys/new_modal') %>');
2 | showModal('ajax-modal', '600px');
3 |
--------------------------------------------------------------------------------
/app/views/repository_git_config_keys/show.api.rsb:
--------------------------------------------------------------------------------
1 | api.git_config_key do
2 | api.id @git_config_key.id
3 | api.key @git_config_key.key
4 | api.value @git_config_key.value
5 | end
6 |
--------------------------------------------------------------------------------
/app/views/repository_git_extras/sort_urls.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html('<%= escape_javascript(render partial: 'repository_git_extras/sort_urls_modal') %>');
2 | showModal('ajax-modal', '400px');
3 |
--------------------------------------------------------------------------------
/app/views/repository_post_receive_urls/edit.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html('<%= escape_javascript(render partial: 'repository_post_receive_urls/edit_modal') %>');
2 | showModal('ajax-modal', '600px');
3 |
--------------------------------------------------------------------------------
/app/views/repository_post_receive_urls/new.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html('<%= escape_javascript(render partial: 'repository_post_receive_urls/new_modal') %>');
2 | showModal('ajax-modal', '600px');
3 |
--------------------------------------------------------------------------------
/app/views/repository_protected_branches/new.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html('<%= escape_javascript(render partial: 'repository_protected_branches/new_modal') %>');
2 | showModal('ajax-modal', '600px');
3 |
--------------------------------------------------------------------------------
/app/views/repository_protected_branches/edit.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html('<%= escape_javascript(render partial: 'repository_protected_branches/edit_modal') %>');
2 | showModal('ajax-modal', '600px');
3 |
--------------------------------------------------------------------------------
/app/views/repository_deployment_credentials/edit.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html('<%= escape_javascript(render partial: 'repository_deployment_credentials/edit_modal') %>');
2 | showModal('ajax-modal', '600px');
3 |
--------------------------------------------------------------------------------
/app/views/repository_deployment_credentials/new.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html('<%= escape_javascript(render partial: 'repository_deployment_credentials/new_modal') %>');
2 | showModal('ajax-modal', '600px');
3 |
--------------------------------------------------------------------------------
/app/views/settings/install_gitolite_hooks.js.erb:
--------------------------------------------------------------------------------
1 | $('#ajax-modal').html("<%= escape_javascript(render partial: 'settings/redmine_git_hosting/install_hooks_result') %>");
2 | showModal('ajax-modal', '600px');
3 |
--------------------------------------------------------------------------------
/spec/factories/repository_svn.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :repository_svn, class: 'Repository::Subversion' do
5 | is_default { false }
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/contrib/github/database-mysql.yml:
--------------------------------------------------------------------------------
1 | test:
2 | adapter: mysql2
3 | database: redmine
4 | port: <%= ENV["MYSQL_PORT"] %>
5 | host: 127.0.0.1
6 | username: root
7 | password: BestPasswordEver
8 | encoding: utf8mb4
9 |
--------------------------------------------------------------------------------
/app/views/common/_git_hosting_js_headers.html.slim:
--------------------------------------------------------------------------------
1 | - content_for :header_tags do
2 | = stylesheet_link_tag 'application', plugin: 'redmine_git_hosting'
3 | = javascript_include_tag 'application', plugin: 'redmine_git_hosting'
4 |
--------------------------------------------------------------------------------
/spec/factories/project.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :project do
5 | sequence(:identifier) { |n| "project#{n}" }
6 | sequence(:name) { |n| "Project#{n}" }
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/spec/factories/repository_gitolite.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :repository_gitolite, class: 'Repository::Xitolite' do
5 | is_default { false }
6 | association :project
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/views/repositories/statistics/_global.html.slim:
--------------------------------------------------------------------------------
1 | - report = RepositoryGlobalStats.new(repository).build
2 |
3 | ul.thumbnails
4 | - report.each do |key, value|
5 | li.span4
6 | .thumbnail
7 | .title = key
8 | .value = value
9 |
--------------------------------------------------------------------------------
/db/migrate/20111119170948_add_indexes_to_gitolite_public_key.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddIndexesToGitolitePublicKey < ActiveRecord::Migration[4.2]
4 | def change
5 | add_index :gitolite_public_keys, :identifier
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20150703050000_add_type_field_to_git_config_keys.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddTypeFieldToGitConfigKeys < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :repository_git_config_keys, :type, :string
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .*.swp
2 | Gemfile.lock
3 | settings.yml
4 | /tmp
5 | /rdoc
6 | /coverage
7 | /junit
8 | custom_hooks.rb
9 | /ssh_keys/redmine_gitolite_admin_id_rsa*
10 | .DS_Store
11 | .vscode/
12 | node_modules/
13 | yarn.lock
14 | coverage/
15 | .enable_dev
16 |
--------------------------------------------------------------------------------
/db/migrate/20150604051500_add_unique_index_to_fingerprint.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddUniqueIndexToFingerprint < ActiveRecord::Migration[4.2]
4 | def change
5 | add_index :gitolite_public_keys, :fingerprint, unique: true
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20120522000000_add_post_receive_url_modes.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddPostReceiveUrlModes < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :repository_post_receive_urls, :mode, :string, default: 'github'
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20130909195828_rename_table_git_repository_extras.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RenameTableGitRepositoryExtras < ActiveRecord::Migration[4.2]
4 | def change
5 | rename_table :git_repository_extras, :repository_git_extras
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20150410031920_add_urls_order_to_repository_git_extra.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddUrlsOrderToRepositoryGitExtra < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :repository_git_extras, :urls_order, :text
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/views/repository_mirrors/_push_modal.html.slim:
--------------------------------------------------------------------------------
1 | p style='white-space: nowrap;'
2 | = render_push_state @mirror, @push_failed
3 |
4 | strong
5 | = l :label_mirror_push_output
6 | ' :
7 |
8 | pre class="mirror-push-#{'error' if @push_failed}"
9 | = @shellout
10 |
--------------------------------------------------------------------------------
/app/models/repository_git_config_key/option.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RepositoryGitConfigKey::Option < RepositoryGitConfigKey
4 | validates :key, presence: true,
5 | uniqueness: { case_sensitive: false, scope: %i[type repository_id] }
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20140618224954_add_fingerprint_to_gitolite_public_keys.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddFingerprintToGitolitePublicKeys < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :gitolite_public_keys, :fingerprint, :string, after: 'key'
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20141228193500_add_git_annex_to_git_extras.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddGitAnnexToGitExtras < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :repository_git_extras, :git_annex, :boolean, default: false, after: :git_notify
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20140523013000_add_split_payloads_to_post_receive.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddSplitPayloadsToPostReceive < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :repository_post_receive_urls, :split_payloads, :boolean, default: false
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/reports/report_base.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ReportBase
4 | include Redmine::I18n
5 | include ReportHelper
6 | include ReportQuery
7 |
8 | attr_reader :repository
9 |
10 | def initialize(repository)
11 | @repository = repository
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/factories/repository_post_receive_url.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :repository_post_receive_url do
5 | sequence(:url) { |n| "http://example.com/toto#{n}.php" }
6 | association :repository, factory: :repository_gitolite
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/spec/factories/repository_protected_branche.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :repository_protected_branche do
5 | path { 'master' }
6 | permissions { 'RW+' }
7 | association :repository, factory: :repository_gitolite
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/models/github_issue.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class GithubIssue < ActiveRecord::Base
4 | ## Relations
5 | belongs_to :issue
6 |
7 | ## Validations
8 | validates :github_id, presence: true
9 | validates :issue_id, presence: true, uniqueness: { scope: :github_id }
10 | end
11 |
--------------------------------------------------------------------------------
/contrib/scripts/puma.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | stdout_redirect '/home/redmine/redmine/log/puma.stderr.log', '/home/redmine/redmine/log/puma.stdout.log'
4 |
5 | on_worker_boot do
6 | ActiveSupport.on_load :active_record do
7 | ActiveRecord::Base.establish_connection
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/models/github_comment.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class GithubComment < ActiveRecord::Base
4 | ## Relations
5 | belongs_to :journal
6 |
7 | ## Validations
8 | validates :github_id, presence: true
9 | validates :journal_id, presence: true, uniqueness: { scope: :github_id }
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20150125234500_add_public_repo_to_repository_git_extra.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddPublicRepoToRepositoryGitExtra < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :repository_git_extras, :public_repo, :boolean, default: false, after: :protected_branch
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/views/repository_protected_branches/show.api.rsb:
--------------------------------------------------------------------------------
1 | api.protected_branch do
2 | api.id @protected_branch.id
3 | api.path @protected_branch.path
4 | api.permissions @protected_branch.permissions
5 | api.user_list @protected_branch.user_list
6 | api.position @protected_branch.position
7 | end
8 |
--------------------------------------------------------------------------------
/app/views/settings/redmine_git_hosting/_sidekiq_interface.html.slim:
--------------------------------------------------------------------------------
1 | / Gitolite Sidekiq Config
2 | p
3 | = additionals_settings_checkbox :gitolite_use_sidekiq,
4 | value: RedmineGitHosting::Config.get_setting(:gitolite_use_sidekiq, true),
5 | value_is_bool: true
6 |
--------------------------------------------------------------------------------
/db/migrate/20140621004300_add_protected_branch_to_repository_git_extra.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddProtectedBranchToRepositoryGitExtra < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :repository_git_extras, :protected_branch, :boolean, default: false, after: :default_branch
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/factories/repository_deployment_credential.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :repository_deployment_credential do
5 | perm { 'RW+' }
6 | association :repository, factory: :repository_gitolite
7 | association :user
8 | association :gitolite_public_key
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/factories/repository_mirror.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :repository_mirror do
5 | sequence(:url) { |n| "ssh://git@example.com:22/john_doe/john_doe/john_doe_#{n}.git" }
6 | push_mode { 0 }
7 | association :repository, factory: :repository_gitolite
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20140516224900_add_trigger_to_post_receive.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddTriggerToPostReceive < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :repository_post_receive_urls, :use_triggers, :boolean, default: false
6 | add_column :repository_post_receive_urls, :triggers, :text
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20140327015700_create_github_issues.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateGithubIssues < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :github_issues do |t|
6 | t.column :github_id, :integer, null: false
7 | t.column :issue_id, :integer, null: false
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20140327015701_create_github_comments.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateGithubComments < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :github_comments do |t|
6 | t.column :github_id, :integer, null: false
7 | t.column :journal_id, :integer, null: false
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/factories/repository_git_extra.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :repository_git_extra do
5 | git_http { 0 }
6 | default_branch { 'master' }
7 | association :repository, factory: :repository_gitolite
8 | key { RedmineGitHosting::Utils::Crypto.generate_secret 64 }
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/helpers/repository_git_config_keys_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RepositoryGitConfigKeysHelper
4 | def git_config_key_options
5 | [
6 | [l(:label_git_key_type_config), 'RepositoryGitConfigKey::GitConfig'],
7 | [l(:label_git_key_type_option), 'RepositoryGitConfigKey::Option']
8 | ]
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20130910195931_add_columns_to_repository_git_notification.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddColumnsToRepositoryGitNotification < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :repository_git_notifications, :prefix, :string
6 | add_column :repository_git_notifications, :sender_address, :string
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/lib/hrack/bundle.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rack/builder'
4 | require 'rack/parser'
5 |
6 | module Hrack
7 | module Bundle
8 | module_function
9 |
10 | def new(config)
11 | Rack::Builder.new do
12 | use Rack::Parser
13 | run Hrack::Server.new(config)
14 | end
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/services/redmine_hooks/http_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineHooks
4 | module HttpHelper
5 | def http_post(url, **opts)
6 | RedmineGitHosting::Utils::Http.http_post url, **opts
7 | end
8 |
9 | def http_get(url, **opts)
10 | RedmineGitHosting::Utils::Http.http_get url, **opts
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/views/repository_git_extras/update.js.erb:
--------------------------------------------------------------------------------
1 | $('#xitolite-options').html("<%= escape_javascript(render 'repositories/xitolite_options', repository: @repository) %>");
2 | $('#xitolite-messages').append("<%= escape_javascript render_flash_messages %>");
3 | $('#xitolite-messages').children().each(function(index, element){ $(element).delay(3000).slideUp(200, function(){$(this) }) });
4 |
--------------------------------------------------------------------------------
/db/migrate/20091119162428_create_git_caches.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateGitCaches < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :git_caches do |t|
6 | t.column :command, :text
7 | t.column :command_output, :binary
8 | t.column :proj_identifier, :string
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/views/repository_git_config_keys/index.api.rsb:
--------------------------------------------------------------------------------
1 | api.array :repository_git_config_keys, api_meta(total_count: @repository_git_config_keys.count) do
2 | @repository_git_config_keys.each do |git_config_key|
3 | api.git_config_key do
4 | api.id git_config_key.id
5 | api.key git_config_key.key
6 | api.value git_config_key.value
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20140306002300_create_repository_git_config_keys.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateRepositoryGitConfigKeys < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :repository_git_config_keys do |t|
6 | t.column :repository_id, :integer
7 | t.column :key, :string
8 | t.column :value, :string
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/auth.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | class Auth
5 | def find(login, password)
6 | user = User.find_by login: login
7 | # Return if user not found
8 | return if user.nil?
9 |
10 | # Return user if password matches
11 | user if user.check_password? password
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20140624150200_remove_gitolite_public_keys_active_column.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RemoveGitolitePublicKeysActiveColumn < ActiveRecord::Migration[4.2]
4 | def up
5 | remove_column :gitolite_public_keys, :active
6 | end
7 |
8 | def down
9 | add_column :gitolite_public_keys, :active, :boolean, default: true, after: :fingerprint
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/views/repositories/_download_revision.html.slim:
--------------------------------------------------------------------------------
1 | - if repository.downloadable?
2 | = font_awesome_icon 'fas_download', post_text: l(:label_download_format)
3 | '
4 | = select_tag :download, options_for_select(available_download_format(repository, @rev)),
5 | prompt: l(:label_download_select_format),
6 | onchange: "if (this.value != '') { window.location = this.value; }"
7 |
--------------------------------------------------------------------------------
/db/migrate/20110807000000_create_repository_mirrors.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateRepositoryMirrors < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :repository_mirrors do |t|
6 | t.references :project, type: :integer
7 | t.column :active, :integer, default: 1
8 | t.column :url, :string
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20130909195727_create_repository_git_notifications.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateRepositoryGitNotifications < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :repository_git_notifications do |t|
6 | t.column :repository_id, :integer
7 | t.column :include_list, :text
8 | t.column :exclude_list, :text
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/models/group_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path('../spec_helper', __dir__)
4 |
5 | describe Group do
6 | subject { group }
7 |
8 | let(:group) { build :group }
9 |
10 | it { is_expected.to have_many(:protected_branches_members).dependent(:destroy) }
11 | it { is_expected.to have_many(:protected_branches).through(:protected_branches_members) }
12 | end
13 |
--------------------------------------------------------------------------------
/.github/workflows/stylelint.yml:
--------------------------------------------------------------------------------
1 | name: Run StyleLint
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | build:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v3
14 | - uses: actions/setup-node@v3
15 | with:
16 | node-version: '16'
17 | - run: yarn install
18 | - run: node_modules/.bin/stylelint "assets/stylesheets/*.css"
19 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Config
5 | GITHUB_ISSUE = 'https://github.com/jbox-web/redmine_git_hosting/issues'
6 | GITHUB_WIKI = 'http://redmine-git-hosting.io/configuration/variables/'
7 |
8 | GITOLITE_DEFAULT_CONFIG_FILE = 'gitolite.conf'
9 | GITOLITE_IDENTIFIER_DEFAULT_PREFIX = 'redmine_'
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/models/repository_git_config_key/git_config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RepositoryGitConfigKey::GitConfig < RepositoryGitConfigKey
4 | VALID_CONFIG_KEY_REGEX = /\A[a-zA-Z0-9]+\.[a-zA-Z0-9.]+\z/
5 |
6 | validates :key, presence: true,
7 | uniqueness: { case_sensitive: false, scope: %i[type repository_id] },
8 | format: { with: VALID_CONFIG_KEY_REGEX }
9 | end
10 |
--------------------------------------------------------------------------------
/app/views/repository_mirrors/show.api.rsb:
--------------------------------------------------------------------------------
1 | api.mirror do
2 | api.id @mirror.id
3 | api.url @mirror.url
4 | api.active @mirror.active
5 | api.push_mode @mirror.push_mode_to_s
6 | api.include_all_branches @mirror.include_all_branches
7 | api.include_all_tags @mirror.include_all_tags
8 | api.explicit_refspec @mirror.explicit_refspec
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20120521000000_create_repository_post_receive_urls.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateRepositoryPostReceiveUrls < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :repository_post_receive_urls do |t|
6 | t.references :project, type: :integer
7 | t.column :active, :integer, default: 1
8 | t.column :url, :string
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20140305053200_remove_notify_cia.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RemoveNotifyCia < ActiveRecord::Migration[4.2]
4 | def up
5 | drop_table :git_cia_notifications
6 | remove_column :repository_git_extras, :notify_cia
7 | end
8 |
9 | def down
10 | create_table :git_cia_notifications do |t|
11 | t.column :repository_id, :integer
12 | t.column :scmid, :string
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/controllers/go_redirector_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class GoRedirectorController < ApplicationController
4 | include XitoliteRepositoryFinder
5 |
6 | # prevents login action to be filtered by check_if_login_required application scope filter
7 | skip_before_action :check_if_login_required, :verify_authenticity_token
8 |
9 | before_action :find_xitolite_repository_by_path
10 |
11 | def index; end
12 | end
13 |
--------------------------------------------------------------------------------
/app/views/repository_post_receive_urls/show.api.rsb:
--------------------------------------------------------------------------------
1 | api.post_receive_url do
2 | api.id @post_receive_url.id
3 | api.url @post_receive_url.url
4 | api.mode @post_receive_url.mode.to_s
5 | api.active @post_receive_url.active
6 | api.use_triggers @post_receive_url.use_triggers
7 | api.triggers @post_receive_url.triggers
8 | api.split_payloads @post_receive_url.split_payloads
9 | end
10 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/git_access_status.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | class GitAccessStatus
5 | attr_accessor :status, :message
6 | alias allowed? status
7 |
8 | def initialize(status, message = '')
9 | @status = status
10 | @message = message
11 | end
12 |
13 | def to_json(*_args)
14 | { status: @status, message: @message }.to_json
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/plugin_author.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | class PluginAuthor
5 | attr_reader :author
6 |
7 | def initialize(author)
8 | @author = author
9 | end
10 |
11 | def name
12 | RedmineGitHosting::Utils::Git.author_name author
13 | end
14 |
15 | def email
16 | RedmineGitHosting::Utils::Git.author_email(author).downcase
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/db/migrate/20091119162427_create_gitolite_public_keys.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateGitolitePublicKeys < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :gitolite_public_keys do |t|
6 | t.column :title, :string
7 | t.column :identifier, :string
8 | t.column :key, :text
9 | t.column :active, :integer, default: 1
10 | t.references :user
11 | t.timestamps
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20130910195930_add_columns_to_repository_git_extra.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddColumnsToRepositoryGitExtra < ActiveRecord::Migration[4.2]
4 | def up
5 | return if RepositoryGitExtra.column_names.include? 'git_notify'
6 |
7 | add_column :repository_git_extras, :git_notify, :integer, default: 0, after: :git_http
8 | end
9 |
10 | def down
11 | remove_column :repository_git_extras, :git_notify
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/global/purge_recycle_bin.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Global
6 | class PurgeRecycleBin < GitoliteWrappers::Base
7 | def call
8 | RedmineGitHosting::RecycleBin.delete_expired_content
9 | RedmineGitHosting.logger.info 'purge_recycle_bin : done !'
10 | end
11 | end
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/views/repositories/_readme.html.slim:
--------------------------------------------------------------------------------
1 | #readme_container.box
2 | h2.readme_trigger style='cursor: pointer;'
3 | | README
4 |
5 | hr
6 |
7 | #readme_at_repo_dir.wiki
8 | - if html.html_safe.respond_to? :force_encoding
9 | = html.html_safe.force_encoding 'UTF-8'
10 | - else
11 | = html.html_safe
12 |
13 | javascript:
14 | $(document).ready(function() { $("#readme_trigger").click(function(){ $("#readme_at_repo_dir").toggle(500); }); });
15 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/plugins.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Plugins
5 | extend self
6 |
7 | def execute(step, repository, **opts)
8 | Plugins::GitolitePlugin.all_plugins.each do |plugin|
9 | plugin.new(repository, **opts).send(step) if plugin.method_defined? step
10 | end
11 | rescue StandardError => e
12 | RedmineGitHosting.logger.error e.message
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/utils/crypto.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'securerandom'
4 |
5 | module RedmineGitHosting
6 | module Utils
7 | module Crypto
8 | extend self
9 |
10 | def generate_secret(length)
11 | length = length.to_i
12 | secret = SecureRandom.base64 length * 2
13 | secret = secret.gsub %r{[=_\-+/]}, ''
14 | secret.chars.sample(length).join
15 | end
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/factories/user.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :user do
5 | sequence(:login) { |n| "user#{n}" }
6 | sequence(:firstname) { |n| "User#{n}" }
7 | sequence(:lastname) { |n| "Test#{n}" }
8 | sequence(:mail) { |n| "user#{n}@awesome.com" }
9 | language { 'fr' }
10 | hashed_password { '66eb4812e268747f89ec309178e2ea50410653fb' }
11 | salt { '5abd4e59ac0d483daf2f68d3b6544ff3' }
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/global/delete_from_recycle_bin.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Global
6 | class DeleteFromRecycleBin < GitoliteWrappers::Base
7 | def call
8 | RedmineGitHosting::RecycleBin.delete_content object_id
9 | RedmineGitHosting.logger.info 'delete_from_recycle_bin : done !'
10 | end
11 | end
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/use_cases/repository_mirrors/base.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RepositoryMirrors
4 | class Base
5 | attr_reader :mirror, :repository
6 |
7 | def initialize(mirror)
8 | @mirror = mirror
9 | @repository = mirror.repository
10 | end
11 |
12 | class << self
13 | def call(mirror)
14 | new(mirror).call
15 | end
16 | end
17 |
18 | def call
19 | raise NotImplementedError
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/models/concerns/gitolitable.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Gitolitable
4 | extend ActiveSupport::Concern
5 | include Gitolitable::Authorizations
6 | include Gitolitable::Cache
7 | include Gitolitable::Config
8 | include Gitolitable::Features
9 | include Gitolitable::Notifications
10 | include Gitolitable::Paths
11 | include Gitolitable::Permissions
12 | include Gitolitable::Urls
13 | include Gitolitable::Users
14 | include Gitolitable::Validations
15 | end
16 |
--------------------------------------------------------------------------------
/app/views/repositories/_edit_top.html.slim:
--------------------------------------------------------------------------------
1 | - if @repository.is_a? Repository::Xitolite
2 | = render partial: 'common/git_hosting_js_headers'
3 |
4 | .git_hosting.box
5 | h3
6 | = l :label_repository_access_url
7 | - unless @repository.git_annex_enabled?
8 | '
9 | = link_to l(:label_sort_urls),
10 | sort_urls_repository_git_extras_path(@repository),
11 | remote: true
12 |
13 | = render 'common/git_urls', repository: @repository
14 |
--------------------------------------------------------------------------------
/app/views/repository_git_config_keys/_form.html.slim:
--------------------------------------------------------------------------------
1 | .flash-messages = error_messages_for 'git_config_key'
2 |
3 | .box
4 | p = f.select :type,
5 | options_for_select(git_config_key_options, @git_config_key.type),
6 | { required: @git_config_key.new_record?, prompt: 'Select a key type' },
7 | { disabled: !@git_config_key.new_record? }
8 | p = f.text_field :key, required: true, size: 65, label: :label_key
9 | p = f.text_field :value, required: true, size: 65
10 |
--------------------------------------------------------------------------------
/spec/models/repository_git_config_key_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path "#{File.dirname __FILE__}/../spec_helper"
4 |
5 | describe RepositoryGitConfigKey do
6 | let(:git_config_key) { build :repository_git_config_key_base }
7 |
8 | subject { git_config_key }
9 |
10 | ## Relations
11 | it { should belong_to(:repository) }
12 |
13 | ## Validations
14 | it { should validate_presence_of(:repository_id) }
15 | it { should validate_presence_of(:value) }
16 | end
17 |
--------------------------------------------------------------------------------
/db/migrate/20120710204007_add_repository_mirror_fields.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddRepositoryMirrorFields < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :repository_mirrors, :push_mode, :integer, default: 0
6 | add_column :repository_mirrors, :include_all_branches, :boolean, default: false
7 | add_column :repository_mirrors, :include_all_tags, :boolean, default: false
8 | add_column :repository_mirrors, :explicit_refspec, :string, default: ''
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/contrib/github/database-postgres.yml:
--------------------------------------------------------------------------------
1 | production:
2 | adapter: postgresql
3 | host: localhost
4 | database: redmine
5 | username: postgres
6 | password: postgres
7 | encoding: utf8
8 |
9 | development:
10 | adapter: postgresql
11 | host: localhost
12 | database: redmine
13 | username: postgres
14 | password: postgres
15 | encoding: utf8
16 |
17 | test:
18 | adapter: postgresql
19 | host: localhost
20 | database: redmine
21 | username: postgres
22 | password: postgres
23 | encoding: utf8
24 |
--------------------------------------------------------------------------------
/app/views/repository_git_extras/_sort_urls_modal.html.slim:
--------------------------------------------------------------------------------
1 | ul#sortable-url.list-unstyled data-remote="true" data-update-url="#{sort_urls_repository_git_extras_path @repository}"
2 | - @repository.available_urls_sorted.each_key do |url_type|
3 | li.url_type.draggable id="repository_git_extra_#{url_type}"
4 | = icon_for_url_type url_type
5 | '
6 | span
7 | = label_for_url_type url_type
8 |
9 | javascript:
10 | $(document).ready(function() { setSortableElement('#sortable-url', '#sortable-url'); });
11 |
--------------------------------------------------------------------------------
/db/migrate/20130909195929_rename_table_deployment_credentials.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RenameTableDeploymentCredentials < ActiveRecord::Migration[4.2]
4 | def up
5 | remove_index :deployment_credentials, :gitolite_public_key_id
6 | rename_table :deployment_credentials, :repository_deployment_credentials
7 | end
8 |
9 | def down
10 | rename_table :repository_deployment_credentials, :deployment_credentials
11 | add_index :deployment_credentials, :gitolite_public_key_id
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/patches/issue_patch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Patches
5 | module IssuePatch
6 | def self.prepended(base)
7 | base.class_eval do
8 | has_one :github_issue, foreign_key: 'issue_id', class_name: 'GithubIssue', dependent: :destroy
9 | end
10 | end
11 | end
12 | end
13 | end
14 |
15 | Issue.prepend RedmineGitHosting::Patches::IssuePatch unless Issue.included_modules.include? RedmineGitHosting::Patches::IssuePatch
16 |
--------------------------------------------------------------------------------
/app/helpers/repository_post_receive_urls_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RepositoryPostReceiveUrlsHelper
4 | # Post-receive Mode
5 | def post_receive_mode(prurl)
6 | label = []
7 | if prurl.github_mode?
8 | label << l(:label_github_post)
9 | label << "(#{l :label_split_payloads})" if prurl.split_payloads?
10 | elsif prurl.mode == :post
11 | label << l(:label_empty_post)
12 | else
13 | label << l(:label_empty_get)
14 | end
15 | label.join ' '
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/views/repository_mirrors/_new_modal.html.slim:
--------------------------------------------------------------------------------
1 | h3.title = l :label_mirror_add
2 |
3 | = labelled_form_for :repository_mirror, @mirror,
4 | url: repository_mirrors_path(@repository),
5 | authenticity_token: form_authenticity_token,
6 | html: { method: :post, class: 'tabular', remote: true } do |f|
7 |
8 | = render partial: 'form', locals: { f: f }
9 | .buttons
10 | = submit_tag l(:button_add)
11 | '
12 | = link_to_function l(:button_cancel), 'hideModal(this);'
13 |
--------------------------------------------------------------------------------
/app/helpers/gitolite_public_keys_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module GitolitePublicKeysHelper
4 | def keylabel(key)
5 | key.user == User.current ? key.title&.to_s : "#{key.user.login}@#{key.title}"
6 | end
7 |
8 | def can_create_deployment_keys_for_some_project(theuser = User.current)
9 | return true if theuser.admin?
10 |
11 | theuser.projects_by_role.each_key do |role|
12 | return true if role.allowed_to? :create_repository_deployment_credentials
13 | end
14 | false
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/views/repository_protected_branches/index.api.rsb:
--------------------------------------------------------------------------------
1 | api.array :repository_protected_branches, api_meta(total_count: @repository_protected_branches.count) do
2 | @repository_protected_branches.each do |protected_branch|
3 | api.protected_branch do
4 | api.id protected_branch.id
5 | api.path protected_branch.path
6 | api.permissions protected_branch.permissions
7 | api.user_list protected_branch.user_list
8 | api.position protected_branch.position
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/models/setting_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path "#{File.dirname __FILE__}/../spec_helper"
4 |
5 | describe Setting do
6 | before do
7 | RedmineGitHosting::Config.reload_from_file!
8 | @settings = Setting.plugin_redmine_git_hosting
9 | @default_settings = Redmine::Plugin.find('redmine_git_hosting').settings[:default]
10 | end
11 |
12 | subject { @settings }
13 |
14 | it { should be_an_instance_of(Hash) }
15 |
16 | # it { expect(@settings).to eq @default_settings }
17 | end
18 |
--------------------------------------------------------------------------------
/app/overrides/repositories/navigation.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Deface::Override.new virtual_path: 'repositories/_navigation',
4 | name: 'show-repositories-hook-navigation',
5 | insert_before: 'erb[loud]:contains("label_statistics")',
6 | original: '88f120e99075ba3246901c6e970ca671d7166855',
7 | text: '<%= call_hook(:view_repositories_navigation, repository: @repository) %>'
8 |
9 | module Repositories
10 | module Navigation
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/views/repository_mirrors/_edit_modal.html.slim:
--------------------------------------------------------------------------------
1 | h3.title = l :label_mirror_edit
2 |
3 | = labelled_form_for :repository_mirror, @mirror,
4 | url: repository_mirror_path(@repository, @mirror),
5 | authenticity_token: form_authenticity_token,
6 | html: { method: :put, class: 'tabular', remote: true } do |f|
7 |
8 | = render partial: 'form', locals: { f: f }
9 | .buttons
10 | = submit_tag l(:button_save)
11 | '
12 | = link_to_function l(:button_cancel), 'hideModal(this);'
13 |
--------------------------------------------------------------------------------
/app/use_cases/projects/base.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Projects
4 | class Base
5 | include RedmineGitHosting::GitoliteAccessor::Methods
6 |
7 | attr_reader :project, :options
8 |
9 | def initialize(project, opts = nil)
10 | @project = project
11 | @options = opts
12 | end
13 |
14 | class << self
15 | def call(project, opts = nil)
16 | new(project, opts).call
17 | end
18 | end
19 |
20 | def call
21 | raise NotImplementedError
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/factories/protected_branches_member.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :protected_branch_user_member, class: 'ProtectedBranchesMember' do
5 | association :protected_branch, factory: :repository_protected_branche
6 | association :principal, factory: :user
7 | end
8 |
9 | factory :protected_branch_group_member, class: 'ProtectedBranchesMember' do
10 | association :protected_branch, factory: :repository_protected_branche
11 | association :principal, factory: :group
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20150705180400_rename_git_config_keys_index.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RenameGitConfigKeysIndex < ActiveRecord::Migration[4.2]
4 | def up
5 | remove_index :repository_git_config_keys, %i[key repository_id]
6 | add_index :repository_git_config_keys, %i[key type repository_id], unique: true, name: :unique_key_name
7 | end
8 |
9 | def down
10 | remove_index :repository_git_config_keys, name: :unique_key_name
11 | add_index :repository_git_config_keys, %i[key repository_id], unique: true
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20140621004200_create_repository_protected_branches.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateRepositoryProtectedBranches < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :repository_protected_branches do |t|
6 | t.column :repository_id, :integer
7 | t.column :path, :string
8 | t.column :permissions, :string
9 | t.column :user_list, :text
10 | t.column :position, :integer
11 | end
12 |
13 | add_index :repository_protected_branches, :repository_id
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/file_logger.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'logger'
4 |
5 | module RedmineGitHosting
6 | class FileLogger < ::Logger
7 | LOG_LEVELS = %w[debug info warn error].freeze
8 |
9 | def self.init_logs!(appname, logfile, loglevel)
10 | logger = new logfile
11 | logger.progname = appname
12 | logger.level = loglevel
13 | logger.formatter = proc do |severity, time, _progname, msg|
14 | "#{time} [#{severity}] #{msg}\n"
15 | end
16 | logger
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/views/repository_git_config_keys/_new_modal.html.slim:
--------------------------------------------------------------------------------
1 | h3.title = l :label_git_config_keys
2 |
3 | = labelled_form_for :repository_git_config_key, @git_config_key,
4 | url: repository_git_config_keys_path(@repository),
5 | authenticity_token: form_authenticity_token,
6 | html: { method: :post, class: 'tabular', remote: true } do |f|
7 |
8 | = render partial: 'form', locals: { f: f }
9 | .buttons
10 | = submit_tag l(:button_add)
11 | '
12 | = link_to_function l(:button_cancel), 'hideModal(this);'
13 |
--------------------------------------------------------------------------------
/app/views/settings/redmine_git_hosting/_gitolite_rescue.html.slim:
--------------------------------------------------------------------------------
1 | p
2 | = label_tag 'settings[rescue][resync_all_projects]', l(:label_resync_all_projects)
3 | = check_box_tag 'settings[rescue][resync_all_projects]', 'true', false
4 |
5 | p
6 | = label_tag 'settings[rescue][resync_all_ssh_keys]', l(:label_resync_all_ssh_keys)
7 | = check_box_tag 'settings[rescue][resync_all_ssh_keys]', 'true', false
8 |
9 | p
10 | = label_tag 'settings[rescue][flush_gitolite_cache]', l(:label_flush_cache)
11 | = check_box_tag 'settings[rescue][flush_gitolite_cache]', 'true', false
12 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/users/resync_ssh_keys.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Users
6 | class ResyncSshKeys < GitoliteWrappers::Base
7 | def call
8 | admin.transaction do
9 | GitolitePublicKey.all.each do |ssh_key|
10 | create_gitolite_key ssh_key
11 | gitolite_admin_repo_commit "Add SSH key : #{ssh_key.identifier}"
12 | end
13 | end
14 | end
15 | end
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/views/repository_post_receive_urls/_new_modal.html.slim:
--------------------------------------------------------------------------------
1 | h3.title = l :label_post_receive_url_add
2 |
3 | = labelled_form_for :repository_post_receive_url, @post_receive_url,
4 | url: repository_post_receive_urls_path(@repository),
5 | authenticity_token: form_authenticity_token,
6 | html: { method: :post, class: 'tabular', remote: true } do |f|
7 |
8 | = render partial: 'form', locals: { f: f }
9 | .buttons
10 | = submit_tag l(:button_add)
11 | '
12 | = link_to_function l(:button_cancel), 'hideModal(this);'
13 |
--------------------------------------------------------------------------------
/app/views/repository_git_config_keys/_edit_modal.html.slim:
--------------------------------------------------------------------------------
1 | h3.title = l :label_git_config_keys
2 |
3 | = labelled_form_for :repository_git_config_key, @git_config_key,
4 | url: repository_git_config_key_path(@repository, @git_config_key),
5 | authenticity_token: form_authenticity_token,
6 | html: { method: :put, class: 'tabular', remote: true } do |f|
7 |
8 | = render partial: 'form', locals: { f: f }
9 | .buttons
10 | = submit_tag l(:button_save)
11 | '
12 | = link_to_function l(:button_cancel), 'hideModal(this);'
13 |
--------------------------------------------------------------------------------
/app/views/repository_protected_branches/_new_modal.html.slim:
--------------------------------------------------------------------------------
1 | h3.title = l :label_protected_branch_add
2 |
3 | = labelled_form_for :repository_protected_branche, @protected_branch,
4 | url: repository_protected_branches_path(@repository),
5 | authenticity_token: form_authenticity_token,
6 | html: { method: :post, class: 'tabular', remote: true } do |f|
7 |
8 | = render partial: 'form', locals: { f: f }
9 | .buttons
10 | = submit_tag l(:button_add)
11 | '
12 | = link_to_function l(:button_cancel), 'hideModal(this);'
13 |
--------------------------------------------------------------------------------
/assets/images/button.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/patches/journal_patch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_dependency 'journal'
4 |
5 | module RedmineGitHosting
6 | module Patches
7 | module JournalPatch
8 | def self.prepended(base)
9 | base.class_eval do
10 | has_one :github_comment, foreign_key: 'journal_id', class_name: 'GithubComment', dependent: :destroy
11 | end
12 | end
13 | end
14 | end
15 | end
16 |
17 | Journal.prepend RedmineGitHosting::Patches::JournalPatch unless Journal.included_modules.include? RedmineGitHosting::Patches::JournalPatch
18 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/patches/repository_patch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_dependency 'repository'
4 |
5 | module RedmineGitHosting
6 | module Patches
7 | module RepositoryPatch
8 | # This is the (possibly non-unique) basename for the Gitolite repository
9 | def redmine_name
10 | identifier.presence || project.identifier
11 | end
12 | end
13 | end
14 | end
15 |
16 | unless Repository.included_modules.include? RedmineGitHosting::Patches::RepositoryPatch
17 | Repository.prepend RedmineGitHosting::Patches::RepositoryPatch
18 | end
19 |
--------------------------------------------------------------------------------
/assets/images/button_selected.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/assets/stylesheets/plugin.css:
--------------------------------------------------------------------------------
1 | /*
2 | PLUGIN ICON
3 | */
4 |
5 | /* stylelint-disable font-family-no-missing-generic-family-keyword */
6 |
7 | #admin-menu a.redmine-git-hosting::before {
8 | font-family: Font Awesome\ 5 Brands;
9 | font-size: 1.4em;
10 | content: "\f841"; /* fab fa-git-alt */
11 | padding-right: 4px;
12 | }
13 |
14 | #admin-menu a.redmine-git-hosting {
15 | padding-left: 0;
16 | }
17 |
18 | .authors-list {
19 | columns: 2;
20 | -webkit-columns: 2;
21 | -moz-columns: 2;
22 | }
23 |
24 | .icon-git { padding-right: 10px; }
25 | .icon-git-disabled { opacity: 0.5; }
26 |
--------------------------------------------------------------------------------
/app/controllers/archived_repositories_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ArchivedRepositoriesController < RepositoriesController
4 | skip_before_action :authorize
5 | skip_before_action :find_project_repository, only: :index
6 |
7 | before_action :can_view_archived_projects
8 |
9 | def index
10 | @archived_projects = Project.where(status: Project::STATUS_ARCHIVED)
11 | .includes(:repositories)
12 | end
13 |
14 | private
15 |
16 | def can_view_archived_projects
17 | render_403 unless User.current.admin?
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/views/repository_mirrors/index.api.rsb:
--------------------------------------------------------------------------------
1 | api.array :repository_mirrors, api_meta(total_count: @repository_mirrors.count) do
2 | @repository_mirrors.each do |mirror|
3 | api.mirror do
4 | api.id mirror.id
5 | api.url mirror.url
6 | api.active mirror.active
7 | api.push_mode mirror.push_mode_to_s
8 | api.include_all_branches mirror.include_all_branches
9 | api.include_all_tags mirror.include_all_tags
10 | api.explicit_refspec mirror.explicit_refspec
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/assets/images/button_focus.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/db/migrate/20110726000000_extend_changesets_notified_cia.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ExtendChangesetsNotifiedCia < ActiveRecord::Migration[4.2]
4 | def up
5 | add_column :changesets, :notified_cia, :integer, default: 0
6 | end
7 |
8 | def down
9 | # Deal with fact that one of next migrations doesn't restore :notified_cia
10 | remove_column :changesets, :notified_cia if column_exists? :changesets, :notified_cia
11 | end
12 |
13 | def column_exists?(table_name, column_name)
14 | columns(table_name).any? { |c| c.name == column_name.to_s }
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/views/repository_post_receive_urls/_edit_modal.html.slim:
--------------------------------------------------------------------------------
1 | h3.title = l :label_post_receive_url_edit
2 |
3 | = labelled_form_for :repository_post_receive_url, @post_receive_url,
4 | url: repository_post_receive_url_path(@repository, @post_receive_url),
5 | authenticity_token: form_authenticity_token,
6 | html: { method: :put, class: 'tabular', remote: true } do |f|
7 |
8 | = render partial: 'form', locals: { f: f }
9 | .buttons
10 | = submit_tag l(:button_save)
11 | '
12 | = link_to_function l(:button_cancel), 'hideModal(this);'
13 |
--------------------------------------------------------------------------------
/app/views/repository_protected_branches/_edit_modal.html.slim:
--------------------------------------------------------------------------------
1 | h3.title = l :label_protected_branch_edit
2 |
3 | = labelled_form_for :repository_protected_branche, @protected_branch,
4 | url: repository_protected_branch_path(@repository, @protected_branch),
5 | authenticity_token: form_authenticity_token,
6 | html: { method: :put, class: 'tabular', remote: true } do |f|
7 |
8 | = render partial: 'form', locals: { f: f }
9 | .buttons
10 | = submit_tag l(:button_save)
11 | '
12 | = link_to_function l(:button_cancel), 'hideModal(this);'
13 |
--------------------------------------------------------------------------------
/spec/models/user_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path('../spec_helper', __dir__)
4 |
5 | describe User do
6 | let(:user) { build :user }
7 |
8 | it { is_expected.to have_many(:protected_branches_members).dependent(:destroy) }
9 | it { is_expected.to have_many(:protected_branches).through(:protected_branches_members) }
10 |
11 | describe '#gitolite_identifier' do
12 | it 'return the gitolite_identifier' do
13 | user = build :user, login: 'adam.30', id: 12
14 | expect(user.gitolite_identifier).to eq 'redmine_adam_30_12'
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/users/regenerate_ssh_keys.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Users
6 | class RegenerateSshKeys < GitoliteWrappers::Base
7 | def call
8 | GitolitePublicKey.all.each do |ssh_key|
9 | gitolite_accessor.destroy_ssh_key ssh_key, bypass_sidekiq: true
10 | ssh_key.reset_identifiers skip_auto_increment: true
11 | gitolite_accessor.create_ssh_key ssh_key, bypass_sidekiq: true
12 | end
13 | end
14 | end
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/views/settings/redmine_git_hosting/_install_hooks_result.html.slim:
--------------------------------------------------------------------------------
1 | h3.title = l :label_install_hook_results
2 |
3 | table.list.git-results
4 | tr
5 | td = l :label_gitolite_hooks_installed
6 | td = render_gitolite_params_status @gitolite_checks[:hook_files]
7 | tr
8 | td = l :label_gitolite_hooks_params_installed
9 | td = render_gitolite_params_status @gitolite_checks[:global_params]
10 | tr
11 | td = l :label_gitolite_mailer_params_installed
12 | td = render_gitolite_params_status @gitolite_checks[:mailer_params]
13 |
14 | .buttons
15 | = link_to_function l(:button_cancel), 'hideModal(this);'
16 |
--------------------------------------------------------------------------------
/db/migrate/20150823030000_create_protected_branches_members.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateProtectedBranchesMembers < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :protected_branches_members do |t|
6 | t.column :protected_branch_id, :integer
7 | t.column :principal_id, :integer
8 | t.column :inherited_by, :integer
9 | end
10 |
11 | add_index :protected_branches_members,
12 | %i[protected_branch_id principal_id inherited_by],
13 | unique: true,
14 | name: 'unique_protected_branch_member'
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_handlers/ssh_keys/delete_ssh_key.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteHandlers
5 | module SshKeys
6 | class DeleteSshKey < Base
7 | def call
8 | repo_key = find_gitolite_key key[:owner], key[:location]
9 |
10 | # Remove it if found
11 | if repo_key
12 | admin.rm_key repo_key
13 | else
14 | logger.info "#{context} : SSH key '#{key[:owner]}@#{key[:location]}' does not exits in Gitolite, exit !"
15 | end
16 | end
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/forms/base_form.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module BaseForm
4 | extend ActiveSupport::Concern
5 |
6 | included do
7 | include ActiveModel::Validations
8 | include ActiveModel::Validations::Callbacks
9 | include ActiveModel::Conversion
10 | extend ActiveModel::Naming
11 | end
12 |
13 | def persisted?
14 | false
15 | end
16 |
17 | def submit(attributes = {})
18 | attributes.each do |name, value|
19 | send "#{name}=", value
20 | end
21 | if valid?
22 | valid_form_submitted if respond_to? :valid_form_submitted
23 | true
24 | else
25 | false
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/db/migrate/20140305083200_add_default_branch_to_repository_git_extra.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddDefaultBranchToRepositoryGitExtra < ActiveRecord::Migration[4.2]
4 | def up
5 | add_column :repository_git_extras, :default_branch, :string, after: :git_notify
6 |
7 | RepositoryGitExtra.reset_column_information
8 | RepositoryGitExtra.all.each do |extra|
9 | extra.update_attribute :default_branch, 'master'
10 | end
11 |
12 | change_column :repository_git_extras, :default_branch, :string, null: false
13 | end
14 |
15 | def down
16 | remove_column :repository_git_extras, :default_branch
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/users/delete_ssh_key.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Users
6 | class DeleteSshKey < GitoliteWrappers::Base
7 | def call
8 | logger.info "Deleting SSH key '#{ssh_key[:title]}'"
9 | admin.transaction do
10 | delete_gitolite_key ssh_key
11 | gitolite_admin_repo_commit "Delete SSH key : #{ssh_key[:title]}"
12 | end
13 | end
14 |
15 | def ssh_key
16 | @ssh_key ||= object_id.symbolize_keys
17 | end
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/views/repository_post_receive_urls/index.api.rsb:
--------------------------------------------------------------------------------
1 | api.array :repository_post_receive_urls, api_meta(total_count: @repository_post_receive_urls.count) do
2 | @repository_post_receive_urls.each do |post_receive_url|
3 | api.post_receive_url do
4 | api.id post_receive_url.id
5 | api.url post_receive_url.url
6 | api.mode post_receive_url.mode.to_s
7 | api.active post_receive_url.active
8 | api.use_triggers post_receive_url.use_triggers
9 | api.triggers post_receive_url.triggers
10 | api.split_payloads post_receive_url.split_payloads
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/models/git_cache.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class GitCache < ActiveRecord::Base
4 | include Redmine::SafeAttributes
5 |
6 | CACHE_ADAPTERS = [%w[Database database],
7 | %w[Memcached memcached],
8 | %w[Redis redis]].freeze
9 |
10 | ## Attributes
11 | safe_attributes 'repo_identifier', 'command', 'command_output'
12 |
13 | ## Validations
14 | validates :repo_identifier, presence: true
15 | validates :command, presence: true
16 | validates :command_output, presence: true
17 |
18 | class << self
19 | def adapters
20 | CACHE_ADAPTERS.map(&:last)
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/users/add_ssh_key.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Users
6 | class AddSshKey < GitoliteWrappers::Base
7 | def call
8 | logger.info "Adding SSH key '#{ssh_key.identifier}'"
9 | admin.transaction do
10 | create_gitolite_key ssh_key
11 | gitolite_admin_repo_commit "Add SSH key : #{ssh_key.identifier}"
12 | end
13 | end
14 |
15 | def ssh_key
16 | @ssh_key ||= GitolitePublicKey.find_by id: object_id
17 | end
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/contrib/hooks/post-receive/lib/git_hosting_hook_logger.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module GitHosting
4 | class HookLogger
5 | attr_reader :loglevel
6 |
7 | def initialize(loglevel: 'info')
8 | @loglevel = loglevel
9 | end
10 |
11 | def debug(message)
12 | write message if loglevel == 'debug'
13 | end
14 |
15 | def info(message)
16 | write message
17 | end
18 |
19 | def error(message)
20 | write message
21 | end
22 |
23 | private
24 |
25 | def write(message)
26 | $stdout.sync = true
27 | $stdout.puts "\e[1G#{message}"
28 | $stdout.flush
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/app/views/repositories/_show_top.html.slim:
--------------------------------------------------------------------------------
1 | - if @repository.is_a? Repository::Xitolite
2 | - content_for :header_tags do
3 | = stylesheet_link_tag 'application', plugin: 'redmine_git_hosting'
4 | = stylesheet_link_tag 'markdown', plugin: 'redmine_git_hosting'
5 |
6 | .git_hosting_urls.box
7 | .container
8 | .row
9 | .col-md-8 style='vertical-align: middle;'
10 | = render 'common/git_urls', repository: @repository
11 | .col-md-4 style='text-align: right; vertical-align: middle;'
12 | - if RedmineGitHosting::Config.download_revision_enabled?
13 | = render 'repositories/download_revision', repository: @repository
14 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/markdown_renderer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'html/pipeline'
4 | require 'task_list/filter'
5 | require 'task_list/railtie'
6 |
7 | module RedmineGitHosting
8 | module MarkdownRenderer
9 | extend self
10 |
11 | def to_html(markdown)
12 | pipeline.call(markdown)[:output].to_s
13 | end
14 |
15 | private
16 |
17 | def pipeline
18 | HTML::Pipeline.new filters
19 | end
20 |
21 | def filters
22 | [RedmineGitHosting::RedcarpetFilter,
23 | TaskList::Filter,
24 | HTML::Pipeline::AutolinkFilter,
25 | HTML::Pipeline::TableOfContentsFilter]
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/patches/repositories_helper_patch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_dependency 'repositories_helper'
4 |
5 | module RedmineGitHosting
6 | module Patches
7 | module RepositoriesHelperPatch
8 | def xitolite_field_tags(form, repository)
9 | encoding_field(form, repository) +
10 | create_readme_field(form, repository) +
11 | enable_git_annex_field(form, repository)
12 | end
13 | end
14 | end
15 | end
16 |
17 | unless RepositoriesHelper.included_modules.include? RedmineGitHosting::Patches::RepositoriesHelperPatch
18 | RepositoriesHelper.prepend RedmineGitHosting::Patches::RepositoriesHelperPatch
19 | end
20 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path "#{File.dirname __FILE__}/../../../spec/spec_helper"
4 |
5 | HOME_BASE_DIR = RUBY_PLATFORM.include?('darwin') ? '/Users' : '/home'
6 |
7 | Shoulda::Matchers.configure do |config|
8 | config.integrate do |with|
9 | with.test_framework :rspec
10 | with.library :rails
11 | end
12 | end
13 |
14 | ## Configure RSpec
15 | RSpec.configure do |config|
16 | # Include our helpers from support directory
17 | config.include GlobalHelpers
18 |
19 | config.before :suite do
20 | RedmineGitHosting::Config.reload_from_file!
21 | Setting.enabled_scm = %w[Git Subversion Xitolite]
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/workers/githosting_shell_worker.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class GithostingShellWorker
4 | include Sidekiq::Worker
5 |
6 | sidekiq_options queue: :redmine_git_hosting, retry: false
7 |
8 | def self.maybe_do(command, object, options)
9 | args = [command.to_s, object, options]
10 | Sidekiq::Queue.new(:redmine_git_hosting).each do |job|
11 | return nil if job.args == args
12 | end
13 |
14 | perform_async command, object, options
15 | end
16 |
17 | def perform(command, object, options)
18 | logger.info "#{command} | #{object} | #{options}"
19 | RedmineGitHosting::GitoliteWrapper.resync_gitolite command, object, **options
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/contrib/hooks/post-receive/redmine_gitolite.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # frozen_string_literal: true
4 |
5 | # This file was placed here by Redmine Git Hosting. It makes sure that your pushed commits
6 | # will be processed properly.
7 |
8 | refs = ARGF.read
9 | repo_path = Dir.pwd
10 |
11 | require_relative 'lib/git_hosting/http_helper'
12 | require_relative 'lib/git_hosting/hook_logger'
13 | require_relative 'lib/git_hosting/config'
14 | require_relative 'lib/git_hosting/post_receive'
15 | require_relative 'lib/git_hosting/custom_hook'
16 |
17 | if GitHosting::PostReceive.new(repo_path, refs).exec && GitHosting::CustomHook.new(repo_path, refs).exec
18 | exit 0
19 | else
20 | exit 1
21 | end
22 |
--------------------------------------------------------------------------------
/app/views/settings/_redmine_git_hosting.html.slim:
--------------------------------------------------------------------------------
1 | = render partial: 'common/git_hosting_js_headers'
2 | - content_for :header_tags do
3 | = additionals_library_load :select2
4 |
5 | span
6 | = l :label_need_help
7 | ' :
8 | = link_to_external l(:label_redmine_git_hosting_wiki),
9 | RedmineGitHosting::Config::GITHUB_WIKI
10 |
11 | br
12 |
13 | span
14 | = l :label_open_issue
15 | ' :
16 | = link_to_external l(:label_redmine_git_hosting_issue),
17 | RedmineGitHosting::Config::GITHUB_ISSUE
18 |
19 | br
20 | br
21 |
22 | = render_tabs gitolite_plugin_settings_tabs
23 |
24 | javascript:
25 | $(document).ready(function() { setSettingsActiveTab(); });
26 |
--------------------------------------------------------------------------------
/app/use_cases/repositories/base.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Repositories
4 | class Base
5 | include RedmineGitHosting::GitoliteAccessor::Methods
6 |
7 | attr_reader :repository, :options, :project
8 |
9 | def initialize(repository, opts = nil)
10 | @repository = repository
11 | @options = opts
12 | @project = repository.project
13 | end
14 |
15 | class << self
16 | def call(repository, opts = nil)
17 | new(repository, opts).call
18 | end
19 | end
20 |
21 | def call
22 | raise NotImplementedError
23 | end
24 |
25 | private
26 |
27 | def logger
28 | RedmineGitHosting.logger
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/config/gitolite_cache.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Config
5 | module GitoliteCache
6 | extend self
7 |
8 | def gitolite_cache_max_time
9 | get_setting(:gitolite_cache_max_time).to_i
10 | end
11 |
12 | def gitolite_cache_max_elements
13 | get_setting(:gitolite_cache_max_elements).to_i
14 | end
15 |
16 | def gitolite_cache_max_size
17 | get_setting(:gitolite_cache_max_size).to_i * 1024 * 1024
18 | end
19 |
20 | def gitolite_cache_adapter
21 | get_setting :gitolite_cache_adapter
22 | end
23 | end
24 |
25 | extend Config::GitoliteCache
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_params/base_param.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteParams
5 | module BaseParam
6 | private
7 |
8 | # Return a hash with global config parameters.
9 | #
10 | def get_git_config_params(namespace)
11 | RedmineGitHosting::Commands.sudo_get_git_global_params namespace
12 | end
13 |
14 | def set_git_config_param(namespace, key, value)
15 | RedmineGitHosting::Commands.sudo_set_git_global_param namespace, key, value
16 | end
17 |
18 | def unset_git_config_param(key)
19 | RedmineGitHosting::Commands.sudo_unset_git_global_param key
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/factories/gitolite_public_key.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :gitolite_public_key do
5 | sequence(:title) { |n| "test-key#{n}" }
6 | key do
7 | 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDpqFJzsx3wTi3t3X/eOizU6rdtNQoqg5uSjL89F+Ojjm2/sah3ouzx+3E461FDYaoJL58Qs9eRhL+ev0BY7khYXp' \
8 | 'h8nIVDzNEjhLqjevX+YhpaW9Ll7V807CwAyvMNm08aup/NrrlI/jO+At348/ivJrfO7ClcPhq4+Id9RZfvbrKaitGOURD7q6Bd7xjUjELUN8wmYxu5zvx/2n/5woVd' \
9 | 'BUMXamTPxOY5y6DxTNJ+EYzrCr+bNb7459rWUvBHUQGI2fXDGmFpGiv6ShKRhRtwob1JHI8QC9OtxonrIUesa2dW6RFneUaM7tfRfffC704Uo7yuSswb7YK+p1A9QI' \
10 | 't5 nicolas@tchoum'
11 | end
12 | key_type { 0 }
13 | association :user
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/use_cases/projects/execute_hooks.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Projects
4 | class ExecuteHooks
5 | attr_reader :project, :hook_type, :payloads
6 |
7 | def initialize(project, hook_type, payloads = nil)
8 | @project = project
9 | @hook_type = hook_type
10 | @payloads = payloads
11 | end
12 |
13 | class << self
14 | def call(project, hook_type, payloads = nil)
15 | new(project, hook_type, payloads).call
16 | end
17 | end
18 |
19 | def call
20 | send "execute_#{hook_type}_hook"
21 | end
22 |
23 | private
24 |
25 | def execute_github_hook
26 | RedmineHooks::GithubIssuesSync.call project, payloads
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/patches/sys_controller_patch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_dependency 'sys_controller'
4 |
5 | module RedmineGitHosting
6 | module Patches
7 | module SysControllerPatch
8 | include RedmineGitHosting::GitoliteAccessor::Methods
9 |
10 | def fetch_changesets
11 | # Flush GitCache
12 | gitolite_accessor.flush_git_cache
13 |
14 | super
15 |
16 | # Purge RecycleBin
17 | gitolite_accessor.purge_recycle_bin
18 | end
19 | end
20 | end
21 | end
22 |
23 | unless SysController.included_modules.include? RedmineGitHosting::Patches::SysControllerPatch
24 | SysController.prepend RedmineGitHosting::Patches::SysControllerPatch
25 | end
26 |
--------------------------------------------------------------------------------
/app/models/protected_branches_member.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ProtectedBranchesMember < ActiveRecord::Base
4 | include Redmine::SafeAttributes
5 |
6 | ## Attributes
7 | safe_attributes 'principal_id', 'inherited_by'
8 |
9 | ## Relations
10 | belongs_to :protected_branch, class_name: 'RepositoryProtectedBranche'
11 | belongs_to :principal
12 |
13 | ## Callbacks
14 | after_destroy :remove_dependent_objects
15 |
16 | private
17 |
18 | def remove_dependent_objects
19 | return unless principal.instance_of? Group
20 |
21 | principal.users.each do |user|
22 | member = self.class.find_by principal_id: user.id, inherited_by: principal.id
23 | member&.destroy!
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/services/permissions_builder/protected_branches.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module PermissionsBuilder
4 | class ProtectedBranches < Base
5 | attr_reader :permissions
6 |
7 | def initialize(*args)
8 | super
9 | @permissions = []
10 | end
11 |
12 | def build
13 | build_protected_branch_permissions
14 | permissions
15 | end
16 |
17 | def build_protected_branch_permissions
18 | repository.protected_branches.each do |branch|
19 | perms = {}
20 | perms[branch.permissions] = {}
21 | perms[branch.permissions][branch.path] = branch.allowed_users unless branch.allowed_users.empty?
22 | permissions.push perms
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/commands/base.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Commands
5 | module Base
6 | extend self
7 |
8 | # Wrapper to Open3.capture.
9 | #
10 | def capture(args = [], **opts)
11 | cmd = args.shift
12 | RedmineGitHosting::Utils::Exec.capture cmd, args, **opts
13 | end
14 |
15 | # Wrapper to Open3.capture.
16 | #
17 | def execute(args = [], **opts)
18 | cmd = args.shift
19 | RedmineGitHosting::Utils::Exec.execute cmd, args, **opts
20 | end
21 |
22 | private
23 |
24 | def logger
25 | RedmineGitHosting.logger
26 | end
27 | end
28 |
29 | extend Commands::Base
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/config/gitolite_storage.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Config
5 | module GitoliteStorage
6 | extend self
7 |
8 | def gitolite_global_storage_dir
9 | get_setting :gitolite_global_storage_dir
10 | end
11 |
12 | def gitolite_redmine_storage_dir
13 | get_setting :gitolite_redmine_storage_dir
14 | end
15 |
16 | def gitolite_recycle_bin_dir
17 | get_setting :gitolite_recycle_bin_dir
18 | end
19 |
20 | def recycle_bin_dir
21 | File.join gitolite_home_dir, gitolite_recycle_bin_dir
22 | rescue StandardError
23 | nil
24 | end
25 | end
26 |
27 | extend Config::GitoliteStorage
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/config/gitolite_notifications.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Config
5 | module GitoliteNotifications
6 | extend self
7 |
8 | def gitolite_notify_global_prefix
9 | get_setting :gitolite_notify_global_prefix
10 | end
11 |
12 | def gitolite_notify_global_sender_address
13 | get_setting :gitolite_notify_global_sender_address
14 | end
15 |
16 | def gitolite_notify_global_include
17 | get_setting :gitolite_notify_global_include
18 | end
19 |
20 | def gitolite_notify_global_exclude
21 | get_setting :gitolite_notify_global_exclude
22 | end
23 | end
24 |
25 | extend Config::GitoliteNotifications
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_handlers/repositories/delete_repository.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteHandlers
5 | module Repositories
6 | class DeleteRepository < Base
7 | def call
8 | if configuration_exists?
9 | log_ok_and_continue 'delete it ...'
10 |
11 | # Delete Gitolite repository
12 | delete_repository_config
13 | else
14 | log_repo_not_exist 'exit !'
15 | end
16 | end
17 |
18 | def gitolite_repo_name
19 | repository[:repo_name]
20 | end
21 |
22 | def gitolite_repo_path
23 | repository[:repo_path]
24 | end
25 | end
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/app/services/permissions_builder/base.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module PermissionsBuilder
4 | class Base
5 | attr_reader :repository, :gitolite_users, :old_permissions
6 |
7 | def initialize(repository, gitolite_users, old_permissions = {})
8 | @repository = repository
9 | @gitolite_users = gitolite_users
10 | @old_permissions = old_permissions
11 | end
12 |
13 | class << self
14 | def build(repository, gitolite_users, old_permissions = {})
15 | new(repository, gitolite_users, old_permissions).build
16 | end
17 | end
18 |
19 | def build
20 | raise NotImplementedError
21 | end
22 |
23 | private
24 |
25 | def no_users?(type)
26 | gitolite_users[type].blank?
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/app/views/repository_deployment_credentials/_edit_modal.html.slim:
--------------------------------------------------------------------------------
1 | h3.title = l :label_deployment_credential_edit
2 |
3 | = labelled_form_for :repository_deployment_credential, @credential,
4 | url: repository_deployment_credential_path(@repository, @credential),
5 | authenticity_token: form_authenticity_token,
6 | html: { method: :put, class: 'tabular', remote: true } do |f|
7 |
8 | .flash-messages = error_messages_for 'credential'
9 |
10 | .box
11 | p = f.select :perm, options_for_select(RepositoryDeploymentCredential::VALID_PERMS, @credential.perm), required: true
12 | p = f.check_box :active
13 |
14 | .buttons
15 | = submit_tag l :button_save
16 | '
17 | = link_to_function l(:button_cancel), 'hideModal(this);'
18 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_handlers/repositories/update_repository.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteHandlers
5 | module Repositories
6 | class UpdateRepository < Base
7 | def call
8 | if configuration_exists?
9 | log_ok_and_continue 'update it ...'
10 |
11 | # Update Gitolite repository
12 | update_repository_config
13 | else
14 | log_repo_not_exist 'exit !'
15 | end
16 | end
17 |
18 | def gitolite_repo_name
19 | repository.gitolite_repository_name
20 | end
21 |
22 | def gitolite_repo_path
23 | repository.gitolite_repository_path
24 | end
25 | end
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/app/views/repositories/_git_hosting_sidebar.html.slim:
--------------------------------------------------------------------------------
1 | ul.repository.git
2 | - @repositories.sort.each do |repo|
3 | li class="#{'repository git' if repo.is_a? Repository::Xitolite}"
4 | = link_to_repository repo, @repository
5 | - if User.current.allowed_to? :manage_repository, @project
6 | '
7 | = link_to "(#{l :label_settings})", edit_repository_path(repo)
8 |
9 | - if @repository.try(:watchers) && \
10 | (User.current.allowed_to?(:add_repository_xitolite_watchers, @project) || \
11 | (@repository.watchers.present? && User.current.allowed_to?(:view_repository_xitolite_watchers, @project)))
12 |
13 | #watchers
14 | = render 'watchers/watchers', watched: @repository
15 |
16 | javascript:
17 | $(document).ready(function() { $('#sidebar p').remove(); });
18 |
--------------------------------------------------------------------------------
/db/migrate/20140417004100_enforce_models_constraints.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class EnforceModelsConstraints < ActiveRecord::Migration[4.2]
4 | def up
5 | change_column :git_caches, :command_output, :binary, limit: 16_777_216
6 | remove_column :repository_mirrors, :created_at
7 | remove_column :repository_mirrors, :updated_at
8 | remove_column :repository_post_receive_urls, :created_at
9 | remove_column :repository_post_receive_urls, :updated_at
10 | end
11 |
12 | def down
13 | add_column :repository_mirrors, :created_at, :datetime
14 | add_column :repository_mirrors, :updated_at, :datetime
15 | add_column :repository_post_receive_urls, :created_at, :datetime
16 | add_column :repository_post_receive_urls, :updated_at, :datetime
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/use_cases/projects/update.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Projects
4 | class Update < Base
5 | def call
6 | # Adjust daemon status
7 | disable_git_daemon_if_not_public
8 | resync
9 | end
10 |
11 | private
12 |
13 | def disable_git_daemon_if_not_public
14 | # Go through all gitolite repos and disable Git daemon if necessary
15 | project.gitolite_repos.each do |repository|
16 | repository.extra[:git_daemon] = false if repository.git_daemon_enabled? && !project.is_public
17 | # Save GitExtra in all cases to trigger urls order consistency checks
18 | repository.extra.save
19 | end
20 | end
21 |
22 | def resync
23 | gitolite_accessor.update_projects [project.id], **options
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/patches/member_patch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_dependency 'member'
4 |
5 | module RedmineGitHosting
6 | module Patches
7 | module MemberPatch
8 | include RedmineGitHosting::GitoliteAccessor::Methods
9 |
10 | def self.prepended(base)
11 | base.class_eval do
12 | after_commit :update_project
13 | end
14 | end
15 |
16 | private
17 |
18 | def update_project
19 | gitolite_accessor.update_projects [project.id],
20 | message: "Membership changes on project '#{project}', update!"
21 | end
22 | end
23 | end
24 | end
25 |
26 | Member.prepend RedmineGitHosting::Patches::MemberPatch unless Member.included_modules.include? RedmineGitHosting::Patches::MemberPatch
27 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/plugins/sweepers/base_sweeper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting::Plugins::Sweepers
4 | class BaseSweeper < RedmineGitHosting::Plugins::GitolitePlugin
5 | attr_reader :repository_data, :gitolite_repo_name, :gitolite_repo_path, :delete_repository, :git_cache_id
6 |
7 | def initialize(repository_data, _options = {})
8 | @repository_data = repository_data
9 | @gitolite_repo_name = repository_data[:repo_name]
10 | @gitolite_repo_path = repository_data[:repo_path]
11 | @delete_repository = repository_data[:delete_repository]
12 | @git_cache_id = repository_data[:git_cache_id]
13 | end
14 |
15 | private
16 |
17 | def delete_repository?
18 | RedminePluginKit.true? delete_repository
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/.github/workflows/brakeman.yml:
--------------------------------------------------------------------------------
1 | name: Run Brakeman
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | build:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v3
14 |
15 | - name: Install package dependencies
16 | run: >
17 | sudo apt-get install --yes --quiet
18 | pandoc
19 |
20 | - name: Setup Gemfile
21 | run: |
22 | touch .enable_dev
23 | sed -i "3isource 'https://rubygems.org'" Gemfile
24 |
25 | - name: Setup Ruby
26 | uses: ruby/setup-ruby@v1
27 | with:
28 | ruby-version: 3.1
29 | bundler-cache: true
30 |
31 | - name: Setup gems
32 | run: |
33 | bundle install --jobs 4 --retry 3
34 |
35 | - name: Run Brakeman
36 | run: |
37 | bundle exec brakeman -5
38 |
--------------------------------------------------------------------------------
/app/services/redmine_hooks/fetch_changesets.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineHooks
4 | class FetchChangesets < Base
5 | def call
6 | repository.empty_cache!
7 | execute_hook do |out|
8 | out << fetch_changesets
9 | end
10 | end
11 |
12 | def repository
13 | object
14 | end
15 |
16 | def start_message
17 | "Fetching changesets for '#{repository.redmine_name}' repository"
18 | end
19 |
20 | private
21 |
22 | def fetch_changesets
23 | repository.fetch_changesets
24 | log_hook_succeeded
25 | success_message
26 | rescue ::Redmine::Scm::Adapters::CommandFailed => e
27 | log_hook_failed
28 | logger.error "Error during fetching changesets : #{e.message}"
29 | failure_message
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/app/views/repository_git_extras/move.html.slim:
--------------------------------------------------------------------------------
1 | h2 = l :label_move_repository, repo_name: @repository.gitolite_repository_name
2 |
3 | .box
4 | = labelled_form_for :repository_mover, @move_repository_form,
5 | url: move_repository_git_extras_path(@repository),
6 | authenticity_token: form_authenticity_token,
7 | html: { method: :post, class: 'tabular', data: { confirm: l(:text_are_you_sure) } } do |f|
8 |
9 | .flash-messages = error_messages_for 'move_repository_form'
10 | p
11 | = f.select :project_id,
12 | render_options_for_move_repo_select_box(@project),
13 | required: true
14 | p
15 | = f.submit l(:button_move)
16 | '
17 | = link_to l(:button_cancel), settings_project_path(@project, tab: 'repositories')
18 |
--------------------------------------------------------------------------------
/app/use_cases/projects/create_repository.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Projects
4 | class CreateRepository < Base
5 | def call
6 | create_project_repository
7 | end
8 |
9 | private
10 |
11 | def create_project_repository
12 | # Create new repository
13 | repository = Repository.factory 'Xitolite'
14 | repository.is_default = true
15 | repository.extra_info = {}
16 | repository.extra_info['extra_report_last_commit'] = '1'
17 |
18 | # Save it to database
19 | project.repositories << repository
20 |
21 | # Create it in Gitolite
22 | Repositories::Create.call repository, creation_options
23 | end
24 |
25 | def creation_options
26 | { create_readme_file: RedmineGitHosting::Config.init_repositories_on_create? }
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/patches/setting_patch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_dependency 'setting'
4 |
5 | module RedmineGitHosting
6 | module Patches
7 | module SettingPatch
8 | def self.prepended(base)
9 | class << base
10 | prepend ClassMethods
11 | end
12 | end
13 |
14 | module ClassMethods
15 | def check_cache
16 | settings_updated_on = Setting.maximum :updated_on
17 | if settings_updated_on && @cached_cleared_on <= settings_updated_on
18 | clear_cache
19 | RedmineGitHosting::Config.check_cache
20 | end
21 | end
22 | end
23 | end
24 | end
25 | end
26 |
27 | Setting.prepend RedmineGitHosting::Patches::SettingPatch unless Setting.included_modules.include? RedmineGitHosting::Patches::SettingPatch
28 |
--------------------------------------------------------------------------------
/db/migrate/20151127024000_shrink_git_notifications.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ShrinkGitNotifications < ActiveRecord::Migration[4.2]
4 | def up
5 | add_column :repository_git_extras, :notification_sender, :string
6 | add_column :repository_git_extras, :notification_prefix, :string
7 | drop_table :repository_git_notifications
8 | end
9 |
10 | def down
11 | remove_column :repository_git_extras, :notification_sender
12 | remove_column :repository_git_extras, :notification_prefix
13 |
14 | create_table :repository_git_notifications do |t|
15 | t.integer :repository_id
16 | t.text :include_list
17 | t.text :exclude_list
18 | t.string :prefix
19 | t.string :sender_address
20 | end
21 |
22 | add_index :repository_git_notifications, :repository_id, unique: true
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/config/mirroring.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Config
5 | module Mirroring
6 | extend self
7 |
8 | def mirroring_public_key
9 | @mirroring_public_key ||= MirrorKeysInstaller.mirroring_public_key gitolite_ssh_public_key
10 | end
11 |
12 | def mirroring_keys_installed?
13 | @mirroring_keys_installed ||= MirrorKeysInstaller.new(gitolite_home_dir,
14 | gitolite_ssh_public_key,
15 | gitolite_ssh_private_key).installed?
16 | end
17 |
18 | def gitolite_mirroring_script
19 | File.join gitolite_home_dir, '.ssh', 'run_gitolite_admin_ssh'
20 | end
21 | end
22 |
23 | extend Config::Mirroring
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/models/repository_git_config_key/option_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path "#{File.dirname __FILE__}/../../spec_helper"
4 |
5 | describe RepositoryGitConfigKey::Option do
6 | before :each do
7 | @git_config_key = build :repository_git_option_key
8 | end
9 |
10 | subject { @git_config_key }
11 |
12 | ## Validations
13 | it { should be_valid }
14 | it { should validate_presence_of(:key) }
15 | it { should validate_uniqueness_of(:key).case_insensitive.scoped_to(:type, :repository_id) }
16 | it { should allow_value('hookfoo', 'hookfoo.foo', 'hookfoo.foo.bar').for(:key) }
17 |
18 | context 'when key is updated' do
19 | before do
20 | @git_config_key.save
21 | @git_config_key.key = 'hookbar.foo'
22 | @git_config_key.save
23 | end
24 |
25 | it { should be_valid }
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/overrides/repositories/show.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Deface::Override.new virtual_path: 'repositories/show',
4 | name: 'show-repositories-hook-bottom',
5 | insert_before: 'erb[silent]:contains("other_formats_links")',
6 | original: 'f302d110cd10675a0a952f5f3e1ecfe57ebd38be',
7 | text: '<%= call_hook(:view_repositories_show_bottom, repository: @repository) %>'
8 |
9 | Deface::Override.new virtual_path: 'repositories/show',
10 | name: 'show-repositories-hook-sidebar',
11 | insert_before: 'erb[silent]:contains("html_title")',
12 | original: '2a0a09659d76066b896016c72527d479c69463ec',
13 | partial: 'hooks/show_repositories_sidebar'
14 |
15 | module Repositories
16 | module Show
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_handlers/ssh_keys/add_ssh_key.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteHandlers
5 | module SshKeys
6 | class AddSshKey < Base
7 | def call
8 | repo_key = find_gitolite_key key.owner, key.location
9 |
10 | # Add it if not found
11 | if repo_key.nil?
12 | admin.add_key build_gitolite_key(key)
13 | else
14 | logger.info "#{context} : SSH key '#{key.owner}@#{key.location}' already exists in Gitolite, update it ..."
15 | repo_key.type = key.type
16 | repo_key.blob = key.blob
17 | repo_key.email = key.email
18 | repo_key.owner = key.owner
19 | repo_key.location = key.location
20 | end
21 | end
22 | end
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/forms/plugin_settings_validation/cache_config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module PluginSettingsValidation
4 | module CacheConfig
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | # Gitolite Cache Config
9 | add_accessor :gitolite_cache_max_time,
10 | :gitolite_cache_max_size,
11 | :gitolite_cache_max_elements,
12 | :gitolite_cache_adapter
13 |
14 | validates :gitolite_cache_max_time, presence: true, numericality: { only_integer: true }
15 | validates :gitolite_cache_max_size, presence: true, numericality: { only_integer: true }
16 | validates :gitolite_cache_max_elements, presence: true, numericality: { only_integer: true }
17 | validates :gitolite_cache_adapter, presence: true, inclusion: { in: GitCache.adapters }
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/recycle_bin/item.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module RecycleBin
5 | class Item
6 | attr_reader :path
7 |
8 | def initialize(path)
9 | @path = path
10 | end
11 |
12 | def size
13 | RedmineGitHosting::Commands.sudo_get_dir_size path
14 | end
15 |
16 | def destroy!
17 | logger.info "Deleting '#{path}' from Recycle Bin"
18 | begin
19 | RedmineGitHosting::Commands.sudo_rmdir path, force: true
20 | logger.info 'Done !'
21 | rescue RedmineGitHosting::Error::GitoliteCommandException
22 | logger.error "Errors while deleting '#{path}' from Recycle Bin !"
23 | end
24 | end
25 |
26 | private
27 |
28 | def logger
29 | RedmineGitHosting.logger
30 | end
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/app/helpers/repository_deployment_credentials_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RepositoryDeploymentCredentialsHelper
4 | def build_list_of_keys(user_keys, other_keys, disabled_keys)
5 | option_array = [[l(:label_deployment_credential_select_deploy_key), -1]]
6 | option_array += user_keys.map { |key| [keylabel(key), key.id] }
7 |
8 | if other_keys.present?
9 | option_array2 = other_keys.map { |key| [keylabel(key), key.id] }
10 | maxlen = (option_array + option_array2).map { |x| x.first.length }.max
11 |
12 | extra = [maxlen - l(:select_other_keys).length - 2, 6].max / 2
13 | option_array += [[('-' * extra) + ' ' + l(:select_other_keys) + ' ' + ('-' * extra), -2]]
14 | option_array += option_array2
15 | end
16 |
17 | options_for_select(option_array, selected: -1, disabled: [-2] + disabled_keys.map(&:id))
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/views/common/_git_urls.html.slim:
--------------------------------------------------------------------------------
1 | - content_for :header_tags do
2 | = additionals_library_load :clipboardjs
3 |
4 | = stylesheet_link_tag 'git_urls', plugin: 'redmine_git_hosting'
5 | = javascript_include_tag 'git_urls', plugin: 'redmine_git_hosting'
6 |
7 | javascript:
8 | $(function() {
9 | setFirstGitUrl('.git_url_list'); setGitUrls('.git_url');
10 | $('.clipboard-button').tooltip();
11 | })
12 |
13 | - repositories ||= Array.wrap repository
14 | - if repositories.map(&:available_urls_sorted).any?
15 | - repositories.sort_by { |r| r.is_default ? 0 : 1 }.each do |repository|
16 | - next if repository.available_urls_sorted.empty?
17 | - present repository do |p|
18 | .repository-urls
19 | = p.link_to_repository if repositories.count > 1
20 | = p.git_urls_box
21 |
22 | - else
23 | #git_url_box = l :label_repository_access_not_configured
24 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/plugins/extenders/base_extender.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting::Plugins::Extenders
4 | class BaseExtender < RedmineGitHosting::Plugins::GitolitePlugin
5 | attr_reader :repository, :recovered, :gitolite_repo_name, :gitolite_repo_path, :git_default_branch, :options
6 |
7 | def initialize(repository, **options)
8 | @repository = repository
9 | @recovered = options.delete(:recovered) { false }
10 | @gitolite_repo_name = repository.gitolite_repository_name
11 | @gitolite_repo_path = repository.gitolite_repository_path
12 | @git_default_branch = repository.git_default_branch
13 | @options = options
14 | end
15 |
16 | private
17 |
18 | def recovered?
19 | recovered
20 | end
21 |
22 | def installable?
23 | false
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/recycle_bin.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module RecycleBin
5 | extend self
6 |
7 | delegate :content, to: :recycle_bin
8 |
9 | def delete_expired_content(expiration_time = default_expiration_time)
10 | recycle_bin.delete_expired_content expiration_time
11 | end
12 |
13 | def delete_content(content_list = [])
14 | recycle_bin.delete_content content_list
15 | end
16 |
17 | delegate :move_object_to_recycle, to: :recycle_bin
18 |
19 | delegate :restore_object_from_recycle, to: :recycle_bin
20 |
21 | private
22 |
23 | def default_expiration_time
24 | RedmineGitHosting::Config.gitolite_recycle_bin_expiration_time
25 | end
26 |
27 | def recycle_bin
28 | @recycle_bin ||= RecycleBin::Manager.new RedmineGitHosting::Config.recycle_bin_dir
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/repositories/move_repository.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Repositories
6 | class MoveRepository < GitoliteWrappers::Base
7 | def call
8 | if repository.nil?
9 | log_object_dont_exist
10 | else
11 | move_repository
12 | end
13 | end
14 |
15 | def repository
16 | @repository ||= Repository.find_by id: object_id
17 | end
18 |
19 | def move_repository
20 | admin.transaction do
21 | move_gitolite_repository repository
22 | gitolite_admin_repo_commit repository.gitolite_repository_name
23 | end
24 |
25 | # Fetch changeset
26 | repository.fetch_changesets
27 | end
28 | end
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/app/helpers/git_hosting_users_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module GitHostingUsersHelper
4 | def user_settings_tabs
5 | tabs = super
6 | tabs << { name: 'keys', partial: 'gitolite_public_keys/view', label: :label_public_keys }
7 | end
8 |
9 | # Hacked render_api_custom_values to add plugin values to user api.
10 | # @NOTE: there is no solution for index.api, because @user is missing
11 | # @TODO
12 | def render_api_custom_values(custom_values, api)
13 | rc = super
14 |
15 | if @user.present?
16 | api.array :ssh_keys do
17 | @user.gitolite_public_keys.each do |key|
18 | api.ssh_key do
19 | api.id key.id
20 | api.key_type key.key_type_as_string
21 | api.title key.title
22 | api.key key.key
23 | end
24 | end
25 | end
26 | end
27 |
28 | rc
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/console_logger.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module ConsoleLogger
5 | extend self
6 |
7 | def title(message)
8 | info "\n * #{message}:"
9 | yield if block_given?
10 | info " Done!\n\n"
11 | end
12 |
13 | def debug(message)
14 | to_console message
15 | logger.debug message.strip
16 | end
17 |
18 | def info(message)
19 | to_console message
20 | logger.info message.strip
21 | end
22 |
23 | def warn
24 | to_console message
25 | logger.warn message.strip
26 | end
27 |
28 | def error(message)
29 | to_console message
30 | logger.error message.strip
31 | end
32 |
33 | private
34 |
35 | def to_console(message)
36 | puts message
37 | end
38 |
39 | def logger
40 | RedmineGitHosting.logger
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/error.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Error
5 | # Used to register errors when pulling and pushing the conf file
6 | class GitoliteException < StandardError; end
7 |
8 | class GitoliteWrapperException < GitoliteException; end
9 |
10 | class InvalidSshKey < GitoliteException; end
11 |
12 | class InvalidRefspec < GitoliteException
13 | class BadFormat < InvalidRefspec; end
14 |
15 | class NullComponent < InvalidRefspec; end
16 | end
17 |
18 | # Used to register errors when pulling and pushing the conf file
19 | class GitoliteCommandException < GitoliteException
20 | attr_reader :command, :output
21 |
22 | def initialize(command, output)
23 | super()
24 | @command = command
25 | @output = output
26 | end
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/factories/repository_git_config_key.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :repository_git_config_key_base, class: 'RepositoryGitConfigKey' do
5 | sequence(:key) { |n| "hookfoo.foo#{n}" }
6 | value { 'bar' }
7 | association :repository, factory: :repository_gitolite
8 | end
9 |
10 | factory :repository_git_config_key, class: 'RepositoryGitConfigKey::GitConfig' do
11 | sequence(:key) { |n| "hookfoo.foo#{n}" }
12 | value { 'bar' }
13 | type { 'RepositoryGitConfigKey::GitConfig' }
14 | association :repository, factory: :repository_gitolite
15 | end
16 |
17 | factory :repository_git_option_key, class: 'RepositoryGitConfigKey::Option' do
18 | sequence(:key) { |n| "hookfoo.foo#{n}" }
19 | value { 'bar' }
20 | type { 'RepositoryGitConfigKey::Option' }
21 | association :repository, factory: :repository_gitolite
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/models/repository_git_config_key/git_config_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path "#{File.dirname __FILE__}/../../spec_helper"
4 |
5 | describe RepositoryGitConfigKey::GitConfig do
6 | before :each do
7 | @git_config_key = build :repository_git_config_key
8 | end
9 |
10 | subject { @git_config_key }
11 |
12 | ## Validations
13 | it { should be_valid }
14 | it { should validate_presence_of(:key) }
15 | it { should validate_uniqueness_of(:key).case_insensitive.scoped_to(:type, :repository_id) }
16 | it { should allow_value('hookfoo.foo', 'hookfoo.foo.bar').for(:key) }
17 | it { should_not allow_value('hookfoo').for(:key) }
18 |
19 | context 'when key is updated' do
20 | before do
21 | @git_config_key.save
22 | @git_config_key.key = 'hookbar.foo'
23 | @git_config_key.save
24 | end
25 |
26 | it { should be_valid }
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/app/views/repositories/edit.html.slim:
--------------------------------------------------------------------------------
1 | .contextual
2 | = render_repository_quick_jump @repository
3 |
4 | h2
5 | = link_to l(:label_settings), settings_project_path(@project, tab: 'repositories')
6 | '
7 | = Additionals::LIST_SEPARATOR
8 | = l :label_repository
9 | '
10 | = Additionals::LIST_SEPARATOR
11 | = @repository.redmine_name
12 |
13 | .splitcontent
14 | .splitcontentleft
15 | = call_hook :view_repository_edit_top, repository: @repository, project: @project
16 |
17 | = labelled_form_for :repository, @repository, url: repository_path(@repository), html: { method: :put, id: 'repository-form' } do |f|
18 | = render partial: 'form', locals: { f: f }
19 |
20 | .splitcontentright
21 | #xitolite-options style='vertical-align: top;'
22 | = render 'xitolite_options', repository: @repository
23 |
24 | = call_hook :view_repository_edit_bottom, repository: @repository, project: @project
25 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/projects/move_repositories.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Projects
6 | class MoveRepositories < GitoliteWrappers::Base
7 | include Common
8 |
9 | def call
10 | return if git_projects.empty?
11 |
12 | admin.transaction do
13 | @delete_parent_path = []
14 | @delete_parent_path += handle_repositories_move git_projects
15 | # Remove empty directories
16 | clean_path @delete_parent_path
17 | end
18 | end
19 |
20 | def git_projects
21 | @git_projects ||= projects.uniq.select { |p| p.gitolite_repos.any? }
22 | end
23 |
24 | def projects
25 | @projects ||= Project.find_by(id: object_id).self_and_descendants
26 | end
27 | end
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/app/models/concerns/gitolitable/notifications.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Gitolitable
4 | module Notifications
5 | extend ActiveSupport::Concern
6 |
7 | def mailing_list
8 | default_list + global_include_list - global_exclude_list
9 | end
10 |
11 | def default_list
12 | watcher_users.map(&:email_address).map(&:address)
13 | end
14 |
15 | def global_include_list
16 | RedmineGitHosting::Config.gitolite_notify_global_include
17 | end
18 |
19 | def global_exclude_list
20 | RedmineGitHosting::Config.gitolite_notify_global_exclude
21 | end
22 |
23 | def sender_address
24 | extra.notification_sender.presence || RedmineGitHosting::Config.gitolite_notify_global_sender_address
25 | end
26 |
27 | def email_prefix
28 | extra.notification_prefix.presence || RedmineGitHosting::Config.gitolite_notify_global_prefix
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/.github/workflows/rubocop.yml:
--------------------------------------------------------------------------------
1 | name: Run RuboCop
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | build:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v3
14 |
15 | - name: Install package dependencies
16 | run: >
17 | sudo apt-get install --yes --quiet
18 | pandoc
19 |
20 | - name: Setup Gemfile
21 | run: |
22 | touch .enable_dev
23 | sed -i "3isource 'https://rubygems.org'" Gemfile
24 |
25 | - name: Setup Ruby
26 | uses: ruby/setup-ruby@v1
27 | with:
28 | ruby-version: 3.1
29 | bundler-cache: true
30 |
31 | - name: Setup gems
32 | run: |
33 | bundle install --jobs 4 --retry 3
34 |
35 | - name: Run RuboCop
36 | run: |
37 | bundle exec rubocop -S
38 |
39 | - name: Run Slim-Lint
40 | run: |
41 | bundle exec slim-lint app/views
42 | if: always()
43 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/journal_logger.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | # @see https://github.com/theforeman/journald-logger
5 | if defined? ::Journald::Logger
6 | class JournalLogger < ::Journald::Logger
7 | def self.init_logs!(progname, loglevel)
8 | logger = new progname, type: progname
9 | logger.level = loglevel
10 |
11 | logger
12 | end
13 |
14 | def debug(msg)
15 | super msg2str(msg)
16 | end
17 |
18 | def info(msg)
19 | super msg2str(msg)
20 | end
21 |
22 | def warn(msg)
23 | super msg2str(msg)
24 | end
25 |
26 | def error(msg)
27 | super msg2str(msg)
28 | end
29 |
30 | def msg2str(msg)
31 | case msg
32 | when ::String
33 | msg
34 | else
35 | msg.inspect
36 | end
37 | end
38 | end
39 | else
40 | module JournalLogger
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/app/helpers/git_hosting_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module GitHostingHelper
4 | def present(object, klass = nil, *args)
5 | klass ||= "#{object.class.base_class}Presenter".constantize
6 | presenter = klass.new(object, self, *args)
7 | yield presenter if block_given?
8 | presenter
9 | end
10 |
11 | def checked_image_with_exclamation(checked:)
12 | checked ? image_tag('toggle_check.png') : image_tag('exclamation.png')
13 | end
14 |
15 | def render_shell_text(text)
16 | Redmine::SyntaxHighlighting.highlight_by_language text, 'shell'
17 | end
18 |
19 | def gitolite_project_settings_tabs
20 | tabs = []
21 |
22 | tabs << { name: 'db',
23 | action: :show,
24 | partial: 'projects/settings/db',
25 | label: :label_db }
26 |
27 | tabs << { name: 'db2',
28 | action: :show,
29 | partial: 'projects/settings/db',
30 | label: :label_db }
31 | tabs
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/app/views/dashboards/blocks/_git_urls.html.slim:
--------------------------------------------------------------------------------
1 | - if @project.module_enabled?(:repository) && \
2 | @project.repository.is_a?(Repository::Xitolite) && \
3 | User.current.allowed_to?(:view_changesets, @project)
4 | .git_hosting
5 | h3
6 | = @project.repositories.count > 1 ? l(:label_repository_plural) : l(:label_repository)
7 | = render partial: 'common/git_urls', locals: { repositories: @project.gitolite_repos }
8 | - if @project.repositories.count > 1
9 | .clear-both
10 | p
11 | = link_to l(:label_see_other_repositories), { controller: '/repositories',
12 | action: 'show',
13 | id: @project,
14 | repository_id: @project.repository.identifier_param,
15 | rev: nil,
16 | path: nil }
17 |
--------------------------------------------------------------------------------
/app/views/users/index.api.rsb:
--------------------------------------------------------------------------------
1 | api.array :users, api_meta(total_count: @user_count, offset: @offset, limit: @limit) do
2 | @users.each do |user|
3 | api.user do
4 | api.id user.id
5 | api.login user.login
6 | api.admin user.admin?
7 | api.firstname user.firstname
8 | api.lastname user.lastname
9 | api.mail user.mail
10 | api.created_on user.created_on
11 | api.updated_on user.updated_on
12 | api.last_login_on user.last_login_on
13 | api.passwd_changed_on user.passwd_changed_on
14 |
15 | api.array :ssh_keys do
16 | user.gitolite_public_keys.each do |key|
17 | api.ssh_key do
18 | api.id key.id
19 | api.key_type key.key_type_as_string
20 | api.title key.title
21 | api.key key.key
22 | end
23 | end
24 | end
25 |
26 | render_api_custom_values user.visible_custom_field_values, api
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/patches/dashboard_content_project_patch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Patches
5 | module DashboardContentProjectPatch
6 | extend ActiveSupport::Concern
7 |
8 | included do
9 | prepend InstanceOverwriteMethods
10 | end
11 |
12 | module InstanceOverwriteMethods
13 | def block_definitions
14 | blocks = super
15 |
16 | blocks['giturls'] = { label: l(:label_repository_url_plural),
17 | permission: :manage_repository,
18 | no_settings: true,
19 | partial: 'dashboards/blocks/git_urls' }
20 |
21 | blocks
22 | end
23 | end
24 | end
25 | end
26 | end
27 |
28 | if DashboardContentProject.included_modules.exclude? RedmineGitHosting::Patches::DashboardContentProjectPatch
29 | DashboardContentProject.include RedmineGitHosting::Patches::DashboardContentProjectPatch
30 | end
31 |
--------------------------------------------------------------------------------
/app/views/repository_mirrors/_form.html.slim:
--------------------------------------------------------------------------------
1 | .flash-messages = error_messages_for 'mirror'
2 |
3 | .box
4 | p = f.text_field :url, required: true, size: 65
5 | em
6 | p
7 | = l :label_mirror_url_accepted_format
8 | ' :
9 | br
10 | | ssh://git@redmine.example.org/project1/project2/project3/project4.git
11 | br
12 | | ssh://git@redmine.example.org:22/project1/project2/project3/project4.git
13 |
14 | p = f.check_box :active
15 | p = f.select :push_mode,
16 | options_for_select(mirrors_options, @mirror.push_mode),
17 | { label: :label_mirror_push_mode },
18 | onchange: 'push_mode_change(this); return false;'
19 |
20 | #ref_spec_options style="#{'display: none;' if @mirror.mirror_mode?}"
21 | p = f.check_box :include_all_branches, label: :label_mirror_include_all_branches
22 | p = f.check_box :include_all_tags, label: :label_mirror_include_all_tags
23 | p = f.text_field :explicit_refspec, label: :label_mirror_explicit_refspec, size: 65
24 |
--------------------------------------------------------------------------------
/db/migrate/20150823030100_migrate_protected_branches_users.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RepositoryProtectedBrancheWrapped < RepositoryProtectedBranche
4 | serialize :user_list, Array
5 | end
6 |
7 | class MigrateProtectedBranchesUsers < ActiveRecord::Migration[4.2]
8 | def up
9 | RepositoryProtectedBrancheWrapped.all.each do |protected_branch|
10 | users = protected_branch.user_list.map { |u| User.find_by login: u }.compact.uniq
11 | manager = RepositoryProtectedBranches::MemberManager.new protected_branch
12 | manager.add_users(users.map(&:id))
13 | end
14 | remove_column :repository_protected_branches, :user_list
15 | end
16 |
17 | def down
18 | add_column :repository_protected_branches, :user_list, :text, after: :permissions
19 | RepositoryProtectedBrancheWrapped.all.each do |protected_branch|
20 | users = protected_branch.users.map(&:login).compact.uniq
21 | protected_branch.user_list = users
22 | protected_branch.save!
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/repositories/delete_repository.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Repositories
6 | class DeleteRepository < GitoliteWrappers::Base
7 | def call
8 | if repository.present?
9 | delete_repository
10 | else
11 | log_object_dont_exist
12 | end
13 | end
14 |
15 | def repository
16 | @repository ||= object_id.symbolize_keys
17 | end
18 |
19 | def delete_repository
20 | admin.transaction do
21 | delete_gitolite_repository repository
22 | gitolite_admin_repo_commit repository[:repo_name]
23 | end
24 |
25 | # Call Gitolite plugins
26 | logger.info 'Execute Gitolite Plugins'
27 |
28 | # Move repository to RecycleBin
29 | RedmineGitHosting::Plugins.execute :post_delete, repository
30 | end
31 | end
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/app/views/settings/redmine_git_hosting/_gitolite_recycle_bin.html.slim:
--------------------------------------------------------------------------------
1 | h3 = l :label_empty_recycle_bin
2 |
3 | - if !RedmineGitHosting::RecycleBin || !RedmineGitHosting::RecycleBin.content.empty?
4 | table.list
5 | tr
6 | th = l :label_repository
7 | th = l :label_recycle_bin_content_size
8 | th
9 | = l :label_delete_from_recyle_bin
10 | | (
11 | = link_to l(:label_select_all), 'javascript:void(0);', id: 'select_all_delete'
12 | | )
13 |
14 | - RedmineGitHosting::RecycleBin.content.each do |trash_object|
15 | tr
16 | td = trash_object.path
17 | td = trash_object.size
18 | td = check_box_tag 'settings[rescue][delete_trash_repo][]', trash_object.path, false, class: 'empty_trash'
19 |
20 | #delete_warning.alert.alert-error style='display: none;'
21 | = l :label_delete_warning
22 |
23 | - else
24 | p.nodata style='padding: 5px 0 0;'
25 | = l :label_no_data
26 |
27 | javascript:
28 | $(document).ready(function() { setRecycleBinWarnings(); });
29 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/plugins/sweepers/repository_deletor.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting::Plugins::Sweepers
4 | class RepositoryDeletor < BaseSweeper
5 | def post_delete
6 | # Delete hook param if needed
7 | move_repository_to_recycle if delete_repository?
8 | remove_git_cache
9 | end
10 |
11 | private
12 |
13 | def move_repository_to_recycle
14 | if repository_data.is_a? Hash
15 | RedmineGitHosting::RecycleBin.move_object_to_recycle repository_data[:repo_name], repository_data[:repo_path]
16 | elsif repository_data.is_a? Array
17 | repository_data.each do |object_data|
18 | RedmineGitHosting::RecycleBin.move_object_to_recycle object_data[:repo_name], object_data[:repo_path]
19 | end
20 | end
21 | end
22 |
23 | def remove_git_cache
24 | logger.info "Clean cache for repository '#{gitolite_repo_name}'"
25 | RedmineGitHosting::Cache.clear_cache_for_repository git_cache_id
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/plugins/extenders/config_key_deletor.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting::Plugins::Extenders
4 | class ConfigKeyDeletor < BaseExtender
5 | attr_reader :delete_git_config_key
6 |
7 | def initialize(repository, **options)
8 | super(repository, **options)
9 | @delete_git_config_key = options.delete(:delete_git_config_key) { '' }
10 | end
11 |
12 | def post_update
13 | # Delete hook param if needed
14 | delete_hook_param if delete_git_config_key.present?
15 | end
16 |
17 | private
18 |
19 | def delete_hook_param
20 | sudo_git 'config', '--local', '--unset', delete_git_config_key
21 | rescue RedmineGitHosting::Error::GitoliteCommandException
22 | logger.error "Error while deleting Git config key '#{delete_git_config_key}' for repository '#{gitolite_repo_name}'"
23 | else
24 | logger.info "Git config key '#{delete_git_config_key}' successfully deleted for repository '#{gitolite_repo_name}'"
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/contrib/hooks/post-receive/mail_notifications.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 |
3 | import sys
4 | import os
5 |
6 | import git_multimail
7 |
8 |
9 | # It is possible to modify the output templates here; e.g.:
10 |
11 | git_multimail.FOOTER_TEMPLATE = """\
12 |
13 | -- \n\
14 | This email was generated by the wonderful git-multimail tool from JBox Web.
15 | """
16 |
17 |
18 | # Specify which "git config" section contains the configuration for
19 | # git-multimail:
20 | config = git_multimail.Config('multimailhook')
21 |
22 | # check if hook is enabled
23 | enabled = config.get_bool('enabled')
24 |
25 | if enabled:
26 | # Select the type of environment:
27 | environment = git_multimail.GitoliteEnvironment(config=config)
28 |
29 | # Choose the method of sending emails based on the git config:
30 | mailer = git_multimail.choose_mailer(config, environment)
31 |
32 | # Read changes from stdin and send notification emails:
33 | git_multimail.run_as_post_receive_hook(environment, mailer)
34 | else:
35 | print(" multimailhook is disabled")
36 |
--------------------------------------------------------------------------------
/db/migrate/20120708070841_add_settings_to_plugin_4.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddSettingsToPlugin4 < ActiveRecord::Migration[4.2]
4 | def up
5 | # Add some new settings to settings page, if they don't exist
6 | valuehash = Setting.plugin_redmine_git_hosting.clone
7 | valuehash['gitForceHooksUpdate'] ||= 'true'
8 |
9 | if Setting.plugin_redmine_git_hosting != valuehash
10 | say 'Added redmine_git_hosting settings: gitForceHooksUpdate'
11 | Setting.plugin_redmine_git_hosting = valuehash
12 | end
13 | rescue StandardError => e
14 | say e.message
15 | end
16 |
17 | def down
18 | # Remove above settings from plugin page
19 | valuehash = Setting.plugin_redmine_git_hosting.clone
20 | valuehash.delete 'gitForceHooksUpdate'
21 |
22 | if Setting.plugin_redmine_git_hosting != valuehash
23 | say 'Removed redmine_git_hosting settings: gitForceHooksUpdate'
24 | Setting.plugin_redmine_git_hosting = valuehash
25 | end
26 | rescue StandardError => e
27 | say e.message
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/db/migrate/20110817000000_move_notified_cia_to_git_cia_notifications.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class MoveNotifiedCiaToGitCiaNotifications < ActiveRecord::Migration[4.2]
4 | def up
5 | drop_table :git_cia_notifications if table_exists? :git_cia_notifications
6 |
7 | create_table :git_cia_notifications do |t|
8 | t.column :repository_id, :integer
9 | t.column :scmid, :string
10 | end
11 |
12 | # Speed up searches
13 | add_index :git_cia_notifications, :scmid
14 |
15 | # Make sure uniqueness of the two columns, :scmid, :repository_id
16 | add_index :git_cia_notifications, %i[scmid repository_id], unique: true
17 |
18 | remove_column :changesets, :notified_cia if column_exists? :changesets, :notified_cia
19 | end
20 |
21 | def down
22 | drop_table :git_cia_notifications
23 | end
24 |
25 | def table_exists?(name)
26 | ActiveRecord::Base.connection.tables.include? name
27 | end
28 |
29 | def column_exists?(table_name, column_name)
30 | columns(table_name).any? { |c| c.name == column_name.to_s }
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/patches/grack_server_patch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'grack/server'
4 |
5 | module RedmineGitHosting
6 | module Patches
7 | module GrackServerPatch
8 | # Override original *get_git* method to set the right path for the repository.
9 | # Also pass the *@env['REMOTE_USER']* variable to the Git constructor so we
10 | # can pass it to Gitolite hooks later.
11 | def get_git(path)
12 | path = gitolite_path path
13 | Grack::Git.new @config[:git_path], path, @env['REMOTE_USER']
14 | end
15 |
16 | private
17 |
18 | def gitolite_path(path)
19 | File.join(RedmineGitHosting::Config.gitolite_home_dir,
20 | RedmineGitHosting::Config.gitolite_global_storage_dir,
21 | RedmineGitHosting::Config.gitolite_redmine_storage_dir, path)
22 | end
23 | end
24 | end
25 | end
26 |
27 | unless Grack::Server.included_modules.include? RedmineGitHosting::Patches::GrackServerPatch
28 | Grack::Server.prepend RedmineGitHosting::Patches::GrackServerPatch
29 | end
30 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/recycle_bin/item_base.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module RecycleBin
5 | module ItemBase
6 | TRASH_DIR_SEP = '__'
7 |
8 | attr_reader :object_name, :recycle_bin_dir
9 |
10 | def initialize(recycle_bin_dir, object_name)
11 | @recycle_bin_dir = recycle_bin_dir
12 | @object_name = object_name
13 | end
14 |
15 | def trash_name
16 | object_name.gsub %r{/}, TRASH_DIR_SEP
17 | end
18 |
19 | private
20 |
21 | def logger
22 | RedmineGitHosting.logger
23 | end
24 |
25 | def directory_exists?(dir)
26 | RedmineGitHosting::Commands.sudo_dir_exists? dir
27 | end
28 |
29 | def find_trashed_object(regex)
30 | RedmineGitHosting::Commands.sudo_capture('find', recycle_bin_dir, '-type', 'd', '-regex', regex, '-prune', '-print')
31 | .chomp
32 | .split("\n")
33 | .sort.reverse
34 | end
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/assets/javascripts/plugin.js:
--------------------------------------------------------------------------------
1 | /*
2 | REDMINE PLUGIN LIST VIEW OVERRIDE
3 | */
4 | function openAuthorModalBox(element) {
5 | $('#ajax-modal').dialog({
6 | resizable: false,
7 | autoOpen: false,
8 | height: 'auto',
9 | width: 'auto',
10 | modal: true,
11 | hide: {
12 | effect: "fade",
13 | duration: 500
14 | },
15 | buttons: { Ok: function(){ $(this).dialog('close'); } }
16 | });
17 |
18 | var title = $(element).html();
19 | $.get($(element).attr('href'), function(data){
20 | $('#ajax-modal').html(data);
21 | $('#ajax-modal').dialog('option', 'title', title);
22 | $('#ajax-modal').dialog('open');
23 | });
24 | }
25 |
26 | function enhanceAuthorsUrlForPlugin(plugin_name) {
27 | var link = $('#plugin-' + plugin_name + ' > td.author > a');
28 | if (link.length) {
29 | link.addClass('modal-box');
30 | $(document).on('click', 'a.modal-box', function(e){
31 | e.preventDefault();
32 | openAuthorModalBox(this);
33 | });
34 | }
35 | }
36 |
37 | $(document).ready(function() {
38 | enhanceAuthorsUrlForPlugin('redmine_git_hosting');
39 | });
40 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_handlers/ssh_keys/base.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteHandlers
5 | module SshKeys
6 | class Base
7 | attr_reader :admin, :key, :context
8 |
9 | def initialize(admin, key, context)
10 | @admin = admin
11 | @key = key
12 | @context = context
13 | end
14 |
15 | class << self
16 | def call(admin, key, context)
17 | new(admin, key, context).call
18 | end
19 | end
20 |
21 | def call
22 | raise NotImplementedError
23 | end
24 |
25 | private
26 |
27 | def logger
28 | RedmineGitHosting.logger
29 | end
30 |
31 | def find_gitolite_key(owner, location)
32 | admin.ssh_keys[owner].find { |k| k.location == location && k.owner == owner }
33 | end
34 |
35 | def build_gitolite_key(key)
36 | ::Gitolite::SSHKey.new key.type, key.blob, key.email, key.owner, key.location
37 | end
38 | end
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_hooks.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteHooks
5 | class << self
6 | def register_hooks(&block)
7 | @gitolite_hooks ||= []
8 | class_eval(&block)
9 | end
10 |
11 | def registered_hooks
12 | @gitolite_hooks
13 | end
14 |
15 | def source_dir(source_dir)
16 | @source_dir = source_dir
17 | end
18 |
19 | def hooks_installed?
20 | installed = {}
21 | registered_hooks.each do |hook|
22 | installed[hook.name] = hook.installed?
23 | rescue StandardError
24 | installed[hook.name] = false
25 | end
26 | installed
27 | end
28 |
29 | def install_hooks!
30 | installed = {}
31 | registered_hooks.each do |hook|
32 | installed[hook.name] = hook.install!
33 | end
34 | installed
35 | end
36 |
37 | def gitolite_hook(&block)
38 | @gitolite_hooks << RedmineGitHosting::GitoliteHook.new(@source_dir, &block)
39 | end
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/patches/group_patch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_dependency 'group'
4 |
5 | module RedmineGitHosting
6 | module Patches
7 | module GroupPatch
8 | def self.prepended(base)
9 | base.class_eval do
10 | # Relations
11 | has_many :protected_branches_members, dependent: :destroy, foreign_key: :principal_id
12 | has_many :protected_branches, through: :protected_branches_members
13 | end
14 | end
15 |
16 | def user_added(user)
17 | super
18 | protected_branches.each do |pb|
19 | RepositoryProtectedBranches::MemberManager.new(pb).add_user_from_group(user, id)
20 | end
21 | end
22 |
23 | def user_removed(user)
24 | super
25 | protected_branches.each do |pb|
26 | RepositoryProtectedBranches::MemberManager.new(pb).remove_user_from_group(user, id)
27 | end
28 | end
29 | end
30 | end
31 | end
32 |
33 | Group.prepend RedmineGitHosting::Patches::GroupPatch unless Group.included_modules.include? RedmineGitHosting::Patches::GroupPatch
34 |
--------------------------------------------------------------------------------
/contrib/scripts/redmine:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | ### BEGIN INIT INFO
3 | # Provides: redmine
4 | # Required-Start: $local_fs $remote_fs $network $mysql $named
5 | # Required-Stop: $local_fs $remote_fs $network $mysql $named
6 | # Default-Start: 2 3 4 5
7 | # Default-Stop: 0 1 6
8 | # Short-Description: Redmine projects manager
9 | # Description: This file should be used to start and stop Redmine.
10 | ### END INIT INFO
11 |
12 | [ -f /etc/default/rcS ] && . /etc/default/rcS
13 | . /lib/lsb/init-functions
14 |
15 | REDMINE_USER="redmine"
16 |
17 | WEBSERVER="server_puma"
18 | WORKER1="sidekiq_git_hosting"
19 |
20 | case "$1" in
21 | start)
22 | su - $REDMINE_USER -c "${WEBSERVER}.sh start"
23 | su - $REDMINE_USER -c "${WORKER1}.sh start"
24 | ;;
25 | stop)
26 | su - $REDMINE_USER -c "${WEBSERVER}.sh stop"
27 | su - $REDMINE_USER -c "${WORKER1}.sh stop"
28 | ;;
29 | restart)
30 | su - $REDMINE_USER -c "${WEBSERVER}.sh restart"
31 | su - $REDMINE_USER -c "${WORKER1}.sh restart"
32 | ;;
33 | *)
34 | echo "Usage : /etc/init.d/redmine {start|stop|restart}"
35 | ;;
36 | esac
37 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/projects/move_repositories_tree.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Projects
6 | class MoveRepositoriesTree < GitoliteWrappers::Base
7 | include Common
8 |
9 | # Move repositories tree in a single transaction
10 | #
11 | def call
12 | admin.transaction do
13 | @delete_parent_path = []
14 | projects.each do |project|
15 | # Only take projects that have Git repos.
16 | git_projects = project.self_and_descendants.uniq.select { |p| p.gitolite_repos.any? }
17 | next if git_projects.empty?
18 |
19 | @delete_parent_path += handle_repositories_move git_projects
20 | end
21 | # Remove empty directories
22 | clean_path @delete_parent_path
23 | end
24 | end
25 |
26 | def projects
27 | @projects ||= Project.includes(:repositories).all.select { |x| x.parent_id.nil? }
28 | end
29 | end
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/app/forms/plugin_settings_validation/storage_config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module PluginSettingsValidation
4 | module StorageConfig
5 | extend ActiveSupport::Concern
6 |
7 | PATHS_TO_VALIDATE = %i[gitolite_global_storage_dir gitolite_redmine_storage_dir gitolite_recycle_bin_dir].freeze
8 |
9 | included do
10 | # Gitolite Storage Config
11 | add_accessor :gitolite_global_storage_dir,
12 | :gitolite_redmine_storage_dir,
13 | :gitolite_recycle_bin_dir
14 |
15 | before_validation do
16 | self.gitolite_global_storage_dir = strip_value gitolite_global_storage_dir
17 | self.gitolite_redmine_storage_dir = strip_value gitolite_redmine_storage_dir
18 | self.gitolite_recycle_bin_dir = strip_value gitolite_recycle_bin_dir
19 | end
20 |
21 | validates_presence_of :gitolite_global_storage_dir, :gitolite_recycle_bin_dir
22 |
23 | validates_each PATHS_TO_VALIDATE do |record, attr, value|
24 | record.errors.add attr, 'must be relative' if value.starts_with? '/'
25 | end
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/repositories/update_repository.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Repositories
6 | class UpdateRepository < GitoliteWrappers::Base
7 | def call
8 | if repository.nil?
9 | log_object_dont_exist
10 | else
11 | update_repository
12 | end
13 | end
14 |
15 | def repository
16 | @repository ||= Repository.find_by id: object_id
17 | end
18 |
19 | def update_repository
20 | admin.transaction do
21 | update_gitolite_repository repository
22 | gitolite_admin_repo_commit repository.gitolite_repository_name
23 | end
24 |
25 | # Call Gitolite plugins
26 | logger.info 'Execute Gitolite Plugins'
27 |
28 | # Delete Git Config Keys
29 | RedmineGitHosting::Plugins.execute :post_update, repository, **options
30 |
31 | # Fetch changeset
32 | repository.fetch_changesets
33 | end
34 | end
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/app/forms/plugin_settings_validation/hooks_config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module PluginSettingsValidation
4 | module HooksConfig
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | # Gitolite Hooks Config
9 | add_accessor :gitolite_overwrite_existing_hooks,
10 | :gitolite_hooks_are_asynchronous,
11 | :gitolite_hooks_debug,
12 | :gitolite_hooks_url
13 |
14 | before_validation do
15 | self.gitolite_hooks_url = strip_value gitolite_hooks_url
16 | end
17 |
18 | validates :gitolite_overwrite_existing_hooks, presence: true, inclusion: { in: RedmineGitHosting::Validators::BOOLEAN_FIELDS }
19 | validates :gitolite_hooks_are_asynchronous, presence: true, inclusion: { in: RedmineGitHosting::Validators::BOOLEAN_FIELDS }
20 | validates :gitolite_hooks_debug, presence: true, inclusion: { in: RedmineGitHosting::Validators::BOOLEAN_FIELDS }
21 | validates :gitolite_hooks_url, presence: true, format: { with: URI::DEFAULT_PARSER.make_regexp(%w[http https]) }
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/views/repository_git_config_keys/_config_keys.html.slim:
--------------------------------------------------------------------------------
1 | - if git_config_keys.any?
2 | table.list
3 | thead
4 | tr
5 | th = l :label_key
6 | th = l :field_value
7 | th
8 | tbody
9 | - git_config_keys.each do |git_config_key|
10 | tr
11 | td
12 | span.label.label-info = git_config_key.key
13 | td
14 | span.label.label-success = git_config_key.value
15 | td.buttons
16 | - if User.current.git_allowed_to? :edit_repository_git_config_keys, @repository
17 | = link_to l(:button_edit),
18 | edit_repository_git_config_key_path(@repository, git_config_key),
19 | class: 'icon icon-edit'
20 | = link_to l(:button_delete),
21 | repository_git_config_key_path(@repository, git_config_key),
22 | remote: true,
23 | method: :delete,
24 | data: { confirm: l(:text_are_you_sure) },
25 | class: 'icon icon-del'
26 |
27 | - else
28 | p.nodata = l :label_no_data
29 |
--------------------------------------------------------------------------------
/app/forms/move_repository_form.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class MoveRepositoryForm
4 | include BaseForm
5 |
6 | attr_reader :repository
7 | attr_accessor :project_id
8 |
9 | validates_presence_of :project_id
10 | validate :repository_is_movable
11 | validate :target_project
12 | validate :repository_uniqueness
13 |
14 | def initialize(repository)
15 | @repository = repository
16 | end
17 |
18 | def project
19 | @project ||= Project.find_by id: project_id
20 | end
21 |
22 | def valid_form_submitted
23 | repository.update_attribute :project_id, project.id
24 | RedmineGitHosting::GitoliteAccessor.move_repository repository
25 | end
26 |
27 | private
28 |
29 | def repository_is_movable
30 | errors.add :base, :identifier_empty unless repository.movable?
31 | end
32 |
33 | def target_project
34 | errors.add :base, :wrong_target_project if repository.project == project
35 | end
36 |
37 | def repository_uniqueness
38 | new_repo = project.repositories.find_by identifier: repository.identifier
39 | errors.add :base, :identifier_taken unless new_repo.nil?
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/app/services/redmine_hooks/base.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineHooks
4 | class Base
5 | attr_reader :object, :payloads
6 |
7 | def initialize(object, payloads = {})
8 | @object = object
9 | @payloads = payloads
10 | end
11 |
12 | class << self
13 | def call(object, payloads = {})
14 | new(object, payloads).call
15 | end
16 | end
17 |
18 | def call
19 | raise NotImplementedError
20 | end
21 |
22 | def start_message
23 | raise NotImplementedError
24 | end
25 |
26 | private
27 |
28 | def logger
29 | RedmineGitHosting.logger
30 | end
31 |
32 | def success_message
33 | " [success]\n"
34 | end
35 |
36 | def failure_message
37 | " [failure]\n"
38 | end
39 |
40 | def log_hook_succeeded
41 | logger.info 'Succeeded!'
42 | end
43 |
44 | def log_hook_failed
45 | logger.error 'Failed!'
46 | end
47 |
48 | def execute_hook
49 | y = +''
50 | logger.info start_message
51 | y << " - #{start_message} ... "
52 | yield y
53 | y
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/db/migrate/20110813000000_create_git_repository_extras.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateGitRepositoryExtras < ActiveRecord::Migration[4.2]
4 | def up
5 | drop_table :git_repository_extras if table_exists? :git_repository_extras
6 |
7 | create_table :git_repository_extras do |t|
8 | t.column :repository_id, :integer
9 | t.column :git_daemon, :integer, default: 1
10 | t.column :git_http, :integer, default: 1
11 | t.column :notify_cia, :integer, default: 0
12 | t.column :key, :string
13 | end
14 |
15 | drop_table :git_hook_keys if table_exists? 'git_hook_keys'
16 | remove_column :repositories, :git_daemon if column_exists? :repositories, :git_daemon
17 | remove_column :repositories, :git_http if column_exists? :repositories, :git_http
18 | end
19 |
20 | def down
21 | drop_table :git_repository_extras
22 | end
23 |
24 | def table_exists?(name)
25 | ActiveRecord::Base.connection.tables.include? name
26 | end
27 |
28 | def column_exists?(table_name, column_name)
29 | columns(table_name).any? { |c| c.name == column_name.to_s }
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/recycle_bin/deletable_item.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module RecycleBin
5 | class DeletableItem
6 | include RecycleBin::ItemBase
7 |
8 | def move!(source_path)
9 | if directory_exists? source_path
10 | logger.info "Moving '#{object_name}' to Recycle Bin..."
11 | logger.debug "'#{source_path}' => '#{target_path}'"
12 | do_move source_path
13 | else
14 | logger.warn "Source directory does not exist '#{source_path}', exiting!"
15 | false
16 | end
17 | end
18 |
19 | def target_path
20 | @target_path ||= File.join recycle_bin_dir, "#{Time.now.to_i}#{TRASH_DIR_SEP}#{trash_name}.git"
21 | end
22 |
23 | private
24 |
25 | def do_move(source_path)
26 | RedmineGitHosting::Commands.sudo_move source_path, target_path
27 | logger.info 'Done !'
28 | true
29 | rescue RedmineGitHosting::Error::GitoliteCommandException
30 | logger.error "Attempt to move '#{source_path}' to Recycle Bin failed!"
31 | false
32 | end
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/patches/roles_controller_patch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_dependency 'roles_controller'
4 |
5 | module RedmineGitHosting
6 | module Patches
7 | module RolesControllerPatch
8 | include RedmineGitHosting::GitoliteAccessor::Methods
9 |
10 | def create
11 | super
12 | call_gitolite 'created'
13 | end
14 |
15 | def update
16 | super
17 | call_gitolite 'modified'
18 | end
19 |
20 | def destroy
21 | super
22 | call_gitolite 'deleted'
23 | end
24 |
25 | def permissions
26 | super
27 | call_gitolite 'modified' if request.post?
28 | end
29 |
30 | private
31 |
32 | def call_gitolite(message)
33 | options = { message: "Role has been #{message}, resync all projects (active or closed)..." }
34 | gitolite_accessor.update_projects 'active_or_closed', **options
35 | end
36 | end
37 | end
38 | end
39 |
40 | unless RolesController.included_modules.include? RedmineGitHosting::Patches::RolesControllerPatch
41 | RolesController.prepend RedmineGitHosting::Patches::RolesControllerPatch
42 | end
43 |
--------------------------------------------------------------------------------
/db/migrate/20120724211806_add_settings_to_plugin_5.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddSettingsToPlugin5 < ActiveRecord::Migration[4.2]
4 | def up
5 | # Add some new settings to settings page, if they don't exist
6 | valuehash = Setting.plugin_redmine_git_hosting.clone
7 | valuehash['gitConfigFile'] ||= 'gitolite.conf'
8 | valuehash['gitConfigHasAdminKey'] ||= 'true'
9 |
10 | if Setting.plugin_redmine_git_hosting != valuehash
11 | say 'Added redmine_git_hosting settings: gitConfigFile, gitConfigHasAdminKey'
12 | Setting.plugin_redmine_git_hosting = valuehash
13 | end
14 | rescue StandardError => e
15 | say e.message
16 | end
17 |
18 | def down
19 | # Remove above settings from plugin page
20 | valuehash = Setting.plugin_redmine_git_hosting.clone
21 | valuehash.delete 'gitConfigFile'
22 | valuehash.delete 'gitConfigHasAdminKey'
23 |
24 | if Setting.plugin_redmine_git_hosting != valuehash
25 | say 'Removed redmine_git_hosting settings: gitConfigFile, gitConfigHasAdminKey'
26 | Setting.plugin_redmine_git_hosting = valuehash
27 | end
28 | rescue StandardError => e
29 | say e.message
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/app/views/repository_deployment_credentials/_new_modal.html.slim:
--------------------------------------------------------------------------------
1 | h3.title = l :label_deployment_credential_add
2 |
3 | = labelled_form_for :repository_deployment_credential, @credential,
4 | url: repository_deployment_credentials_path(@repository),
5 | html: { method: :post, class: 'tabular', remote: true } do |f|
6 |
7 | .flash-messages = error_messages_for 'credential'
8 |
9 | .box
10 | - if @user_keys.present? || @other_keys.present?
11 | p = f.select :gitolite_public_key_id,
12 | build_list_of_keys(@user_keys, @other_keys, @disabled_keys),
13 | required: true,
14 | label: :label_deployment_credential_select_deploy_key
15 | p = f.select :perm,
16 | options_for_select(RepositoryDeploymentCredential::VALID_PERMS, RepositoryDeploymentCredential::DEFAULT_PERM),
17 | required: true,
18 | label: :label_permissions
19 | - else
20 | = link_to l(:label_deployment_credential_create_key_first), public_keys_path
21 |
22 | .buttons
23 | = submit_tag l(:button_add)
24 | '
25 | = link_to_function l(:button_cancel), 'hideModal(this);'
26 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/plugins/extenders/branch_updater.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting::Plugins::Extenders
4 | class BranchUpdater < BaseExtender
5 | attr_reader :update_default_branch
6 |
7 | def initialize(repository, **options)
8 | super(repository, **options)
9 | @update_default_branch = options.delete(:update_default_branch) { false }
10 | end
11 |
12 | def post_update
13 | # Update default branch if needed
14 | do_update_default_branch if update_default_branch?
15 | end
16 |
17 | private
18 |
19 | def update_default_branch?
20 | RedminePluginKit.true? update_default_branch
21 | end
22 |
23 | def do_update_default_branch
24 | sudo_git 'symbolic-ref', 'HEAD', new_default_branch
25 | rescue RedmineGitHosting::GitHosting::GitHostingException
26 | logger.error "Error while updating default branch for repository '#{gitolite_repo_name}'"
27 | else
28 | logger.info "Default branch successfully updated for repository '#{gitolite_repo_name}'"
29 | repository.empty_cache!
30 | end
31 |
32 | def new_default_branch
33 | "refs/heads/#{git_default_branch}"
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/patches/watchers_controller_patch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_dependency 'watchers_controller'
4 |
5 | module RedmineGitHosting
6 | module Patches
7 | module WatchersControllerPatch
8 | include RedmineGitHosting::GitoliteAccessor::Methods
9 |
10 | def create
11 | super
12 | update_repository @watched
13 | end
14 |
15 | def destroy
16 | super
17 | update_repository @watched
18 | end
19 |
20 | def watch
21 | super
22 | update_repository @watchables.first
23 | end
24 |
25 | def unwatch
26 | super
27 | update_repository @watchables.first
28 | end
29 |
30 | private
31 |
32 | def update_repository(repo)
33 | return unless repo.is_a? Repository::Xitolite
34 |
35 | options = { message: "Watcher changes on repository '#{repo}', update!" }
36 | gitolite_accessor.update_repository repo, **options
37 | end
38 | end
39 | end
40 | end
41 |
42 | unless WatchersController.included_modules.include? RedmineGitHosting::Patches::WatchersControllerPatch
43 | WatchersController.prepend RedmineGitHosting::Patches::WatchersControllerPatch
44 | end
45 |
--------------------------------------------------------------------------------
/app/reports/report_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ReportHelper
4 | def date_to
5 | User.current.today
6 | end
7 |
8 | def week_day_hash
9 | { day_name(1) => 0,
10 | day_name(2) => 0,
11 | day_name(3) => 0,
12 | day_name(4) => 0,
13 | day_name(5) => 0,
14 | day_name(6) => 0,
15 | day_name(0) => 0 }
16 | end
17 |
18 | def hours
19 | (0..23).step(1).map { |h| "#{h}h" }
20 | end
21 |
22 | def months
23 | (1..12).map { |m| l('date.month_names')[m].capitalize }
24 | end
25 |
26 | def get_hour_from_date(date)
27 | return unless date
28 |
29 | time = date.to_time
30 | zone = User.current.time_zone
31 | local = if zone
32 | time.in_time_zone zone
33 | else
34 | (time.utc? ? time.localtime : time)
35 | end
36 | local.hour
37 | end
38 |
39 | def total_by_month_for(method)
40 | total = [0] * 12
41 | send(method).each { |c| total[(date_to.month - c.first.to_date.month) % 12] += c.last }
42 | total
43 | end
44 |
45 | def total_by_hour_for(method)
46 | total = [0] * 24
47 | send(method).each { |c| total[get_hour_from_date(c)] += 1 }
48 | total
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/app/views/gitolite_public_keys/_view.html.slim:
--------------------------------------------------------------------------------
1 | h3 = l :label_my_public_keys
2 |
3 | fieldset.public_key_view
4 | legend = l :label_current_user_keys
5 | = render partial: 'gitolite_public_keys/ssh_keys',
6 | locals: { ssh_keys: @gitolite_user_keys }
7 |
8 | br
9 |
10 | fieldset.public_key_view
11 | legend = l :label_current_deploy_keys
12 | = render partial: 'gitolite_public_keys/ssh_keys',
13 | locals: { ssh_keys: @gitolite_deploy_keys, deploy_keys: true }
14 |
15 | br
16 |
17 | fieldset.public_key_view
18 | legend = l :label_public_key_new
19 | = render partial: 'gitolite_public_keys/form', locals: { user: @user }
20 |
21 | - content_for :header_tags do
22 | = stylesheet_link_tag 'application', plugin: 'redmine_git_hosting'
23 |
24 | javascript:
25 | function key_type_change(element) {
26 | var idx = element.selectedIndex;
27 | if (idx == 0) {
28 | $('#key_type_options').hide();
29 | $('#gitolite_public_key_delete_when_unused').prop("checked", true);
30 | } else {
31 | $('#key_type_options').show();
32 | }
33 | }
34 |
35 | $(document).ready(function() {
36 | $('#gitolite_public_key_key_type').on('change', function() {
37 | key_type_change(this)
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/app/views/repository_git_config_keys/index.html.slim:
--------------------------------------------------------------------------------
1 | div
2 | - if User.current.git_allowed_to? :create_repository_git_config_keys, @repository
3 | .contextual
4 | = link_to l(:label_git_config_key_add),
5 | new_repository_git_config_key_path(@repository, type: 'git_config'),
6 | remote: true,
7 | class: 'icon icon-add'
8 |
9 | h3
10 | = l :label_git_config_keys
11 | '
12 | = link_to_external "(#{l :label_gitolite_documentation})",
13 | 'https://gitolite.com/gitolite/git-config.html'
14 |
15 | = render 'config_keys', git_config_keys: @repository_git_config_keys
16 |
17 | - if User.current.git_allowed_to? :create_repository_git_config_keys, @repository
18 | .contextual
19 | = link_to l(:label_git_option_key_add),
20 | new_repository_git_config_key_path(@repository, type: 'git_option'),
21 | remote: true,
22 | class: 'icon icon-add'
23 |
24 | h3
25 | = l :label_git_option_keys
26 | '
27 | = link_to_external "(#{l :label_gitolite_documentation})",
28 | 'https://gitolite.com/gitolite/options.html'
29 |
30 | = render 'config_keys', git_config_keys: @repository_git_option_keys
31 |
--------------------------------------------------------------------------------
/spec/lib/redmine_git_hosting/config_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path "#{File.dirname __FILE__}/../../spec_helper"
4 |
5 | describe RedmineGitHosting::Config do
6 | GITOLITE_VERSION_2 = [
7 | 'hello redmine_gitolite_admin_id_rsa, this is gitolite v2.3.1-0-g912a8bd-dt running on git 1.7.2.5',
8 | 'hello gitolite_admin_id_rsa, this is gitolite gitolite-2.3.1 running on git 1.8.1.5',
9 | 'hello gitolite_admin_id_rsa, this is gitolite 2.3.1-1.el6 running on git 1.7.1',
10 | 'hello gitolite_admin_id_rsa, this is gitolite 2.2-1 (Debian) running on git 1.7.9.5'
11 | ].freeze
12 |
13 | GITOLITE_VERSION_3 = [
14 | 'hello redmine_gitolite_admin_id_rsa, this is git@dev running gitolite3 v3.3-11-ga1aba93 on git 1.7.2.5'
15 | ].freeze
16 |
17 | GITOLITE_VERSION_2.each do |gitolite_version|
18 | it 'should recognize Gitolite2' do
19 | version = RedmineGitHosting::Config.find_version gitolite_version
20 | expect(version).to eq 2
21 | end
22 | end
23 |
24 | GITOLITE_VERSION_3.each do |gitolite_version|
25 | it 'should recognize Gitolite3' do
26 | version = RedmineGitHosting::Config.find_version gitolite_version
27 | expect(version).to eq 3
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/app/controllers/concerns/xitolite_repository_finder.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module XitoliteRepositoryFinder
4 | extend ActiveSupport::Concern
5 |
6 | def find_xitolite_repository
7 | @repository = Repository::Xitolite.find find_repository_param
8 | rescue ActiveRecord::RecordNotFound
9 | render_404
10 | else
11 | @project = @repository.project
12 | render_404 if @project.nil?
13 | end
14 |
15 | def find_xitolite_repository_by_path
16 | repo_path = "#{params[:repo_path]}.git"
17 | repository = Repository::Xitolite.find_by_path repo_path, loose: true
18 | if repository.nil?
19 | RedmineGitHosting.logger.error "GoRedirector : repository not found at path : '#{repo_path}', " \
20 | 'exiting!'
21 | render_404
22 | elsif !repository.go_access_available?
23 | RedmineGitHosting.logger.error "GoRedirector : GoAccess is disabled for this repository '#{repository.gitolite_repository_name}', " \
24 | 'exiting!'
25 | render_403
26 | else
27 | RedmineGitHosting.logger.info "GoRedirector : access granted for repository '#{repository.gitolite_repository_name}'"
28 | @repository = repository
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/app/views/repository_post_receive_urls/_form.html.slim:
--------------------------------------------------------------------------------
1 | .flash-messages = error_messages_for 'post_receive_url'
2 |
3 | .box
4 | p = f.url_field :url,
5 | size: 65,
6 | placeholder: 'https://example.com',
7 | required: true
8 |
9 | p = f.check_box :active
10 |
11 | p = f.select :mode,
12 | [['Github-style POST', :github],
13 | ['Empty POST request', :post],
14 | ['Empty GET request', :get]],
15 | {},
16 | onchange: 'post_mode_change(this); return false;'
17 |
18 | #payload_options style="#{@post_receive_url.github_mode? ? '' : 'display: none;'}"
19 | p = f.check_box :split_payloads
20 |
21 | - if @repository.branches.any?
22 | p = f.check_box :use_triggers, onchange: 'trigger_mode_change(this); return false;'
23 |
24 | #triggers_options style="#{@post_receive_url.use_triggers? ? '' : 'display: none;'}"
25 | = hidden_field_tag 'repository_post_receive_url[triggers][]', ''
26 | - @repository.branches.each do |branch|
27 | p
28 | label
29 | = check_box_tag 'repository_post_receive_url[triggers][]', branch.to_s, @post_receive_url.triggers.include?(branch.to_s)
30 | = branch.to_s
31 |
--------------------------------------------------------------------------------
/app/use_cases/repository_mirrors/push.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RepositoryMirrors
4 | class Push < Base
5 | def call
6 | push!
7 | end
8 |
9 | def push!
10 | begin
11 | push_message = repository.mirror_push(*command)
12 | push_failed = false
13 | rescue RedmineGitHosting::Error::GitoliteCommandException => e
14 | push_message = e.output
15 | push_failed = true
16 | end
17 |
18 | [push_failed, push_message]
19 | end
20 |
21 | def command
22 | [mirror.url, branch, push_args]
23 | end
24 |
25 | private
26 |
27 | def push_args
28 | mirror.mirror_mode? ? ['--mirror'] : mirror_args
29 | end
30 |
31 | def mirror_args
32 | push_args = []
33 | push_args << '--force' if mirror.force_mode?
34 | push_args << '--all' if mirror.include_all_branches?
35 | push_args << '--tags' if mirror.include_all_tags?
36 | push_args
37 | end
38 |
39 | def branch
40 | dequote(mirror.explicit_refspec).to_s if mirror.explicit_refspec.present?
41 | end
42 |
43 | # Put backquote in front of crucial characters
44 | def dequote(in_string)
45 | in_string.gsub(/[$,"\\\n]/) { |x| '\\' + x }
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/utils/git.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Utils
5 | module Git
6 | extend self
7 |
8 | REF_COMPONENT_PART = '[\\.\\-\\w_\\*]+'
9 | REF_COMPONENT_REGEX = %r{\A(refs/)?((#{REF_COMPONENT_PART})/)?(#{REF_COMPONENT_PART}(/#{REF_COMPONENT_PART})*)\z}.freeze
10 |
11 | # Parse a reference component. Two possibilities:
12 | #
13 | # 1) refs/type/name
14 | # 2) name
15 | #
16 | def parse_refspec(spec)
17 | parsed_refspec = spec.match REF_COMPONENT_REGEX
18 | return if parsed_refspec.nil?
19 |
20 | if parsed_refspec[1]
21 | # Should be first class. If no type component, return fail
22 | { type: parsed_refspec[3], name: parsed_refspec[4] } if parsed_refspec[3]
23 | elsif parsed_refspec[3]
24 | { type: nil, name: "#{parsed_refspec[3]}/#{parsed_refspec[4]}" }
25 | else
26 | { type: nil, name: parsed_refspec[4] }
27 | end
28 | end
29 |
30 | def author_name(committer)
31 | committer.gsub(/\A([^<]+)\s+.*\z/, '\1')
32 | end
33 |
34 | def author_email(committer)
35 | committer.gsub(/\A.*<([^>]+)>.*\z/, '\1')
36 | end
37 | end
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/spec/factories/role.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | FactoryBot.define do
4 | factory :role do
5 | name { 'Manager' }
6 | builtin { 0 }
7 | issues_visibility { 'all' }
8 | position { 1 }
9 | permissions do
10 | %i[add_project edit_project close_project select_project_modules manage_members
11 | manage_versions manage_categories view_issues add_issues edit_issues manage_issue_relations
12 | manage_subtasks add_issue_notes move_issues delete_issues view_issue_watchers add_issue_watchers
13 | set_issues_private set_notes_private view_private_notes delete_issue_watchers
14 | manage_public_queries save_queries view_gantt view_calendar log_time view_time_entries
15 | edit_time_entries delete_time_entries manage_news comment_news view_documents
16 | add_documents edit_documents delete_documents view_wiki_pages export_wiki_pages
17 | view_wiki_edits edit_wiki_pages delete_wiki_pages_attachments protect_wiki_pages
18 | delete_wiki_pages rename_wiki_pages add_messages edit_messages delete_messages
19 | manage_boards view_files manage_files browse_repository manage_repository view_changesets
20 | manage_related_issues manage_project_activities create_gitolite_ssh_key commit_access]
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/views/settings/redmine_git_hosting/_gitolite_config_storage.html.slim:
--------------------------------------------------------------------------------
1 | / Gitolite Storage Config
2 | - gitolite_global_storage_dir = RedmineGitHosting::Config.get_setting :gitolite_global_storage_dir
3 | - gitolite_redmine_storage_dir = RedmineGitHosting::Config.get_setting :gitolite_redmine_storage_dir
4 | - gitolite_recycle_bin_dir = RedmineGitHosting::Config.get_setting :gitolite_recycle_bin_dir
5 |
6 | h3 = l :label_gitolite_storage_config
7 |
8 | p
9 | label = l :label_gitolite_global_storage_dir
10 | = text_field_tag 'settings[gitolite_global_storage_dir]',
11 | gitolite_global_storage_dir,
12 | size: 60,
13 | required: true
14 | br
15 | em.info
16 | = l :label_gitolite_global_storage_dir_desc
17 |
18 | p
19 | label = l :label_gitolite_recycle_bin_dir
20 | = text_field_tag 'settings[gitolite_recycle_bin_dir]',
21 | gitolite_recycle_bin_dir,
22 | size: 60,
23 | required: true
24 | br
25 | em.info
26 | = l :label_gitolite_recycle_bin_dir_desc
27 |
28 | p
29 | label = l :label_gitolite_redmine_storage_dir
30 | = text_field_tag 'settings[gitolite_redmine_storage_dir]', gitolite_redmine_storage_dir, size: 60
31 | br
32 | em.info
33 | = l :label_gitolite_redmine_storage_dir_desc
34 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_handlers/repositories/add_repository.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteHandlers
5 | module Repositories
6 | class AddRepository < Base
7 | def call
8 | if !configuration_exists?
9 | # Create repository in Gitolite
10 | log_repo_not_exist 'create it ...'
11 | create_repository_config
12 |
13 | elsif configuration_exists? && force
14 | # Recreate repository in Gitolite
15 | log_repo_already_exist 'force mode !'
16 | recreate_repository_config
17 |
18 | else
19 | log_repo_already_exist 'exit !'
20 | end
21 | end
22 |
23 | def gitolite_repo_name
24 | repository.gitolite_repository_name
25 | end
26 |
27 | def gitolite_repo_path
28 | repository.gitolite_repository_path
29 | end
30 |
31 | attr_reader :force
32 |
33 | def initialize(gitolite_config, repository, context, **options)
34 | super(gitolite_config, repository, context, **options)
35 | @force = @options.delete(:force) { false }
36 | @old_perms = @options.delete(:old_perms) { {} }
37 | end
38 | end
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/app/models/repository_git_config_key.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RepositoryGitConfigKey < ActiveRecord::Base
4 | include Redmine::SafeAttributes
5 |
6 | ## Attributes
7 | safe_attributes 'type', 'key', 'value'
8 |
9 | ## Relations
10 | belongs_to :repository
11 |
12 | ## Validations
13 | validates :repository_id, presence: true
14 | validates :type, presence: true, inclusion: { in: ['RepositoryGitConfigKey::GitConfig', 'RepositoryGitConfigKey::Option'] }
15 | validates :value, presence: true
16 |
17 | ## Callbacks
18 | after_save :check_if_key_changed
19 |
20 | ## Virtual attribute
21 | attr_accessor :key_has_changed
22 | attr_accessor :old_key
23 |
24 | # Syntaxic sugar
25 | def key_has_changed?
26 | key_has_changed
27 | end
28 |
29 | private
30 |
31 | # This is Rails method : saved_changes
32 | # However, the value is cleared before passing the object to the controller.
33 | # We need to save it in virtual attribute to trigger Gitolite resync if changed.
34 | #
35 | def check_if_key_changed
36 | if saved_changes&.key? :key
37 | self.key_has_changed = true
38 | self.old_key = saved_changes[:key][1]
39 | else
40 | self.key_has_changed = false
41 | self.old_key = ''
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/global/common.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Global
6 | module Common
7 | def redmine_gitolite_key
8 | 'redmine_gitolite_admin_id_rsa'
9 | end
10 |
11 | def all_repository
12 | '@all'
13 | end
14 |
15 | def all_repository_config
16 | gitolite_config.repos[all_repository]
17 | end
18 |
19 | def rw_access_config
20 | repo_conf = ::Gitolite::Config::Repo.new all_repository
21 | repo_conf.permissions = rw_access_perms
22 | repo_conf
23 | end
24 |
25 | def rw_access_perms
26 | permissions = {}
27 | permissions['RW+'] = {}
28 | permissions['RW+'][''] = [redmine_gitolite_key]
29 | [permissions]
30 | end
31 |
32 | def repo_conf
33 | all_repository_config
34 | end
35 |
36 | def perms
37 | repo_conf.permissions.select { |p| p.key? 'RW+' }
38 | end
39 |
40 | # RedmineGitHosting key can act on any refspec ('') so it should be in that 'subgroup'
41 | #
42 | def users
43 | perms[0]['RW+']['']
44 | end
45 | end
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/plugins/extenders/git_annex_creator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting::Plugins::Extenders
4 | class GitAnnexCreator < BaseExtender
5 | attr_reader :enable_git_annex
6 |
7 | def initialize(repository, **options)
8 | super(repository, **options)
9 | @enable_git_annex = options.delete(:enable_git_annex) { false }
10 | end
11 |
12 | def post_create
13 | return unless installable?
14 |
15 | if git_annex_installed?
16 | logger.warn "GitAnnex already exists in path '#{gitolite_repo_path}'"
17 | else
18 | install_git_annex
19 | end
20 | end
21 |
22 | private
23 |
24 | def installable?
25 | enable_git_annex? && !recovered?
26 | end
27 |
28 | def enable_git_annex?
29 | RedminePluginKit.true? enable_git_annex
30 | end
31 |
32 | def git_annex_installed?
33 | directory_exists? File.join(gitolite_repo_path, 'annex')
34 | end
35 |
36 | def install_git_annex
37 | sudo_git 'annex', 'init'
38 | rescue RedmineGitHosting::Error::GitoliteCommandException
39 | logger.error "Error while enabling GitAnnex for repository '#{gitolite_repo_name}'"
40 | else
41 | logger.info "GitAnnex successfully enabled for repository '#{gitolite_repo_name}'"
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/redcarpet_filter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'html/pipeline/filter'
4 | require 'html/pipeline/text_filter'
5 | require 'redcarpet'
6 | require 'rouge'
7 | require 'rouge/plugins/redcarpet'
8 |
9 | module RedmineGitHosting
10 | class HTMLwithRouge < Redcarpet::Render::HTML
11 | include Rouge::Plugins::Redcarpet
12 | end
13 |
14 | class RedcarpetFilter < HTML::Pipeline::TextFilter
15 | def initialize(text, context = nil, result = nil)
16 | super text, context, result
17 | @text = @text.delete "\r"
18 | end
19 |
20 | # Convert Markdown to HTML using the best available implementation
21 | # and convert into a DocumentFragment.
22 | #
23 | def call
24 | html = self.class.renderer.render @text
25 | html.rstrip!
26 | html
27 | end
28 |
29 | def self.renderer
30 | @renderer ||= Redcarpet::Markdown.new HTMLwithRouge, markdown_options
31 | end
32 |
33 | def self.markdown_options
34 | @markdown_options ||= {
35 | fenced_code_blocks: true,
36 | lax_spacing: true,
37 | strikethrough: true,
38 | autolink: true,
39 | tables: true,
40 | underline: true,
41 | highlight: true
42 | }.freeze
43 | end
44 |
45 | private_class_method :markdown_options
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/gitolite_wrappers/projects/common.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module GitoliteWrappers
5 | module Projects
6 | module Common
7 | def handle_repositories_move(projects)
8 | repo_list = []
9 | delete_parent_path = []
10 | projects.reverse_each do |project|
11 | project.gitolite_repos.reverse_each do |repository|
12 | repo_list << repository.gitolite_repository_name
13 | delete_parent_path << move_gitolite_repository(repository)
14 | end
15 | gitolite_admin_repo_commit "#{context} : #{project.identifier} | #{repo_list}"
16 | end
17 | delete_parent_path
18 | end
19 |
20 | def clean_path(path_list)
21 | path_list.compact.uniq.sort.reverse_each do |path|
22 | rmdir path
23 | end
24 | end
25 |
26 | def rmdir(path)
27 | logger.info "#{context} : cleaning repository path : '#{path}'"
28 | begin
29 | RedmineGitHosting::Commands.sudo_rmdir path
30 | rescue RedmineGitHosting::Error::GitoliteCommandException
31 | logger.error "#{context} : error while cleaning repository path '#{path}'"
32 | end
33 | end
34 | end
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/app/views/gitolite_public_keys/_form.html.slim:
--------------------------------------------------------------------------------
1 | #validation_messages
2 | = error_messages_for 'gitolite_public_key'
3 |
4 | = labelled_form_for :gitolite_public_key, GitolitePublicKey.new,
5 | url: { controller: 'gitolite_public_keys', action: 'create', user_id: params[:id], tab: params[:id] && 'keys' },
6 | html: { method: :post } do |f|
7 | p
8 | = f.text_field :title, label: :label_identifier_can_be_arbitrary, required: true, style: 'width: 97%;'
9 |
10 | - if can_create_deployment_keys_for_some_project @user
11 | p
12 | = f.select :key_type,
13 | options_for_select([[l(:label_user_key), 0], [l(:label_deploy_key), 1]]),
14 | { required: true, label: :label_key_type },
15 | { class: 'select_key_type' }
16 | #key_type_options style="display: none;"
17 | p
18 | = f.check_box :delete_when_unused, required: true, label: :label_deployment_credential_delete_when_unused
19 | p
20 | = f.text_area :key, label: :label_public_key, required: true,
21 | style: 'width: 97%; height: 200px; overflow: auto;',
22 | cols: nil,
23 | rows: nil
24 | em
25 | = l :label_cut_and_paste
26 | br
27 | br
28 | = submit_tag l(:button_create), name: 'create_button'
29 | '
30 | = cancel_button_tag my_account_path
31 |
--------------------------------------------------------------------------------
/app/reports/report_query.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ReportQuery
4 | private
5 |
6 | def all_changesets
7 | @all_changesets ||= Changeset.where repository_id: repository.id
8 | end
9 |
10 | def all_changes
11 | @all_changes ||= Change.joins(:changeset).where("#{Changeset.table_name}.repository_id = ?", repository.id)
12 | end
13 |
14 | def all_commits_by_day
15 | @all_commits_by_day ||= all_changesets.group :commit_date
16 | end
17 |
18 | def all_changes_by_day
19 | @all_changes_by_day ||= all_changes.group :commit_date
20 | end
21 |
22 | def redmine_committers
23 | @redmine_committers ||= all_changesets.where.not(user_id: nil).distinct.count(:user_id)
24 | end
25 |
26 | def external_committers
27 | @external_committers ||= all_changesets.where(user_id: nil).distinct.count(:committer)
28 | end
29 |
30 | def commits_by_day
31 | @commits_by_day ||= all_commits_by_day.count
32 | end
33 |
34 | def changes_by_day
35 | @changes_by_day ||= all_changes_by_day.count
36 | end
37 |
38 | def commits_by_hour
39 | @commits_by_hour ||= all_changesets.map(&:committed_on)
40 | end
41 |
42 | def commits_by_author
43 | @commits_by_author ||= all_changesets.group(:committer).count
44 | end
45 |
46 | def changes_by_author
47 | @changes_by_author ||= all_changes.group(:committer).count
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/spec/models/project_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path "#{File.dirname __FILE__}/../spec_helper"
4 |
5 | describe Project do
6 | before :all do
7 | @project = create :project
8 | @git_repo_1 = create_git_repository project: @project, is_default: true
9 | @git_repo_2 = create_git_repository project: @project, identifier: 'git-repo-test'
10 | @svn_repo_1 = create_svn_repository project: @project, identifier: 'svn-repo-test', url: 'http://svn-repo-test'
11 | end
12 |
13 | subject { @project }
14 |
15 | ## Test relations
16 | it { should respond_to(:gitolite_repos) }
17 | it { should respond_to(:repo_blank_ident) }
18 |
19 | it 'should have 1 repository with blank ident' do
20 | expect(@project.repo_blank_ident).to eq @git_repo_1
21 | end
22 |
23 | it 'should have 2 Git repositories' do
24 | expect(@project.gitolite_repos).to include @git_repo_1, @git_repo_2
25 | end
26 |
27 | it 'should have 3 repositories' do
28 | expect(@project.repositories).to include @git_repo_1, @git_repo_2, @svn_repo_1
29 | end
30 |
31 | it 'should not match existing repository identifier' do
32 | expect(build(:project, identifier: 'git-repo-test')).to be_invalid
33 | end
34 |
35 | it 'should not match Gitolite Admin repository identifier' do
36 | expect(build(:project, identifier: 'gitolite-admin')).to be_invalid
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/utils/ssh.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Utils
5 | module Ssh
6 | extend self
7 |
8 | def ssh_fingerprint(key)
9 | file = Tempfile.new 'keytest'
10 | file.write key
11 | file.close
12 |
13 | begin
14 | output = Utils::Exec.capture 'ssh-keygen', ['-l', '-f', file.path]
15 | rescue RedmineGitHosting::Error::GitoliteCommandException
16 | raise RedmineGitHosting::Error::InvalidSshKey, "Invalid Ssh Key : #{key}"
17 | else
18 | output.split[1]
19 | ensure
20 | file.unlink
21 | end
22 | end
23 |
24 | def sanitize_ssh_key(key)
25 | # First -- let the first control char or space stand (to divide key type from key)
26 | # Really, this is catching a special case in which there is a \n between type and key.
27 | # Most common case turns first space back into space....
28 | key = key.sub(/[ \r\n\t]/, ' ')
29 |
30 | # Next, if comment divided from key by control char, let that one stand as well
31 | # We can only tell this if there is an "=" in the key. So, won't help 1/3 times.
32 | key = key.sub(/=[ \r\n\t]/, '= ')
33 |
34 | # Delete any remaining control characters....
35 | key.gsub(/[\a\r\n\t]/, '').strip
36 | end
37 | end
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/app/controllers/download_git_revision_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class DownloadGitRevisionController < ApplicationController
4 | include XitoliteRepositoryFinder
5 |
6 | before_action :find_xitolite_repository
7 | before_action :can_download_git_revision
8 | before_action :set_download
9 | before_action :validate_download
10 |
11 | helper :git_hosting
12 |
13 | def index
14 | send_data @download.content, filename: @download.filename, type: @download.content_type
15 | rescue StandardError => e
16 | flash.now[:error] = l :git_archive_timeout, timeout: e.output
17 | render_404
18 | end
19 |
20 | private
21 |
22 | def find_repository_param
23 | params[:id]
24 | end
25 |
26 | def can_download_git_revision
27 | render_403 unless User.current.allowed_to_download? @repository
28 | end
29 |
30 | def set_download
31 | @download = Repositories::DownloadRevision.new @repository, download_revision, download_format
32 | end
33 |
34 | def download_revision
35 | @download_revision ||= (params[:rev] || 'master')
36 | end
37 |
38 | def download_format
39 | @download_format ||= (params[:download_format] || 'tar')
40 | end
41 |
42 | def validate_download
43 | return if @download.valid_commit?
44 |
45 | flash.now[:error] = l :error_download_revision_no_such_commit, commit: download_revision
46 | render_404
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/.slim-lint.yml:
--------------------------------------------------------------------------------
1 | linters:
2 | LineLength:
3 | max: 140
4 | RuboCop:
5 | ignored_cops:
6 | - Layout/ArgumentAlignment
7 | - Layout/ArrayAlignment
8 | - Layout/BlockEndNewline
9 | - Layout/EmptyLineAfterGuardClause
10 | - Layout/HashAlignment
11 | - Layout/IndentationConsistency
12 | - Layout/IndentationWidth
13 | - Layout/IndentFirstArgument
14 | - Layout/IndentFirstArrayElement
15 | - Layout/IndentFirstHashElement
16 | - Layout/MultilineArrayBraceLayout
17 | - Layout/MultilineAssignmentLayout
18 | - Layout/MultilineBlockLayout
19 | - Layout/MultilineHashBraceLayout
20 | - Layout/MultilineMethodCallBraceLayout
21 | - Layout/MultilineMethodCallIndentation
22 | - Layout/MultilineMethodDefinitionBraceLayout
23 | - Layout/MultilineOperationIndentation
24 | - Layout/TrailingBlankLines
25 | - Layout/TrailingEmptyLines
26 | - Layout/TrailingWhitespace
27 | - Lint/BlockAlignment
28 | - Lint/EndAlignment
29 | - Lint/Void
30 | - Metrics/BlockLength
31 | - Metrics/BlockNesting
32 | - Metrics/LineLength
33 | - Naming/FileName
34 | - Rails/OutputSafety
35 | - Style/ConditionalAssignment
36 | - Style/FrozenStringLiteralComment
37 | - Style/IdenticalConditionalBranches
38 | - Style/IfUnlessModifier
39 | - Style/Next
40 | - Style/WhileUntilModifier
41 |
--------------------------------------------------------------------------------
/assets/javascripts/git_urls.js:
--------------------------------------------------------------------------------
1 | // GIT URLS
2 | function updateUrl(element) {
3 | var url = $(element).data('url');
4 | var target = $(element).data('target');
5 | var committer = $(element).data('committer');
6 | $('#git_url_text_' + target).val(url);
7 | $('#git_url_permissions_' + target).html(committer);
8 | $(element).parent().find('li').removeClass('selected');
9 | $(element).addClass('selected');
10 | }
11 |
12 | function setGitUrls(elements) {
13 | $(elements).each(function(index, element){
14 | $(element).on('click', function(){
15 | updateUrl($(this));
16 | });
17 | });
18 | }
19 |
20 | function setFirstGitUrl(elements) {
21 | $(elements).each(function(index, element){
22 | var first_url = $(element).children().first();
23 | updateUrl(first_url);
24 | });
25 | }
26 |
27 | // GIT INSTRUCTIONS
28 | function updateInstructionUrl(element) {
29 | var url = $(element).data('url');
30 | var committer = $(element).data('committer');
31 | $('.git_url_access').html(url);
32 | if (committer == 'RW') {
33 | $('#repository_setup').show();
34 | } else {
35 | $('#repository_setup').hide();
36 | }
37 | }
38 |
39 | function setGitUrlsInstructions(elements) {
40 | $(elements).each(function(index, element){
41 | if (index == 0){
42 | updateInstructionUrl(element);
43 | };
44 | $(element).on('click', function(){
45 | updateInstructionUrl($(this));
46 | });
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/custom_hooks.rb.example:
--------------------------------------------------------------------------------
1 | # You can declare here you own hooks to install globally in Gitolite.
2 | # You must set the source directory of the files with the *source_dir* method and
3 | # declare your hooks with *gitolite_hook* method.
4 | #
5 | # *RedmineGitHosting::GitoliteHooks.register_hooks* can be called multiple times
6 | # with a different *source_dir*.
7 | #
8 | # *name* : the hook name (just a name to identify the hook)
9 | # *source* : the source path concatenated with *source_dir*
10 | # *destination* : the destination path on Gitolite side.
11 | #
12 | # The *destination* must be relative.
13 | # The final destination will depend on your Gitolite version :
14 | #
15 | # Gitolite v2 : /.gitolite/hooks/common
16 | # Gitolite v3 : /local/hooks/common/
17 | #
18 | # RedmineGitHosting::GitoliteHooks.register_hooks do
19 | # source_dir '/tmp/global-hooks'
20 | #
21 | # gitolite_hook do
22 | # name 'global/check-branch'
23 | # source 'pre-receive/check_branch.rb'
24 | # destination 'pre-receive.d/check_branch.rb'
25 | # executable true
26 | # end
27 | # end
28 | #
29 | # RedmineGitHosting::GitoliteHooks.register_hooks do
30 | # source_dir '/tmp/custom-hooks'
31 | #
32 | # gitolite_hook do
33 | # name 'custom/notify-users'
34 | # source 'post-receive/notify_users.rb'
35 | # destination 'post-receive.d/notify_users.rb'
36 | # executable true
37 | # end
38 | # end
39 |
--------------------------------------------------------------------------------
/app/views/repository_protected_branches/_form.html.slim:
--------------------------------------------------------------------------------
1 | .flash-messages = error_messages_for 'protected_branch'
2 |
3 | .box
4 | p = f.text_field :path, required: true, size: 65, label: l(:label_branch_path)
5 | p = f.select :permissions,
6 | options_for_select(RepositoryProtectedBranche::VALID_PERMS, @protected_branch.permissions),
7 | required: true,
8 | label: :label_permissions
9 |
10 | = hidden_field_tag 'user_ids[]', ''
11 | = hidden_field_tag 'group_ids[]', ''
12 |
13 | .container
14 | .row
15 | .col-md-6
16 | h4
17 | = font_awesome_icon 'fas_user', post_text: l(:label_user_plural), class: 'fa-lg'
18 |
19 | - @project.users_available.each do |user|
20 | p style='padding: 0px;'
21 | label style='margin-left: 0; width: auto; font-weight: lighter;'
22 | = check_box_tag 'user_ids[]', user.id, @protected_branch.users.include?(user)
23 | = user
24 | br
25 |
26 | .col-md-6
27 | h4
28 | = font_awesome_icon 'fas_users', post_text: l(:label_group_plural), class: 'fa-lg'
29 |
30 | - @project.groups_available.each do |group|
31 | p style= 'padding: 0px;'
32 | label style='margin-left: 0; width: auto; font-weight: lighter;'
33 | = check_box_tag 'group_ids[]', group.id, @protected_branch.groups.include?(group)
34 | = group
35 | br
36 |
--------------------------------------------------------------------------------
/app/forms/plugin_settings_validation/ssh_config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module PluginSettingsValidation
4 | module SshConfig
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | # Gitolite SSH Config
9 | add_accessor :gitolite_user,
10 | :gitolite_server_host,
11 | :gitolite_server_port,
12 | :gitolite_ssh_private_key,
13 | :gitolite_ssh_public_key
14 |
15 | before_validation do
16 | self.gitolite_user = strip_value gitolite_user
17 | self.gitolite_server_host = strip_value gitolite_server_host
18 | self.gitolite_server_port = strip_value gitolite_server_port
19 |
20 | self.gitolite_ssh_private_key = strip_value gitolite_ssh_private_key
21 | self.gitolite_ssh_public_key = strip_value gitolite_ssh_public_key
22 | end
23 |
24 | validates :gitolite_user, :gitolite_server_host, :gitolite_ssh_private_key, :gitolite_ssh_public_key,
25 | presence: true
26 | validates :gitolite_server_port,
27 | presence: true,
28 | numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 65_536 }
29 |
30 | validates_each :gitolite_ssh_private_key, :gitolite_ssh_public_key do |record, attr, value|
31 | record.errors.add attr, 'must exists on filesystem' unless File.exist? value
32 | end
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/app/views/settings/redmine_git_hosting/_gitolite_config_cache.html.slim:
--------------------------------------------------------------------------------
1 | / Gitolite Cache Config
2 | ruby:
3 | gitolite_cache_max_time = RedmineGitHosting::Config.get_setting :gitolite_cache_max_time
4 | gitolite_cache_max_size = RedmineGitHosting::Config.get_setting :gitolite_cache_max_size
5 | gitolite_cache_max_elements = RedmineGitHosting::Config.get_setting :gitolite_cache_max_elements
6 | gitolite_cache_adapter = RedmineGitHosting::Config.get_setting :gitolite_cache_adapter
7 |
8 | h3 = l :label_gitolite_cache_config
9 |
10 | p
11 | label = l :label_gitolite_cache_max_time
12 | = select_tag 'settings[gitolite_cache_max_time]', options_for_select(git_cache_options, gitolite_cache_max_time)
13 | br
14 |
15 | p
16 | label = l :label_gitolite_cache_max_size
17 | = number_field_tag 'settings[gitolite_cache_max_size]',
18 | gitolite_cache_max_size,
19 | size: 20,
20 | min: -1
21 | em<
22 | ' MB
23 | br
24 |
25 | p
26 | label = l :label_gitolite_cache_max_elements
27 | = number_field_tag 'settings[gitolite_cache_max_elements]',
28 | gitolite_cache_max_elements,
29 | size: 20,
30 | min: 1
31 | br
32 |
33 | p
34 | label = l :label_gitolite_cache_adapter
35 | = select_tag 'settings[gitolite_cache_adapter]', options_for_select(GitCache::CACHE_ADAPTERS, gitolite_cache_adapter)
36 | br
37 | em = l :label_gitolite_cache_adapter_desc
38 |
--------------------------------------------------------------------------------
/spec/controllers/users_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path "#{File.dirname __FILE__}/../spec_helper"
4 |
5 | require 'sshkey'
6 |
7 | describe UsersController do
8 | describe 'GET #edit' do
9 | context 'with git hosting patch' do
10 | let(:user) { create_admin_user }
11 | let :user_key do
12 | create_ssh_key(user_id: user.id,
13 | title: 'user_key',
14 | key: SSHKey.generate(comment: 'faker_user_key@john_doe').ssh_public_key,
15 | key_type: 0)
16 | end
17 | let :deploy_key do
18 | create_ssh_key(user_id: user.id,
19 | title: 'deploy_key',
20 | key: SSHKey.generate(comment: 'faker_deploy_key@john_doe').ssh_public_key,
21 | key_type: 1)
22 | end
23 |
24 | it 'populates an array of gitolite_user_keys' do
25 | set_session_user user
26 | get :edit,
27 | params: { id: user.id }
28 |
29 | assert_response :success
30 | expect(@controller.view_assigns['gitolite_user_keys']).to eq [user_key]
31 | end
32 |
33 | # it 'populates an array of gitolite_deploy_keys' do
34 | # set_session_user(user)
35 | # get :edit,
36 | # params: { id: user.id }
37 | # expect(@controller.view_assigns['gitolite_deploy_keys']).to eq [deploy_key]
38 | # end
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/spec/lib/redmine_git_hosting/utils/git_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require File.expand_path "#{File.dirname __FILE__}/../../../spec_helper"
4 |
5 | describe RedmineGitHosting::Utils::Git do
6 | include RedmineGitHosting::Utils::Git
7 |
8 | describe '.parse_refspec' do
9 | context 'it should accept different refspec format' do
10 | it 'should accept ' do
11 | expect(parse_refspec('dev')).to eq({ type: nil, name: 'dev' })
12 | end
13 |
14 | it 'should accept a branch path' do
15 | expect(parse_refspec('refs/branches/dev')).to eq({ type: 'branches', name: 'dev' })
16 | end
17 |
18 | it 'should accept the wildcard param (*)' do
19 | expect(parse_refspec('refs/branches/dev/*')).to eq({ type: 'branches', name: 'dev/*' })
20 | expect(parse_refspec('refs/tags/*')).to eq({ type: 'tags', name: '*' })
21 | end
22 |
23 | it 'should parse different refspec path' do
24 | expect(parse_refspec('refs/remotes/origin/experiment/*')).to eq({ type: 'remotes', name: 'origin/experiment/*' })
25 | expect(parse_refspec('refs/remotes/origin/experiment/master')).to eq({ type: 'remotes', name: 'origin/experiment/master' })
26 | expect(parse_refspec('refs/remotes/origin/experiment')).to eq({ type: 'remotes', name: 'origin/experiment' })
27 | expect(parse_refspec('refs/heads/experiment')).to eq({ type: 'heads', name: 'experiment' })
28 | end
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/config/redmine_config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting
4 | module Config
5 | module RedmineConfig
6 | extend self
7 |
8 | def gitolite_use_sidekiq?
9 | get_setting :gitolite_use_sidekiq, true
10 | end
11 |
12 | def sidekiq_available?
13 | @sidekiq_available ||=
14 | begin
15 | require 'sidekiq'
16 | require 'sidekiq/api'
17 | rescue LoadError
18 | false
19 | else
20 | true
21 | end
22 | end
23 |
24 | def hierarchical_organisation?
25 | get_setting :hierarchical_organisation, true
26 | end
27 |
28 | def unique_repo_identifier?
29 | get_setting :unique_repo_identifier, true
30 | end
31 |
32 | def all_projects_use_git?
33 | get_setting :all_projects_use_git, true
34 | end
35 |
36 | def init_repositories_on_create?
37 | get_setting :init_repositories_on_create, true
38 | end
39 |
40 | def download_revision_enabled?
41 | get_setting :download_revision_enabled, true
42 | end
43 |
44 | def delete_git_repositories?
45 | get_setting :delete_git_repositories, true
46 | end
47 |
48 | def gitolite_recycle_bin_expiration_time
49 | (get_setting(:gitolite_recycle_bin_expiration_time).to_f * 60).to_i
50 | end
51 | end
52 |
53 | extend Config::RedmineConfig
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/db/migrate/20111123214911_add_settings_to_plugin.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddSettingsToPlugin < ActiveRecord::Migration[4.2]
4 | def up
5 | # Add some new settings to settings page, if they don't exist
6 | valuehash = Setting.plugin_redmine_git_hosting.clone
7 | valuehash['gitRecycleBasePath'] ||= 'recycle_bin/'
8 | valuehash['gitRecycleExpireTime'] ||= '24.0'
9 | valuehash['gitLockWaitTime'] ||= '10'
10 | valuehash['httpServer'] ||= RedmineGitHosting::Config.my_root_url ssl: false
11 |
12 | if Setting.plugin_redmine_git_hosting != valuehash
13 | Setting.plugin_redmine_git_hosting = valuehash
14 | say 'Added redmine_git_hosting settings: gitRecycleBasePath, getRecycleExpireTime, getLockWaitTime, httpServer'
15 | end
16 | rescue StandardError => e
17 | say e.message
18 | end
19 |
20 | def down
21 | # Remove above settings from plugin page
22 | valuehash = Setting.plugin_redmine_git_hosting.clone
23 | valuehash.delete 'gitRecycleBasePath'
24 | valuehash.delete 'gitRecycleExpireTime'
25 | valuehash.delete 'gitLockWaitTime'
26 | valuehash.delete 'gitLockWaitTime'
27 |
28 | if Setting.plugin_redmine_git_hosting != valuehash
29 | Setting.plugin_redmine_git_hosting = valuehash
30 | say 'Removed redmine_git_hosting settings: gitRecycleBasePath, getRecycleExpireTime, getLockWaitTime, httpServer'
31 | end
32 | Setting.plugin_redmine_git_hosting = valuehash
33 | rescue StandardError => e
34 | say e.message
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/app/controllers/repository_git_extras_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RepositoryGitExtrasController < RedmineGitHostingController
4 | include RedmineGitHosting::GitoliteAccessor::Methods
5 |
6 | skip_before_action :set_current_tab
7 |
8 | helper :extend_repositories
9 |
10 | def update
11 | set_git_extra
12 | @git_extra.safe_attributes = params[:repository_git_extra]
13 |
14 | if @git_extra.save
15 | flash.now[:notice] = l :notice_gitolite_extra_updated
16 | gitolite_accessor.update_repository @repository, update_default_branch: @git_extra.default_branch_has_changed?
17 | else
18 | flash.now[:error] = l :notice_gitolite_extra_update_failed
19 | end
20 | end
21 |
22 | def sort_urls
23 | set_git_extra
24 | return unless request.post?
25 |
26 | if @git_extra.update urls_order: params[:repository_git_extra]
27 | flash.now[:notice] = l :notice_gitolite_extra_updated
28 | else
29 | flash.now[:error] = l :notice_gitolite_extra_update_failed
30 | end
31 | end
32 |
33 | def move
34 | @move_repository_form = MoveRepositoryForm.new @repository
35 | return unless request.post?
36 |
37 | @move_repository_form = MoveRepositoryForm.new @repository
38 | return unless @move_repository_form.submit params[:repository_mover]
39 |
40 | redirect_to settings_project_path(@repository.project, tab: 'repositories')
41 | end
42 |
43 | private
44 |
45 | def set_git_extra
46 | @git_extra = @repository.extra
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/app/helpers/repository_mirrors_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RepositoryMirrorsHelper
4 | # Mirror Mode
5 | def mirror_mode(mirror)
6 | [l(:label_mirror_full_mirror),
7 | l(:label_mirror_forced_update),
8 | l(:label_mirror_fast_forward)][mirror.push_mode]
9 | end
10 |
11 | # Refspec for mirrors
12 | def refspec(mirror, max_refspec = 0)
13 | if mirror.mirror_mode?
14 | l :all_references
15 | else
16 | result = []
17 | result << tag.li(l(:all_branches)) if mirror.include_all_branches
18 | result << tag.li(l(:all_tags)) if mirror.include_all_tags
19 | result << tag.li(mirror.explicit_refspec) if max_refspec.zero? || ((1..max_refspec).include? mirror.explicit_refspec.length)
20 | result << tag.li(l(:explicit)) if max_refspec.positive? && (mirror.explicit_refspec.length > max_refspec)
21 |
22 | tag.ul safe_join(result), class: 'list-unstyled' if result.any?
23 | end
24 | end
25 |
26 | def mirrors_options
27 | [
28 | [l(:label_mirror_full_mirror), 0],
29 | [l(:label_mirror_forced_update), 1],
30 | [l(:label_mirror_fast_forward), 2]
31 | ]
32 | end
33 |
34 | def render_push_state(mirror, error)
35 | if error
36 | status = l :label_mirror_push_fail
37 | status_css = 'error'
38 | else
39 | status = l :label_mirror_push_sucess
40 | status_css = 'success'
41 | end
42 |
43 | t :label_mirror_push_info_html, mirror_url: mirror.url, status: status, status_css: status_css
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/app/reports/repository_global_stats.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RepositoryGlobalStats < ReportBase
4 | def build
5 | data = {}
6 | data[l(:label_total_commits)] = total_commits
7 | data[l(:label_total_contributors)] = committers
8 | data[l(:label_first_commit_date)] = format_date first_commit.commit_date
9 | data[l(:label_latest_commit_date)] = format_date last_commit.commit_date
10 | data[l(:label_active_for)] = "#{active_for} #{l :days, active_for}"
11 | data[l(:label_average_commit_per_day)] = average_commit_per_day
12 | data[l(:label_average_contributor_commits)] = average_contributor_commits
13 | data
14 | end
15 |
16 | private
17 |
18 | def total_commits
19 | @total_commits ||= all_changesets.count
20 | end
21 |
22 | def committers
23 | @committers ||= redmine_committers + external_committers
24 | end
25 |
26 | def first_commit
27 | @first_commit ||= all_changesets.order(commit_date: :asc).first
28 | end
29 |
30 | def last_commit
31 | @last_commit ||= all_changesets.order(commit_date: :asc).last
32 | end
33 |
34 | def active_for
35 | @active_for ||= (last_commit.commit_date - first_commit.commit_date).to_i
36 | end
37 |
38 | def average_commit_per_day
39 | @average_commit_per_day ||= total_commits.fdiv(active_for).round(2)
40 | end
41 |
42 | def average_contributor_commits
43 | @average_contributor_commits ||= total_commits.fdiv(committers).round(2)
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/app/views/gitolite_public_keys/_ssh_keys.html.slim:
--------------------------------------------------------------------------------
1 | table.list
2 | - if ssh_keys.empty?
3 | tr
4 | td
5 | label = l :label_no_public_keys
6 | - else
7 | tbody
8 | - ssh_keys.each do |key|
9 | tr class="#{'highlight' if @gitolite_public_key == key}"
10 | td.name
11 | - used_in = []
12 | - if defined?(deploy_keys) && deploy_keys
13 | - key.repository_deployment_credentials.each do |deployment_credential|
14 | - used_in << deployment_credential.repository.name
15 | - if used_in.count.positive?
16 | = tag.acronym h(key), title: used_in.sort.join(', ')
17 | - else
18 | = h key
19 | td.description
20 | i.fas.fa-check style="color: green; margin-left: 5px; margin-right: 5px;"
21 | = key.fingerprint
22 | - if params[:id]
23 | td style="text-align: left;"
24 | = key.gitolite_path
25 | - if defined?(deploy_keys) && deploy_keys
26 | td.tick
27 | - if key.delete_when_unused
28 | i.fas.fa-minus-circle title="#{l :label_deployment_credential_delete_when_unused}"
29 | td.buttons style="width: 10%;"
30 | = link_to l(:button_delete), public_key_path(key, user_id: params[:id]),
31 | method: 'delete',
32 | class: 'icon icon-del',
33 | data: { confirm: l(:text_gitolite_key_destroy_confirmation, title: keylabel(key)) }
34 |
--------------------------------------------------------------------------------
/app/forms/plugin_settings_validation/mailing_list_config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module PluginSettingsValidation
4 | module MailingListConfig
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | # Git Mailing List Config
9 | add_accessor :gitolite_notify_by_default,
10 | :gitolite_notify_global_prefix,
11 | :gitolite_notify_global_sender_address,
12 | :gitolite_notify_global_include,
13 | :gitolite_notify_global_exclude
14 |
15 | before_validation do
16 | self.gitolite_notify_global_include = filter_email_list gitolite_notify_global_include
17 | self.gitolite_notify_global_exclude = filter_email_list gitolite_notify_global_exclude
18 | end
19 |
20 | validates :gitolite_notify_by_default, presence: true, inclusion: { in: RedmineGitHosting::Validators::BOOLEAN_FIELDS }
21 | validates :gitolite_notify_global_sender_address, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
22 | validate :git_notifications_intersection
23 | end
24 |
25 | private
26 |
27 | # Validate intersection of global_include/global_exclude
28 | #
29 | def git_notifications_intersection
30 | intersection = gitolite_notify_global_include & gitolite_notify_global_exclude
31 | return unless intersection.count.positive?
32 |
33 | errors.add :base, 'duplicated entries detected in gitolite_notify_global_include and gitolite_notify_global_exclude'
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/spec/root_spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | if ENV['COVERAGE']
4 | require 'simplecov'
5 |
6 | ## Start Simplecov
7 | SimpleCov.start 'rails' do
8 | add_group 'Redmine Git Hosting', 'plugins/redmine_git_hosting'
9 | end
10 | end
11 |
12 | unless defined? 'HOME_BASE_DIR'
13 | HOME_BASE_DIR = RUBY_PLATFORM.include?('darwin') ? '/Users' : '/home'
14 | end
15 |
16 | ## Load Redmine App
17 | ENV['RAILS_ENV'] = 'test'
18 | require File.expand_path "#{File.dirname __FILE__}/../config/environment"
19 | require 'rspec/rails'
20 |
21 | ## Load FactoryBots factories
22 | Dir[Rails.root.join('plugins/*/spec/factories/**/*.rb')].sort.each { |f| require f }
23 |
24 | Dir[Rails.root.join('plugins/*/spec/support/**/*.rb')].sort.each { |f| require f }
25 |
26 | ## Configure RSpec
27 | RSpec.configure do |config|
28 | config.include FactoryBot::Syntax::Methods
29 |
30 | config.infer_spec_type_from_file_location!
31 |
32 | config.color = true
33 | config.fail_fast = false
34 |
35 | config.expect_with :rspec do |c|
36 | c.syntax = :expect
37 | end
38 |
39 | config.before :suite do
40 | DatabaseCleaner.clean_with :truncation
41 | end
42 |
43 | config.before :each do
44 | DatabaseCleaner.strategy = :transaction
45 | end
46 |
47 | config.before :each do
48 | DatabaseCleaner.start
49 | end
50 |
51 | config.after :each do
52 | DatabaseCleaner.clean
53 | end
54 | end
55 |
56 | # Disable Test::Unit automatic runner
57 | Test::Unit::AutoRunner.need_auto_run = false if defined?(Test::Unit::AutoRunner)
58 |
--------------------------------------------------------------------------------
/lib/redmine_git_hosting/plugins/gitolite_plugin.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineGitHosting::Plugins
4 | class GitolitePlugin
5 | class << self
6 | def plugins
7 | @plugins ||= []
8 | end
9 |
10 | def all_plugins
11 | sweepers + extenders
12 | end
13 |
14 | def sweepers
15 | plugins.find { |p| p.name.demodulize == 'BaseSweeper' }.subclasses
16 | end
17 |
18 | def extenders
19 | plugins.find { |p| p.name.demodulize == 'BaseExtender' }.subclasses
20 | end
21 |
22 | def inherited(klass)
23 | @plugins ||= []
24 | @plugins << klass
25 | end
26 | end
27 |
28 | private
29 |
30 | def logger
31 | RedmineGitHosting.logger
32 | end
33 |
34 | def repository_empty?
35 | RedmineGitHosting::Commands.sudo_repository_empty? gitolite_repo_path
36 | end
37 |
38 | def directory_exists?(dir)
39 | RedmineGitHosting::Commands.sudo_dir_exists? dir
40 | end
41 |
42 | def sudo_git(*params)
43 | cmd = RedmineGitHosting::Commands.sudo_git_args_for_repo(gitolite_repo_path, git_args).concat(params)
44 | RedmineGitHosting::Commands.capture cmd, **git_opts
45 | end
46 |
47 | # You may override this method to prepend args like environment variables
48 | # to the git command.
49 | #
50 | def git_args
51 | []
52 | end
53 |
54 | # You may override this method to pass opts to Open3.capture.
55 | #
56 | def git_opts
57 | {}
58 | end
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/app/models/repository_protected_branche.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RepositoryProtectedBranche < ActiveRecord::Base
4 | include Redmine::SafeAttributes
5 | VALID_PERMS = ['RW+', 'RW', 'R', '-'].freeze
6 | DEFAULT_PERM = 'RW+'
7 |
8 | acts_as_positioned
9 |
10 | ## Attributes
11 | safe_attributes 'path', 'permissions', 'position'
12 |
13 | ## Relations
14 | belongs_to :repository
15 | has_many :protected_branches_members, foreign_key: :protected_branch_id, dependent: :destroy
16 | has_many :members, through: :protected_branches_members, source: :principal
17 |
18 | ## Validations
19 | validates :repository_id, presence: true
20 | validates :path, presence: true, uniqueness: { case_sensitive: true, scope: %i[permissions repository_id] }
21 | validates :permissions, presence: true, inclusion: { in: VALID_PERMS }
22 |
23 | ## Scopes
24 | default_scope { order position: :asc }
25 | scope :sorted, -> { order :path }
26 |
27 | class << self
28 | def clone_from(parent)
29 | parent = find_by id: parent unless parent.is_a? RepositoryProtectedBranche
30 | copy = new
31 | copy.attributes = parent.attributes
32 | copy.repository = parent.repository
33 | copy
34 | end
35 | end
36 |
37 | # Accessors
38 | #
39 | def users
40 | members.select { |m| m.instance_of? User }.uniq
41 | end
42 |
43 | def groups
44 | members.select { |m| m.instance_of? Group }.uniq
45 | end
46 |
47 | def allowed_users
48 | users.map(&:gitolite_identifier).sort
49 | end
50 | end
51 |
--------------------------------------------------------------------------------