diff --git a/app/assets/javascripts/repository/components/web_ide_link.vue b/app/assets/javascripts/repository/components/web_ide_link.vue deleted file mode 100644 index 6549d5a387860add3705082c993f22ef504dd296..0000000000000000000000000000000000000000 --- a/app/assets/javascripts/repository/components/web_ide_link.vue +++ /dev/null @@ -1,47 +0,0 @@ - - - diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js index 187bbfed125624ac55ccb685bd266e31a92466f2..7f72524b6fe136c36eed4e4295cf6354b8060b8d 100644 --- a/app/assets/javascripts/repository/index.js +++ b/app/assets/javascripts/repository/index.js @@ -1,30 +1,22 @@ import Vue from 'vue'; -import { escapeFileUrl } from '../lib/utils/url_utility'; +import { escapeFileUrl, joinPaths, webIDEUrl } from '../lib/utils/url_utility'; import createRouter from './router'; import App from './components/app.vue'; import Breadcrumbs from './components/breadcrumbs.vue'; import LastCommit from './components/last_commit.vue'; import TreeActionLink from './components/tree_action_link.vue'; -import WebIdeLink from './components/web_ide_link.vue'; +import WebIdeLink from '~/vue_shared/components/web_ide_link.vue'; import DirectoryDownloadLinks from './components/directory_download_links.vue'; import apolloProvider from './graphql'; import { setTitle } from './utils/title'; import { updateFormAction } from './utils/dom'; -import { parseBoolean } from '../lib/utils/common_utils'; +import { convertObjectPropsToCamelCase, parseBoolean } from '../lib/utils/common_utils'; import { __ } from '../locale'; export default function setupVueRepositoryList() { const el = document.getElementById('js-tree-list'); const { dataset } = el; - const { - canPushCode, - projectPath, - projectShortPath, - forkPath, - ref, - escapedRef, - fullName, - } = dataset; + const { projectPath, projectShortPath, ref, escapedRef, fullName } = dataset; const router = createRouter(projectPath, escapedRef); apolloProvider.clients.defaultClient.cache.writeData({ @@ -121,6 +113,10 @@ export default function setupVueRepositoryList() { const webIdeLinkEl = document.getElementById('js-tree-web-ide-link'); if (webIdeLinkEl) { + const { ideBasePath, ...options } = convertObjectPropsToCamelCase( + JSON.parse(webIdeLinkEl.dataset.options), + ); + // eslint-disable-next-line no-new new Vue({ el: webIdeLinkEl, @@ -128,10 +124,10 @@ export default function setupVueRepositoryList() { render(h) { return h(WebIdeLink, { props: { - projectPath, - refSha: ref, - forkPath, - canPushCode: parseBoolean(canPushCode), + webIdeUrl: webIDEUrl( + joinPaths('/', ideBasePath, 'edit', ref, '-', this.$route.params.path || '', '/'), + ), + ...options, }, }); }, diff --git a/app/assets/javascripts/vue_shared/components/actions_button.vue b/app/assets/javascripts/vue_shared/components/actions_button.vue new file mode 100644 index 0000000000000000000000000000000000000000..f333ab49ead02409a195715b5b7c24badae1f6ea --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/actions_button.vue @@ -0,0 +1,90 @@ + + + diff --git a/app/assets/javascripts/vue_shared/components/web_ide_link.vue b/app/assets/javascripts/vue_shared/components/web_ide_link.vue new file mode 100644 index 0000000000000000000000000000000000000000..8307c6d3b551c94f3cec3e15791da678e7c1cefa --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/web_ide_link.vue @@ -0,0 +1,118 @@ + + + diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index b6039b971a93489653e5f2530d4dbaec81901b77..a9c1652d00dfece1e4698900a88b8e585dbd5e35 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -542,3 +542,13 @@ fieldset[disabled] .btn, .btn-no-padding { padding: 0; } + +// This class helps convert `.gl-button` children so that they consistently +// match the style of `.btn` elements which might be around them. Ideally we +// wouldn't need this class. +// +// Remove by upgrading all buttons in a container to use the new `.gl-button` style. +.gl-button-deprecated-adapter .gl-button { + box-shadow: none; + border-width: 1px; +} diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index d41f10202a37d256fb5ccab3f24f818ed7fd539a..0f01f3b38aaeb9b68be4483cc923fa74333e7038 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -1135,3 +1135,17 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu { width: $gl-dropdown-width-wide; } } + +.gl-dropdown-item-deprecated-adapter { + .dropdown-item { + align-items: flex-start; + + .gl-new-dropdown-item-text-primary { + @include gl-font-weight-bold; + } + + .gl-new-dropdown-item-text-secondary { + color: inherit; + } + } +} diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb index 8653fe3b6ede31498aea16e383b0dff16ac492e0..ea4d3e861be05a9b8a0b557a1eda293ad656afec 100644 --- a/app/controllers/profiles/preferences_controller.rb +++ b/app/controllers/profiles/preferences_controller.rb @@ -51,6 +51,7 @@ def preferences_param_names :view_diffs_file_by_file, :tab_width, :sourcegraph_enabled, + :gitpod_enabled, :render_whitespace_in_code ] end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 9a28088ccf665b0dc8623eb83233fc54ae69ca61..248d5755d92f19603355b923e98cc65e8f2c0291 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -104,6 +104,7 @@ def user_params :bio, :email, :role, + :gitpod_enabled, :hide_no_password, :hide_no_ssh_key, :hide_project_limit, diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index b56453d60e981f3455ea7598260a5345abf82553..9245cc1cb1c3d38611bbf46af0b9fc64c40553c4 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -222,6 +222,8 @@ def visible_attributes :gitaly_timeout_default, :gitaly_timeout_medium, :gitaly_timeout_fast, + :gitpod_enabled, + :gitpod_url, :grafana_enabled, :grafana_url, :gravatar_enabled, diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index ff1f9d6d9fe2b33e03c2f89b0d2c130c8e73bad1..2c406641882e4ce82a5d0367d2f0d889c0f97392 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -80,6 +80,13 @@ def language_choices ) end + def integration_views + [].tap do |views| + views << 'gitpod' if Gitlab::Gitpod.feature_and_settings_enabled? + views << 'sourcegraph' if Gitlab::Sourcegraph.feature_available? && Gitlab::CurrentSettings.sourcegraph_enabled + end + end + private # Ensure that anyone adding new options updates `DASHBOARD_CHOICES` too diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 90a5b6da4c73319d88db191ea31d59d20aa7bd63..7644ed783eb7b877e1337793e80a50d9d9620bd8 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -191,16 +191,46 @@ def breadcrumb_data_attributes def vue_file_list_data(project, ref) { - can_push_code: current_user&.can?(:push_code, project) && "true", project_path: project.full_path, project_short_path: project.path, - fork_path: current_user&.fork_of(project)&.full_path, ref: ref, escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref), full_name: project.name_with_namespace } end + def ide_base_path(project) + can_push_code = current_user&.can?(:push_code, project) + fork_path = current_user&.fork_of(project)&.full_path + + if can_push_code + project.full_path + else + fork_path || project.full_path + end + end + + def vue_ide_link_data(project, ref) + can_collaborate = can_collaborate_with_project?(project) + can_create_mr_from_fork = can?(current_user, :fork_project, project) && can?(current_user, :create_merge_request_in, project) + show_web_ide_button = (can_collaborate || current_user&.already_forked?(project) || can_create_mr_from_fork) + + { + ide_base_path: ide_base_path(project), + needs_to_fork: !can_collaborate && !current_user&.already_forked?(project), + show_web_ide_button: show_web_ide_button, + show_gitpod_button: show_web_ide_button && Gitlab::Gitpod.feature_and_settings_enabled?(project), + gitpod_url: full_gitpod_url(project, ref), + gitpod_enabled: current_user&.gitpod_enabled + } + end + + def full_gitpod_url(project, ref) + return "" unless Gitlab::Gitpod.feature_and_settings_enabled?(project) + + "#{Gitlab::CurrentSettings.gitpod_url}##{project_tree_url(project, tree_join(ref, @path || ''))}" + end + def directory_download_links(project, ref, archive_prefix) Gitlab::Workhorse::ARCHIVE_FORMATS.map do |fmt| { diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index b660c81488189a1a0bb4347fb54f00d20b6deffa..e9a3dcf39df2d64c2534d9a54e588dc008eaf02a 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -132,6 +132,11 @@ def self.repository_storages_weighted_attributes presence: true, if: :sourcegraph_enabled + validates :gitpod_url, + presence: true, + addressable_url: { enforce_sanitization: true }, + if: :gitpod_enabled + validates :snowplow_collector_hostname, presence: true, hostname: true, diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index 206123bb4704eea7278c525a00ffc1f583ea2c57..7a869d16a317a32ba792f0a7189057b4b2cda196 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -74,6 +74,8 @@ def defaults gitaly_timeout_default: 55, gitaly_timeout_fast: 10, gitaly_timeout_medium: 30, + gitpod_enabled: false, + gitpod_url: 'https://gitpod.io/', gravatar_enabled: Settings.gravatar['enabled'], group_download_export_limit: 1, group_export_limit: 6, diff --git a/app/models/user.rb b/app/models/user.rb index 9b8fe4881adf99fafb81e872fed7d7645d857936..0a784b30d8f0537a24faedbfde857d0224d238bb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -279,6 +279,7 @@ def update_tracked_fields!(request) :view_diffs_file_by_file, :view_diffs_file_by_file=, :tab_width, :tab_width=, :sourcegraph_enabled, :sourcegraph_enabled=, + :gitpod_enabled, :gitpod_enabled=, :setup_for_company, :setup_for_company=, :render_whitespace_in_code, :render_whitespace_in_code=, :experience_level, :experience_level=, diff --git a/app/views/admin/application_settings/_gitpod.html.haml b/app/views/admin/application_settings/_gitpod.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..bbad5155ada7a6f0211782642c7fb9dbadb7377d --- /dev/null +++ b/app/views/admin/application_settings/_gitpod.html.haml @@ -0,0 +1,30 @@ +- return unless Gitlab::Gitpod.feature_available? +- expanded = integration_expanded?('gitpod_') +- gitpod_link = link_to("Gitpod#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}".html_safe, 'https://gitpod.io/', target: '_blank', rel: 'noopener noreferrer') + +%section.settings.no-animate#js-gitpod-settings{ class: ('expanded' if expanded) } + .settings-header + %h4 + = _('Gitpod') + %button.btn.btn-default.js-settings-toggle{ type: 'button' } + = expanded ? _('Collapse') : _('Expand') + %p + = s_('Enable %{gitpod_link} integration to launch a development environment in your browser directly from GitLab.').html_safe % { gitpod_link: gitpod_link } + = link_to sprite_icon('question-o'), help_page_path('integration/gitpod.md'), target: '_blank', class: 'has-tooltip', title: _('More information') + + + .settings-content + = form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-gitpod-settings'), html: { class: 'fieldset-form' } do |f| + = form_errors(@application_setting) + + %fieldset + .form-group + .form-check + = f.check_box :gitpod_enabled, class: 'form-check-input' + = f.label :gitpod_enabled, s_('Gitpod|Enable Gitpod integration'), class: 'form-check-label' + .form-group + = f.label :gitpod_url, s_('Gitpod|Gitpod URL'), class: 'label-bold' + = f.text_field :gitpod_url, class: 'form-control', placeholder: s_('Gitpod|e.g. https://gitpod.example.com') + .form-text.text-muted + = s_('Gitpod|Add the URL to your Gitpod instance configured to read your GitLab projects.') + = f.submit s_('Save changes'), class: 'btn btn-success' diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml index 788dc0b0f1bab7d8315d821248a70b4a544230ad..823cee09d4bbfa012489fa948feb1f10ebe51017 100644 --- a/app/views/admin/application_settings/general.html.haml +++ b/app/views/admin/application_settings/general.html.haml @@ -117,6 +117,7 @@ #js-maintenance-mode-settings = render_if_exists 'admin/application_settings/elasticsearch_form' += render 'admin/application_settings/gitpod' = render 'admin/application_settings/plantuml' = render 'admin/application_settings/sourcegraph' = render_if_exists 'admin/application_settings/slack' diff --git a/app/views/profiles/preferences/_gitpod.html.haml b/app/views/profiles/preferences/_gitpod.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..69c9443ebbb08a97a4f9c769de8dfdaf84a83028 --- /dev/null +++ b/app/views/profiles/preferences/_gitpod.html.haml @@ -0,0 +1,11 @@ +- gitpod_link = link_to("Gitpod#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}".html_safe, 'https://gitpod.io/', target: '_blank', rel: 'noopener noreferrer') + +%label.label-bold#gitpod + = s_('Gitpod') += link_to sprite_icon('question-o'), help_page_path('integration/gitpod.md'), target: '_blank', class: 'has-tooltip', title: _('More information') +.form-group.form-check + = f.check_box :gitpod_enabled, class: 'form-check-input' + = f.label :gitpod_enabled, class: 'form-check-label' do + = s_('Gitpod|Enable Gitpod integration').html_safe + .form-text.text-muted + = s_('Enable %{gitpod_link} integration to launch a development environment in your browser directly from GitLab.').html_safe % { gitpod_link: gitpod_link } diff --git a/app/views/profiles/preferences/_integrations.html.haml b/app/views/profiles/preferences/_integrations.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..037fe5df263ed0983031f7c7fedcf9a5124c3346 --- /dev/null +++ b/app/views/profiles/preferences/_integrations.html.haml @@ -0,0 +1,18 @@ +- views = integration_views +- return unless views.any? + +.col-sm-12 + %hr + +.col-lg-4.profile-settings-sidebar#integrations + %h4.gl-mt-0 + = s_('Preferences|Integrations') + %p + = s_('Preferences|Customize integrations with third party services.') + = succeed '.' do + = link_to _('Learn more'), help_page_path('user/profile/preferences.md', anchor: 'integrations'), target: '_blank' + +.col-lg-8 + - views.each do |view| + = render view, f: f + diff --git a/app/views/profiles/preferences/_sourcegraph.html.haml b/app/views/profiles/preferences/_sourcegraph.html.haml index 595b70befcce538ac7c3caa04c766ead8744db91..fdd0be22664957c7d971b6a3a972e2f185973a93 100644 --- a/app/views/profiles/preferences/_sourcegraph.html.haml +++ b/app/views/profiles/preferences/_sourcegraph.html.haml @@ -1,26 +1,10 @@ -- return unless Gitlab::Sourcegraph::feature_available? && Gitlab::CurrentSettings.sourcegraph_enabled -- sourcegraph_url = Gitlab::CurrentSettings.sourcegraph_url - -.col-sm-12 - %hr - -.col-lg-4.profile-settings-sidebar#integrations - %h4.gl-mt-0 - = s_('Preferences|Integrations') - %p - = s_('Preferences|Customize integrations with third party services.') - = succeed '.' do - = link_to _('Learn more'), help_page_path('user/profile/preferences.md', anchor: 'integrations'), target: '_blank' -.col-lg-8 - %label.label-bold - = s_('Preferences|Sourcegraph') - = link_to sprite_icon('question-o'), help_page_path('user/profile/preferences.md', anchor: 'sourcegraph'), target: '_blank', class: 'has-tooltip', title: _('More information') - .form-group.form-check - = f.check_box :sourcegraph_enabled, class: 'form-check-input' - = f.label :sourcegraph_enabled, class: 'form-check-label' do - - link_start = ''.html_safe % { url: sourcegraph_url } - - link_end = ''.html_safe - = s_('Preferences|Enable integrated code intelligence on code views').html_safe % { link_start: link_start, link_end: link_end } - .form-text.text-muted - = sourcegraph_url_message - = sourcegraph_experimental_message +%label.label-bold + = s_('Preferences|Sourcegraph') += link_to sprite_icon('question-o'), help_page_path('user/profile/preferences.md', anchor: 'sourcegraph'), target: '_blank', class: 'has-tooltip', title: _('More information') +.form-group.form-check + = f.check_box :sourcegraph_enabled, class: 'form-check-input' + = f.label :sourcegraph_enabled, class: 'form-check-label' do + = s_('Preferences|Enable integrated code intelligence on code views').html_safe + .form-text.text-muted + = sourcegraph_url_message + = sourcegraph_experimental_message diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index 472185f0b051d705198bc709cce242d387ba80e9..2c705886f4728d1b65a08d58327ba0639c2540d6 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -138,7 +138,7 @@ .form-text.text-muted = s_('Preferences|For example: 30 mins ago.') - = render 'sourcegraph', f: f + = render 'integrations', f: f .col-lg-4.profile-settings-sidebar .col-lg-8 diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index eab6d750a02694e7ae853b787ead1fb1dba5ba02..268858f8ff8946fa8c920e8b8615041ce16b512b 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -1,5 +1,6 @@ - can_collaborate = can_collaborate_with_project?(@project) - can_create_mr_from_fork = can?(current_user, :fork_project, @project) && can?(current_user, :create_merge_request_in, @project) +- can_visit_ide = can_collaborate || current_user&.already_forked?(@project) .tree-ref-container .tree-ref-holder @@ -14,12 +15,12 @@ = render 'projects/find_file_link' - - if can_collaborate || current_user&.already_forked?(@project) - #js-tree-web-ide-link.d-inline-block - - elsif can_create_mr_from_fork - = link_to '#modal-confirm-fork', class: 'btn btn-default qa-web-ide-button', data: { target: '#modal-confirm-fork', toggle: 'modal'} do - = _('Web IDE') - = render 'shared/confirm_fork_modal', fork_path: ide_fork_and_edit_path(@project, @ref, @path) + - if can_visit_ide || can_create_mr_from_fork + #js-tree-web-ide-link.d-inline-block{ data: { options: vue_ide_link_data(@project, @ref).to_json } } + - if !can_visit_ide + = render 'shared/confirm_fork_modal', fork_path: ide_fork_and_edit_path(@project, @ref, @path) + - unless current_user&.gitpod_enabled + = render 'shared/gitpod/enable_gitpod_modal' - if show_xcode_link?(@project) .project-action-button.project-xcode.inline< diff --git a/app/views/shared/gitpod/_enable_gitpod_modal.html.haml b/app/views/shared/gitpod/_enable_gitpod_modal.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..a6bd1d10e433ebc9c6905320a1bcd27d2dd0c6ca --- /dev/null +++ b/app/views/shared/gitpod/_enable_gitpod_modal.html.haml @@ -0,0 +1,12 @@ +#modal-enable-gitpod.modal.qa-enable-gitpod-modal + .modal-dialog + .modal-content + .modal-header + %h3.page-title= _('Enable Gitpod?') + %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') } + %span{ "aria-hidden": true } × + .modal-body.p-3 + %p= (_("To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}.") % { user_prefs: link_to(_('user preferences'), profile_preferences_path(anchor: 'gitpod')) }).html_safe + .modal-footer + = link_to _('Cancel'), '#', class: "btn btn-cancel", "data-dismiss" => "modal" + = link_to _('Enable Gitpod'), profile_path(user: { gitpod_enabled: true}), class: 'btn btn-success', method: :put diff --git a/changelogs/unreleased/37985-add-gitpod-button-to-open-project-in-gitpod.yml b/changelogs/unreleased/37985-add-gitpod-button-to-open-project-in-gitpod.yml new file mode 100644 index 0000000000000000000000000000000000000000..4d4117cdca747fd198a3f26d620974b9a493bc8a --- /dev/null +++ b/changelogs/unreleased/37985-add-gitpod-button-to-open-project-in-gitpod.yml @@ -0,0 +1,5 @@ +--- +title: Add Gitpod integration +merge_request: 37985 +author: Cornelius Ludmann @corneliusludmann +type: added diff --git a/config/feature_flags/development/gitpod.yml b/config/feature_flags/development/gitpod.yml new file mode 100644 index 0000000000000000000000000000000000000000..148ea7294ba434c7e01ed1c46cff926938ea1f84 --- /dev/null +++ b/config/feature_flags/development/gitpod.yml @@ -0,0 +1,7 @@ +--- +name: gitpod +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37985 +rollout_issue_url: +group: group::editor +type: development +default_enabled: false \ No newline at end of file diff --git a/db/migrate/20200811154630_add_gitpod_application_settings.rb b/db/migrate/20200811154630_add_gitpod_application_settings.rb new file mode 100644 index 0000000000000000000000000000000000000000..e4211d25d0b8ca98f240165fd97a9d3e4540a0bb --- /dev/null +++ b/db/migrate/20200811154630_add_gitpod_application_settings.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class AddGitpodApplicationSettings < ActiveRecord::Migration[6.0] + DOWNTIME = false + + # rubocop:disable Migration/AddLimitToTextColumns + # limit is added in 20200727154631_add_gitpod_application_settings_text_limit + def change + add_column :application_settings, :gitpod_enabled, :boolean, default: false, null: false + add_column :application_settings, :gitpod_url, :text, default: 'https://gitpod.io/', null: true + end + # rubocop:enable Migration/AddLimitToTextColumns +end diff --git a/db/migrate/20200811154631_add_gitpod_application_settings_text_limit.rb b/db/migrate/20200811154631_add_gitpod_application_settings_text_limit.rb new file mode 100644 index 0000000000000000000000000000000000000000..1f43b5d88d56a8182623b85186e48034bf299572 --- /dev/null +++ b/db/migrate/20200811154631_add_gitpod_application_settings_text_limit.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddGitpodApplicationSettingsTextLimit < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_text_limit :application_settings, :gitpod_url, 255 + end + + def down + remove_text_limit :application_settings, :gitpod_url + end +end diff --git a/db/migrate/20200811154632_add_gitpod_user_preferences.rb b/db/migrate/20200811154632_add_gitpod_user_preferences.rb new file mode 100644 index 0000000000000000000000000000000000000000..0392c80d39c4488eb8a7246d669aaf9c7ca1cef3 --- /dev/null +++ b/db/migrate/20200811154632_add_gitpod_user_preferences.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddGitpodUserPreferences < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def change + add_column :user_preferences, :gitpod_enabled, :boolean, default: false, null: false + end +end diff --git a/db/schema_migrations/20200811154630 b/db/schema_migrations/20200811154630 new file mode 100644 index 0000000000000000000000000000000000000000..0498382ef2eb0dc9277128e2d7ba453a49abab4b --- /dev/null +++ b/db/schema_migrations/20200811154630 @@ -0,0 +1 @@ +c04fe7e1a56bdcd41b5e1af346f9bfcae170d601954c4a0bcfcc9aea19d55528 \ No newline at end of file diff --git a/db/schema_migrations/20200811154631 b/db/schema_migrations/20200811154631 new file mode 100644 index 0000000000000000000000000000000000000000..1817460cd30eed85a3ac92b82aae96617d02a556 --- /dev/null +++ b/db/schema_migrations/20200811154631 @@ -0,0 +1 @@ +0ce17a8ad6c5ca5bba49ff522fede400fe6666490157af123ad98a7643f3ce01 \ No newline at end of file diff --git a/db/schema_migrations/20200811154632 b/db/schema_migrations/20200811154632 new file mode 100644 index 0000000000000000000000000000000000000000..bb1167e31826afea4caf928c551ae292f21d0656 --- /dev/null +++ b/db/schema_migrations/20200811154632 @@ -0,0 +1 @@ +523f200c635e37ee1ac52257ffd45443a3e17bfe993d22775a5377865e044a46 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index ec8147b1caed3156f2aefcf2895cdd72b4fd1723..408e6c253125f60e7ac9a8dce840503af56fae26 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -9272,6 +9272,9 @@ CREATE TABLE public.application_settings ( enforce_namespace_storage_limit boolean DEFAULT false NOT NULL, container_registry_delete_tags_service_timeout integer DEFAULT 250 NOT NULL, elasticsearch_client_request_timeout integer DEFAULT 0 NOT NULL, + gitpod_enabled boolean DEFAULT false NOT NULL, + gitpod_url text DEFAULT 'https://gitpod.io/'::text, + CONSTRAINT check_2dba05b802 CHECK ((char_length(gitpod_url) <= 255)), CONSTRAINT check_51700b31b5 CHECK ((char_length(default_branch_name) <= 255)), CONSTRAINT check_9c6c447a13 CHECK ((char_length(maintenance_mode_message) <= 255)), CONSTRAINT check_d03919528d CHECK ((char_length(container_registry_vendor) <= 255)), @@ -16255,7 +16258,8 @@ CREATE TABLE public.user_preferences ( tab_width smallint, feature_filter_type bigint, experience_level smallint, - view_diffs_file_by_file boolean DEFAULT false NOT NULL + view_diffs_file_by_file boolean DEFAULT false NOT NULL, + gitpod_enabled boolean DEFAULT false NOT NULL ); CREATE SEQUENCE public.user_preferences_id_seq diff --git a/doc/integration/gitpod.md b/doc/integration/gitpod.md new file mode 100644 index 0000000000000000000000000000000000000000..f26483e3b5eb8e13df5f1d3d7910697913cc8f96 --- /dev/null +++ b/doc/integration/gitpod.md @@ -0,0 +1,74 @@ +--- +type: reference, how-to +stage: Create +group: Editor +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +--- + +# Gitpod Integration + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/228893) in GitLab 13.4. +> - It's [deployed behind a feature flag](#enable-or-disable-the-gitpod-integration), disabled by default. +> - It's enabled on GitLab.com. +> - It's recommended for production use. +> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#configure-your-gitlab-instance-with-gitpod). **(CORE ONLY)** + +CAUTION: **Warning:** +This feature might not be available to you. Check the **version history** note above for details. + +With [Gitpod](https://gitpod.io/) you can describe your dev environment as code to get fully set +up, compiled, and tested dev environments for any GitLab project. The dev environments are not only +automated but also prebuilt which means that Gitpod continuously builds your Git branches like a CI +server. By that you don’t have to wait for dependencies to be downloaded and builds to finish, but +you can start coding immediately. + +In short: With Gitpod you can start coding instantly on any project, branch, and merge request from +any device, at any time. + +![Gitpod interface](img/gitpod_web_interface_v13_4.png) + +You can launch Gitpod directly from GitLab by clicking the **Gitpod** button from the **Web IDE** +dropdown on the project page: + +![Gitpod Button on Project Page](img/gitpod_button_project_page_v13_4.png) + +To learn more about Gitpod, see their [features](https://www.gitpod.io/features/) and +[documentation](https://www.gitpod.io/docs/). + +To use the GitLab-Gitpod integration, you need to enable it from your user preferences: + +1. From the GitLab UI, click your avatar in the top-right corner, then click **Settings**. +1. On the left-hand nav, click **Preferences**. +1. Under **Integrations**, find the **Gitpod** section. +1. Check **Enable Gitpod**. + +Users of GitLab.com can enable it and start using straightaway. Users of GitLab self-managed instances +can follow the same steps once the integration has been enabled and configured by a GitLab administrator. + +## Configure your GitLab instance with Gitpod **(CORE ONLY)** + +If you are new to Gitpod, head over to the [Gitpod documentation](https://www.gitpod.io/docs/self-hosted/latest/self-hosted/) +and get your instance up and running. + +1. In GitLab, go to **Admin Area > Settings > Integrations**. +1. Expand the **Gitpod** configuration section. +1. Check **Enable Gitpod**. +1. Add your Gitpod instance URL (for example, `https://gitpod.example.com`). + +## Enable or disable the Gitpod integration **(CORE ONLY)** + +The Gitpod integration is under development and not ready for production use. It is deployed behind a +feature flag that is **disabled by default**. +[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md) +can enable it. + +To enable it: + +```ruby +Feature.enable(:gitpod) +``` + +To disable it: + +```ruby +Feature.disable(:gitpod) diff --git a/doc/integration/img/gitpod_button_project_page_v13_4.png b/doc/integration/img/gitpod_button_project_page_v13_4.png new file mode 100644 index 0000000000000000000000000000000000000000..55a70d891699d0377e8f0a0f4d2d86a16723cbaa Binary files /dev/null and b/doc/integration/img/gitpod_button_project_page_v13_4.png differ diff --git a/doc/integration/img/gitpod_web_interface_v13_4.png b/doc/integration/img/gitpod_web_interface_v13_4.png new file mode 100644 index 0000000000000000000000000000000000000000..5cd9a6aad0fdf35b4f68bb9529d40f417872adba Binary files /dev/null and b/doc/integration/img/gitpod_web_interface_v13_4.png differ diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md index b94ae958d3b8c7f38f5013e85123258e11b91bd2..caf0775a7464d4e1b6653f66a42abf736debd8ac 100644 --- a/doc/user/profile/preferences.md +++ b/doc/user/profile/preferences.md @@ -182,6 +182,12 @@ Manage the availability of integrated code intelligence features powered by Sourcegraph. View [the Sourcegraph feature documentation](../../integration/sourcegraph.md#enable-sourcegraph-in-user-preferences) for more information. +### Gitpod + +Enable and disable the [GitLab-Gitpod integration](../../integration/gitpod.md). This is only +visible after the integration is configured by a GitLab administrator. View +[the Gitpod feature documentation](../../integration/gitpod.md) for more information. +