From 1bf106b17275c87cf8baa199599f113f154a52fe Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 20 Jul 2021 18:08:46 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../branches/components/new_branch_form.vue | 201 +++++++++++++++ .../branches/components/project_dropdown.vue | 2 +- .../jira_connect/branches/constants.js | 18 ++ .../mutations/create_branch.mutation.graphql | 6 + .../jira_connect/branches/index.js | 36 +++ app/assets/javascripts/jobs/store/utils.js | 16 +- .../token_access/components/token_access.vue | 7 +- .../components/token_projects_table.vue | 1 - .../registrations/welcome_controller.rb | 2 +- .../devise/shared/_email_opted_in.html.haml | 4 +- doc/user/admin_area/license.md | 44 ++-- doc/user/analytics/index.md | 4 +- .../group/value_stream_analytics/index.md | 7 +- .../packages/composer_repository/index.md | 2 +- doc/user/packages/container_registry/index.md | 22 +- doc/user/packages/maven_repository/index.md | 8 +- doc/user/packages/nuget_repository/index.md | 4 +- .../project/repository/branches/default.md | 3 +- doc/user/project/wiki/index.md | 15 ++ lib/support/nginx/gitlab-pages-ssl | 15 +- lib/support/nginx/gitlab-ssl | 18 +- lib/support/nginx/registry-ssl | 21 +- locale/gitlab.pot | 15 ++ .../registrations/welcome_controller_spec.rb | 22 +- spec/features/registrations/welcome_spec.rb | 21 -- .../components/new_branch_form_spec.js | 230 ++++++++++++++++++ .../token_access/token_access_spec.js | 17 +- .../welcome/show.html.haml_spec.rb | 24 +- 28 files changed, 631 insertions(+), 154 deletions(-) create mode 100644 app/assets/javascripts/jira_connect/branches/components/new_branch_form.vue create mode 100644 app/assets/javascripts/jira_connect/branches/graphql/mutations/create_branch.mutation.graphql create mode 100644 app/assets/javascripts/jira_connect/branches/index.js delete mode 100644 spec/features/registrations/welcome_spec.rb create mode 100644 spec/frontend/jira_connect/branches/components/new_branch_form_spec.js diff --git a/app/assets/javascripts/jira_connect/branches/components/new_branch_form.vue b/app/assets/javascripts/jira_connect/branches/components/new_branch_form.vue new file mode 100644 index 00000000000..b2cc3a315cc --- /dev/null +++ b/app/assets/javascripts/jira_connect/branches/components/new_branch_form.vue @@ -0,0 +1,201 @@ + + + diff --git a/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue b/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue index c1f57be7f97..2accb46db60 100644 --- a/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue +++ b/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue @@ -60,7 +60,7 @@ export default { }, }, methods: { - async onProjectSelect(project) { + onProjectSelect(project) { this.$emit('change', project); }, onError({ message } = {}) { diff --git a/app/assets/javascripts/jira_connect/branches/constants.js b/app/assets/javascripts/jira_connect/branches/constants.js index 987c8d356b4..7095f123a9e 100644 --- a/app/assets/javascripts/jira_connect/branches/constants.js +++ b/app/assets/javascripts/jira_connect/branches/constants.js @@ -1,2 +1,20 @@ +import { __, s__ } from '~/locale'; + export const BRANCHES_PER_PAGE = 20; export const PROJECTS_PER_PAGE = 20; + +export const I18N_NEW_BRANCH_PAGE_TITLE = __('New branch'); +export const I18N_NEW_BRANCH_LABEL_DROPDOWN = __('Project'); +export const I18N_NEW_BRANCH_LABEL_BRANCH = __('Branch name'); +export const I18N_NEW_BRANCH_LABEL_SOURCE = __('Source branch'); +export const I18N_NEW_BRANCH_SUBMIT_BUTTON_TEXT = __('Create branch'); + +export const CREATE_BRANCH_ERROR_GENERIC = s__( + 'JiraConnect|Failed to create branch. Please try again.', +); +export const CREATE_BRANCH_ERROR_WITH_CONTEXT = s__('JiraConnect|Failed to create branch.'); + +export const CREATE_BRANCH_SUCCESS_ALERT = { + title: s__('JiraConnect|New branch was successfully created.'), + message: s__('JiraConnect|You can now close this window and return to Jira.'), +}; diff --git a/app/assets/javascripts/jira_connect/branches/graphql/mutations/create_branch.mutation.graphql b/app/assets/javascripts/jira_connect/branches/graphql/mutations/create_branch.mutation.graphql new file mode 100644 index 00000000000..7e9cbda8317 --- /dev/null +++ b/app/assets/javascripts/jira_connect/branches/graphql/mutations/create_branch.mutation.graphql @@ -0,0 +1,6 @@ +mutation createBranch($name: String!, $projectPath: ID!, $ref: String!) { + createBranch(input: { name: $name, projectPath: $projectPath, ref: $ref }) { + clientMutationId + errors + } +} diff --git a/app/assets/javascripts/jira_connect/branches/index.js b/app/assets/javascripts/jira_connect/branches/index.js new file mode 100644 index 00000000000..b8fe255e310 --- /dev/null +++ b/app/assets/javascripts/jira_connect/branches/index.js @@ -0,0 +1,36 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import JiraConnectNewBranchForm from '~/jira_connect/branches/components/new_branch_form.vue'; +import createDefaultClient from '~/lib/graphql'; + +Vue.use(VueApollo); + +export default async function initJiraConnectBranches() { + const el = document.querySelector('.js-jira-connect-create-branch'); + if (!el) { + return null; + } + + const { initialBranchName } = el.dataset; + + const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient( + {}, + { + assumeImmutableResults: true, + }, + ), + }); + + return new Vue({ + el, + apolloProvider, + render(createElement) { + return createElement(JiraConnectNewBranchForm, { + props: { + initialBranchName, + }, + }); + }, + }); +} diff --git a/app/assets/javascripts/jobs/store/utils.js b/app/assets/javascripts/jobs/store/utils.js index 36391a4d433..3f6ab77e26c 100644 --- a/app/assets/javascripts/jobs/store/utils.js +++ b/app/assets/javascripts/jobs/store/utils.js @@ -132,7 +132,7 @@ export const logLinesParserLegacy = (lines = [], accumulator = []) => ); export const logLinesParser = (lines = [], previousTraceState = {}, prevParsedLines = []) => { - let currentLine = previousTraceState?.prevLineCount ?? 0; + let currentLineCount = previousTraceState?.prevLineCount ?? 0; let currentHeader = previousTraceState?.currentHeader; let isPreviousLineHeader = previousTraceState?.isPreviousLineHeader ?? false; const parsedLines = prevParsedLines.length > 0 ? prevParsedLines : []; @@ -141,27 +141,27 @@ export const logLinesParser = (lines = [], previousTraceState = {}, prevParsedLi for (let i = 0; i < lines.length; i += 1) { const line = lines[i]; // First run we can use the current index, later runs we have to retrieve the last number of lines - currentLine = previousTraceState?.prevLineCount ? currentLine + 1 : i + 1; + currentLineCount = previousTraceState?.prevLineCount ? currentLineCount + 1 : i + 1; if (line.section_header && !isPreviousLineHeader) { // If there's no previous line header that means we're at the root of the log isPreviousLineHeader = true; - parsedLines.push(parseHeaderLine(line, currentLine)); + parsedLines.push(parseHeaderLine(line, currentLineCount)); currentHeader = { index: parsedLines.length - 1 }; } else if (line.section_header && isPreviousLineHeader) { // If there's a current section, we can't push to the parsedLines array sectionsQueue.push(currentHeader); - currentHeader = parseHeaderLine(line, currentLine); // Let's parse the incoming header line + currentHeader = parseHeaderLine(line, currentLineCount); // Let's parse the incoming header line } else if (line.section && !line.section_duration) { // We're inside a collapsible section and want to parse a standard line if (currentHeader?.index) { // If the current section header is only an index, add the line as part of the lines // array of the current collapsible section - parsedLines[currentHeader.index].lines.push(parseLine(line, currentLine)); + parsedLines[currentHeader.index].lines.push(parseLine(line, currentLineCount)); } else { // Otherwise add it to the innermost collapsible section lines array - currentHeader.lines.push(parseLine(line, currentLine)); + currentHeader.lines.push(parseLine(line, currentLineCount)); } } else if (line.section && line.section_duration) { // NOTE: This marks the end of a section_header @@ -187,7 +187,7 @@ export const logLinesParser = (lines = [], previousTraceState = {}, prevParsedLi currentHeader = previousSection; } } else { - parsedLines.push(parseLine(line, currentLine)); + parsedLines.push(parseLine(line, currentLineCount)); } } @@ -197,7 +197,7 @@ export const logLinesParser = (lines = [], previousTraceState = {}, prevParsedLi isPreviousLineHeader, currentHeader, sectionsQueue, - prevLineCount: lines.length, + prevLineCount: currentLineCount, }, }; }; diff --git a/app/assets/javascripts/token_access/components/token_access.vue b/app/assets/javascripts/token_access/components/token_access.vue index 24565c441d8..e739ec37739 100644 --- a/app/assets/javascripts/token_access/components/token_access.vue +++ b/app/assets/javascripts/token_access/components/token_access.vue @@ -187,12 +187,7 @@ export default { /> diff --git a/app/controllers/registrations/welcome_controller.rb b/app/controllers/registrations/welcome_controller.rb index 303ee431a4d..6482d02761e 100644 --- a/app/controllers/registrations/welcome_controller.rb +++ b/app/controllers/registrations/welcome_controller.rb @@ -41,7 +41,7 @@ module Registrations end def update_params - params.require(:user).permit(:role, :other_role, :setup_for_company, :email_opted_in) + params.require(:user).permit(:role, :other_role, :setup_for_company) end def requires_confirmation?(user) diff --git a/app/views/devise/shared/_email_opted_in.html.haml b/app/views/devise/shared/_email_opted_in.html.haml index 6896ef21536..3817f9f651d 100644 --- a/app/views/devise/shared/_email_opted_in.html.haml +++ b/app/views/devise/shared/_email_opted_in.html.haml @@ -1,6 +1,6 @@ -- is_hidden = local_assigns.fetch(:hidden, Gitlab.dev_env_or_com?) +- return unless Gitlab.dev_env_or_com? -.gl-mb-3.js-email-opt-in{ class: is_hidden ? 'hidden' : '' } +.gl-mb-3.js-email-opt-in.hidden .gl-font-weight-bold.gl-mb-3 = _('Email updates (optional)') = f.check_box :email_opted_in diff --git a/doc/user/admin_area/license.md b/doc/user/admin_area/license.md index 57f643b75c7..0e6538b1339 100644 --- a/doc/user/admin_area/license.md +++ b/doc/user/admin_area/license.md @@ -5,31 +5,33 @@ info: To determine the technical writer assigned to the Stage/Group associated w type: howto --- -# Activate GitLab EE with a license **(PREMIUM SELF)** +# Activating GitLab EE -To activate all GitLab Enterprise Edition (EE) functionality, you need to upload -a license. It's only possible to activate GitLab Enterprise Edition, so first verify which edition -you are running. To verify, sign in to GitLab and browse to `/help`. The GitLab edition and version -are listed at the top of the **Help** page. +To enable features of GitLab Enterprise Edition (EE), you need to activate your instance. Ensure you are running an enterprise edition. To verify, sign in to GitLab and browse to `/help`. The GitLab edition and version are listed at the top of the **Help** page. -If you are running GitLab Community Edition (CE), upgrade your installation to -GitLab Enterprise Edition (EE). For more details, see [Upgrading between editions](../../update/index.md#upgrading-between-editions). -If you have questions or need assistance upgrading from GitLab CE to EE please [contact GitLab Support](https://about.gitlab.com/support/#contact-support). - -The license is a base64-encoded ASCII text file with a `.gitlab-license` -extension. You can obtain the file by [purchasing a license](https://about.gitlab.com/pricing/) -or by signing up for a [free trial](https://about.gitlab.com/free-trial/). - -After you've received your license from GitLab Inc., you can upload it -by **signing into your GitLab instance as an admin** or adding it at -installation time. +If you are running GitLab Community Edition (CE), upgrade your installation to GitLab Enterprise Edition (EE). For more details, see [Upgrading between editions](../../update/index.md#upgrading-between-editions). If you have questions or need assistance upgrading from GitLab CE to EE please [contact GitLab Support](https://about.gitlab.com/support/#contact-support). As of GitLab Enterprise Edition 9.4.0, a newly-installed instance without an -uploaded license only has the Free features active. A trial license -activates all Ultimate features, but after -[the trial expires](#what-happens-when-your-license-expires), some functionality +uploaded license only has the Free features active. A trial license activates all Ultimate features, but after [the trial expires](#what-happens-when-your-license-expires), some functionality is locked. +## Activate GitLab EE with an Activation Code **(PREMIUM SELF)** + +As of GitLab Enterprise Edition 14.0, you need an activation code to activate your instance. You can obtain an activation code by [purchasing a license](https://about.gitlab.com/pricing/) or by signing up for a [free trial](https://about.gitlab.com/free-trial/). This activation code is a 24-character alphanumeric string you receive in a confirmation email. You can also sign in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) to copy the activation code to your clipboard. + +To begin the activation process with your activation code: + +1. Sign in to your GitLab self-managed instance. +1. From the top menu, select the Admin Area **{admin}**. +1. From the left sidebar, select **Subscriptions**. +1. Paste the activation code onto the input field. +1. Read and accept the terms of service. +1. Select **Activate**. + +## Activate GitLab EE with a License File **(PREMIUM SELF)** + +If you receive a license file from GitLab (for example a new trial), you can upload it by signing into your GitLab instance as an admin or adding it during installation. The license is a base64-encoded ASCII text file with a `.gitlab-license` extension. + ## Uploading your license The first time you visit your GitLab EE installation signed in as an administrator, @@ -150,3 +152,7 @@ without enough users is uploaded, GitLab displays a message prompting you to pur additional users. More information on how to determine the required number of users and how to add additional seats can be found in the [licensing FAQ](https://about.gitlab.com/pricing/licensing-faq/). + +### There is a connectivity issue + +In GitLab 14.0 and later, to activate your subscription, your GitLab instance must be connected to the internet. If you have questions or need assistance activating your instance please [contact GitLab Support](https://about.gitlab.com/support/#contact-support). diff --git a/doc/user/analytics/index.md b/doc/user/analytics/index.md index 8c67163c4b0..0d3677fc07d 100644 --- a/doc/user/analytics/index.md +++ b/doc/user/analytics/index.md @@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w When we describe GitLab analytics, we use the following terms: - **Cycle time:** The duration of only the execution work alone. Often displayed in combination with "lead time," which is longer. GitLab measures cycle time from issue first merge request creation to issue close. This approach underestimates lead time because merge request creation is always later than commit time. GitLab displays cycle time in [group-level Value Stream Analytics](../group/value_stream_analytics/index.md). -- **Deploys:** The total number of successful deployments to production in the given time frame (across all applicable projects). GitLab displays deploys in [group-level Value Stream Analytics](../group/value_stream_analytics/index.md). +- **Deploys:** The total number of successful deployments to production in the given time frame (across all applicable projects). GitLab displays deploys in [group-level Value Stream Analytics](../group/value_stream_analytics/index.md) and [project-level Value Stream Analytics](value_stream_analytics.md). - **DORA (DevOps Research and Assessment)** ["Four Keys"](https://cloud.google.com/blog/products/devops-sre/using-the-four-keys-to-measure-your-devops-performance): - **Speed/Velocity** @@ -21,7 +21,7 @@ When we describe GitLab analytics, we use the following terms: improvements and features faster. GitLab measures this as the number of deployments to a [production environment](../../ci/environments/index.md#deployment-tier-of-environments) in the given time period. - GitLab displays deployment frequency in [group-level Value Stream Analytics](../group/value_stream_analytics/index.md). + GitLab displays deployment frequency in [group-level Value Stream Analytics](../group/value_stream_analytics/index.md) and [project-level Value Stream Analytics](value_stream_analytics.md). - **Lead Time for Changes:** The time it takes for a commit to get into production. (1) GitLab measures this as the median duration between merge request merge and deployment to a [production environment](../../ci/environments/index.md#deployment-tier-of-environments) for diff --git a/doc/user/group/value_stream_analytics/index.md b/doc/user/group/value_stream_analytics/index.md index 773d41947e2..4d254279a3d 100644 --- a/doc/user/group/value_stream_analytics/index.md +++ b/doc/user/group/value_stream_analytics/index.md @@ -20,7 +20,12 @@ to uncover, triage, and identify the root cause of slowdowns in the software dev For information on how to contribute to the development of Value Stream Analytics, see our [contributor documentation](../../../development/value_stream_analytics.md). -Group-level Value Stream Analytics is available via **Group > Analytics > Value Stream**. +To view group-level Value Stream Analytics: + +1. On the top bar, select **Menu > Groups** and find your group. +1. On the left sidebar, select **Analytics > Value stream**. + +Value Stream Analytics at the group level includes data for the selected group and its subgroups. [Project-level Value Stream Analytics](../../analytics/value_stream_analytics.md) is also available. diff --git a/doc/user/packages/composer_repository/index.md b/doc/user/packages/composer_repository/index.md index b6eb2975374..2787aefdeca 100644 --- a/doc/user/packages/composer_repository/index.md +++ b/doc/user/packages/composer_repository/index.md @@ -63,7 +63,7 @@ a group's namespace, rather than a user's namespace. Composer packages git commit -m 'Composer package test' git tag v1.0.0 git remote add origin git@gitlab.example.com:/.git - git push --set-upstream origin master + git push --set-upstream origin main git push origin v1.0.0 ``` diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md index eecc17fd60d..6fa302bbca7 100644 --- a/doc/user/packages/container_registry/index.md +++ b/doc/user/packages/container_registry/index.md @@ -236,7 +236,7 @@ combining the two to save us some typing in the `script` section. Here's a more elaborate example that splits up the tasks into 4 pipeline stages, including two tests that run in parallel. The `build` is stored in the container registry and used by subsequent stages, downloading the image -when needed. Changes to `master` also get tagged as `latest` and deployed using +when needed. Changes to `main` also get tagged as `latest` and deployed using an application-specific deploy script: ```yaml @@ -285,14 +285,14 @@ release-image: - docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE - docker push $CONTAINER_RELEASE_IMAGE only: - - master + - main deploy: stage: deploy script: - ./deploy.sh only: - - master + - main ``` NOTE: @@ -436,7 +436,7 @@ build_image: only: - branches except: - - master + - main delete_image: image: docker:19.03.12 @@ -457,7 +457,7 @@ delete_image: only: - branches except: - - master + - main ``` NOTE: @@ -605,10 +605,10 @@ Here are examples of regex patterns you may want to use: v.+ ``` -- Match only the tag named `master`: +- Match only the tag named `main`: ```plaintext - master + main ``` - Match tags that are either named or start with `release`: @@ -617,10 +617,10 @@ Here are examples of regex patterns you may want to use: release.* ``` -- Match tags that either start with `v`, are named `master`, or begin with `release`: +- Match tags that either start with `v`, are named `main`, or begin with `release`: ```plaintext - (?:v.+|master|release.*) + (?:v.+|main|release.*) ``` ### Set cleanup limits to conserve resources @@ -675,11 +675,11 @@ You can set, update, and disable the cleanup policies using the GitLab API. Examples: -- Select all tags, keep at least 1 tag per image, clean up any tag older than 14 days, run once a month, preserve any images with the name `master` and the policy is enabled: +- Select all tags, keep at least 1 tag per image, clean up any tag older than 14 days, run once a month, preserve any images with the name `main` and the policy is enabled: ```shell curl --request PUT --header 'Content-Type: application/json;charset=UTF-8' --header "PRIVATE-TOKEN: " \ - --data-binary '{"container_expiration_policy_attributes":{"cadence":"1month","enabled":true,"keep_n":1,"older_than":"14d","name_regex":"","name_regex_delete":".*","name_regex_keep":".*-master"}}' \ + --data-binary '{"container_expiration_policy_attributes":{"cadence":"1month","enabled":true,"keep_n":1,"older_than":"14d","name_regex":"","name_regex_delete":".*","name_regex_keep":".*-main"}}' \ "https://gitlab.example.com/api/v4/projects/2" ``` diff --git a/doc/user/packages/maven_repository/index.md b/doc/user/packages/maven_repository/index.md index 70b9c28da76..17571047353 100644 --- a/doc/user/packages/maven_repository/index.md +++ b/doc/user/packages/maven_repository/index.md @@ -711,7 +711,7 @@ you can configure GitLab CI/CD to build new packages automatically. ### Create Maven packages with GitLab CI/CD by using Maven -You can create a new package each time the `master` branch is updated. +You can create a new package each time the `main` branch is updated. 1. Create a `ci_settings.xml` file that serves as Maven's `settings.xml` file. @@ -768,7 +768,7 @@ You can create a new package each time the `master` branch is updated. script: - 'mvn deploy -s ci_settings.xml' only: - - master + - main ``` 1. Push those files to your repository. @@ -781,7 +781,7 @@ user's home location. In this example: ### Create Maven packages with GitLab CI/CD by using Gradle -You can create a package each time the `master` branch +You can create a package each time the `main` branch is updated. 1. Authenticate with [a CI job token in Gradle](#authenticate-with-a-ci-job-token-in-gradle). @@ -794,7 +794,7 @@ is updated. script: - 'gradle publish' only: - - master + - main ``` 1. Commit files to your repository. diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md index 46cfd763668..f68c2c6907d 100644 --- a/doc/user/packages/nuget_repository/index.md +++ b/doc/user/packages/nuget_repository/index.md @@ -322,7 +322,7 @@ If you're using NuGet with GitLab CI/CD, a CI job token can be used instead of a personal access token or deploy token. The token inherits the permissions of the user that generates the pipeline. -This example shows how to create a new package each time the `master` branch is +This example shows how to create a new package each time the `main` branch is updated: 1. Add a `deploy` job to your `.gitlab-ci.yml` file: @@ -340,7 +340,7 @@ updated: - dotnet nuget add source "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/nuget/index.json" --name gitlab --username gitlab-ci-token --password $CI_JOB_TOKEN --store-password-in-clear-text - dotnet nuget push "bin/Release/*.nupkg" --source gitlab only: - - master + - main ``` 1. Commit the changes and push it to your GitLab repository to trigger a new CI/CD build. diff --git a/doc/user/project/repository/branches/default.md b/doc/user/project/repository/branches/default.md index 2f1171a7d4f..2b8a47b7ce2 100644 --- a/doc/user/project/repository/branches/default.md +++ b/doc/user/project/repository/branches/default.md @@ -37,7 +37,7 @@ the [Git commands you need](#update-the-default-branch-name-in-your-repository) To update the default branch name for an individual [project](../../index.md): -1. Sign in to GitLab as a user with [Administrator](../../../permissions.md) permissions. +1. Sign in to GitLab as a user with the [Administrator](../../../permissions.md) role. 1. In the left navigation menu, go to **Settings > Repository**. 1. Expand **Default branch**, and select a new default branch. 1. (Optional) Select the **Auto-close referenced issues on default branch** check box to close @@ -168,6 +168,7 @@ current default branch, instead of displaying the "not found" page. ## Resources +- [Configure a default branch for your wiki](../../wiki/index.md) - [Discussion of default branch renaming](https://lore.kernel.org/git/pull.656.v4.git.1593009996.gitgitgadget@gmail.com/) on the Git mailing list - [March 2021 blog post: The new Git default branch name](https://about.gitlab.com/blog/2021/03/10/new-git-default-branch-name/) diff --git a/doc/user/project/wiki/index.md b/doc/user/project/wiki/index.md index 527db38c980..f657b541aae 100644 --- a/doc/user/project/wiki/index.md +++ b/doc/user/project/wiki/index.md @@ -29,6 +29,21 @@ with sibling pages listed in alphabetical order. To view a list of all pages, se ![Wiki sidebar](img/wiki_sidebar_v13_5.png) +## Configure a default branch for your wiki + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221159) in GitLab 14.1. + +The default branch for your wiki repository depends on your version of GitLab: + +- *GitLab versions 14.1 and later:* Wikis inherit the + [default branch name](../repository/branches/default.md) configured for + your instance or group. If no custom value is configured, GitLab uses `main`. +- *GitLab versions 14.0 and earlier:* GitLab uses `master`. + +For any version of GitLab, you can +[rename this default branch](../repository/branches/default.md#update-the-default-branch-name-in-your-repository) +for previously created wikis. + ## Create the wiki home page When a wiki is created, it is empty. On your first visit, create the landing page diff --git a/lib/support/nginx/gitlab-pages-ssl b/lib/support/nginx/gitlab-pages-ssl index 62ed482e2bf..900d91e0575 100644 --- a/lib/support/nginx/gitlab-pages-ssl +++ b/lib/support/nginx/gitlab-pages-ssl @@ -31,16 +31,16 @@ server { ## Strong SSL Security ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/ - ssl on; ssl_certificate /etc/nginx/ssl/gitlab-pages.crt; ssl_certificate_key /etc/nginx/ssl/gitlab-pages.key; - # GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs - ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_prefer_server_ciphers on; + ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; - ssl_session_timeout 5m; + ssl_session_tickets off; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; ## See app/controllers/application_controller.rb for headers set @@ -58,6 +58,9 @@ server { ## # ssl_dhparam /etc/ssl/certs/dhparam.pem; + ## [Optional] Enable HTTP Strict Transport Security + # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains"; + ## Individual nginx logs for GitLab pages access_log /var/log/nginx/gitlab_pages_access.log; error_log /var/log/nginx/gitlab_pages_error.log; diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 576c13d8d10..435b9055929 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -83,23 +83,23 @@ server { ## HTTPS host server { - listen 0.0.0.0:443 ssl; - listen [::]:443 ipv6only=on ssl default_server; + listen 0.0.0.0:443 ssl http2; + listen [::]:443 ipv6only=on ssl http2 default_server; server_name YOUR_SERVER_FQDN; ## Replace this with something like gitlab.example.com server_tokens off; ## Don't show the nginx version number, a security best practice ## Strong SSL Security ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/ - ssl on; ssl_certificate /etc/nginx/ssl/gitlab.crt; ssl_certificate_key /etc/nginx/ssl/gitlab.key; - # GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs - ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_prefer_server_ciphers on; + ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; - ssl_session_timeout 5m; + ssl_session_tickets off; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; ## See app/controllers/application_controller.rb for headers set @@ -120,7 +120,7 @@ server { # ssl_dhparam /etc/ssl/certs/dhparam.pem; ## [Optional] Enable HTTP Strict Transport Security - # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; + # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains"; ## Real IP Module Config ## http://nginx.org/en/docs/http/ngx_http_realip_module.html diff --git a/lib/support/nginx/registry-ssl b/lib/support/nginx/registry-ssl index df126919866..be16037629b 100644 --- a/lib/support/nginx/registry-ssl +++ b/lib/support/nginx/registry-ssl @@ -27,15 +27,24 @@ server { ## Strong SSL Security ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/ - ssl on; ssl_certificate /etc/gitlab/ssl/registry.gitlab.example.com.crt ssl_certificate_key /etc/gitlab/ssl/registry.gitlab.example.com.key - ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4'; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_prefer_server_ciphers on; - ssl_session_cache builtin:1000 shared:SSL:10m; - ssl_session_timeout 5m; + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:10m; + ssl_session_tickets off; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + + ## [Optional] Generate a stronger DHE parameter: + ## sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096 + ## + # ssl_dhparam /etc/ssl/certs/dhparam.pem; + + ## [Optional] Enable HTTP Strict Transport Security + # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains"; access_log /var/log/gitlab/nginx/gitlab_registry_access.log gitlab_access; error_log /var/log/gitlab/nginx/gitlab_registry_error.log; diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 73e250982ed..7f2d1510841 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -18523,6 +18523,18 @@ msgstr "" msgid "Jira-GitLab user mapping template" msgstr "" +msgid "JiraConnect|Failed to create branch." +msgstr "" + +msgid "JiraConnect|Failed to create branch. Please try again." +msgstr "" + +msgid "JiraConnect|New branch was successfully created." +msgstr "" + +msgid "JiraConnect|You can now close this window and return to Jira." +msgstr "" + msgid "JiraService| on branch %{branch_link}" msgstr "" @@ -30846,6 +30858,9 @@ msgstr "" msgid "Source IP" msgstr "" +msgid "Source branch" +msgstr "" + msgid "Source branch: %{source_branch_open}%{source_branch}%{source_branch_close}" msgstr "" diff --git a/spec/controllers/registrations/welcome_controller_spec.rb b/spec/controllers/registrations/welcome_controller_spec.rb index 6d34b56df09..034c9b3d1c0 100644 --- a/spec/controllers/registrations/welcome_controller_spec.rb +++ b/spec/controllers/registrations/welcome_controller_spec.rb @@ -60,10 +60,8 @@ RSpec.describe Registrations::WelcomeController do end describe '#update' do - let(:email_opted_in) { '0' } - subject(:update) do - patch :update, params: { user: { role: 'software_developer', setup_for_company: 'false', email_opted_in: email_opted_in } } + patch :update, params: { user: { role: 'software_developer', setup_for_company: 'false' } } end context 'without a signed in user' do @@ -100,24 +98,6 @@ RSpec.describe Registrations::WelcomeController do end end end - - context 'when the user opted in' do - let(:email_opted_in) { '1' } - - it 'sets the email_opted_in field' do - subject - - expect(controller.current_user.email_opted_in).to eq(true) - end - end - - context 'when the user opted out' do - it 'sets the email_opted_in field' do - subject - - expect(controller.current_user.email_opted_in).to eq(false) - end - end end end end diff --git a/spec/features/registrations/welcome_spec.rb b/spec/features/registrations/welcome_spec.rb deleted file mode 100644 index 74320b69f19..00000000000 --- a/spec/features/registrations/welcome_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Welcome screen' do - let(:user) { create(:user) } - - before do - gitlab_sign_in(user) - - visit users_sign_up_welcome_path - end - - it 'shows the email opt in' do - select 'Software Developer', from: 'user_role' - check 'user_email_opted_in' - click_button 'Get started!' - - expect(user.reload.email_opted_in).to eq(true) - end -end diff --git a/spec/frontend/jira_connect/branches/components/new_branch_form_spec.js b/spec/frontend/jira_connect/branches/components/new_branch_form_spec.js new file mode 100644 index 00000000000..7ea47c2737a --- /dev/null +++ b/spec/frontend/jira_connect/branches/components/new_branch_form_spec.js @@ -0,0 +1,230 @@ +import { GlAlert, GlForm, GlFormInput, GlButton } from '@gitlab/ui'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import NewBranchForm from '~/jira_connect/branches/components/new_branch_form.vue'; +import ProjectDropdown from '~/jira_connect/branches/components/project_dropdown.vue'; +import SourceBranchDropdown from '~/jira_connect/branches/components/source_branch_dropdown.vue'; +import { + CREATE_BRANCH_ERROR_GENERIC, + CREATE_BRANCH_ERROR_WITH_CONTEXT, + CREATE_BRANCH_SUCCESS_ALERT, +} from '~/jira_connect/branches/constants'; +import createBranchMutation from '~/jira_connect/branches/graphql/mutations/create_branch.mutation.graphql'; + +const mockProject = { + id: 'test', + fullPath: 'test-path', + repository: { + branchNames: ['main', 'f-test', 'release'], + rootRef: 'main', + }, +}; +const mockCreateBranchMutationResponse = { + data: { + createBranch: { + clientMutationId: 1, + errors: [], + }, + }, +}; +const mockCreateBranchMutationResponseWithErrors = { + data: { + createBranch: { + clientMutationId: 1, + errors: ['everything is broken, sorry.'], + }, + }, +}; +const mockCreateBranchMutationSuccess = jest + .fn() + .mockResolvedValue(mockCreateBranchMutationResponse); +const mockCreateBranchMutationWithErrors = jest + .fn() + .mockResolvedValue(mockCreateBranchMutationResponseWithErrors); +const mockCreateBranchMutationFailed = jest.fn().mockRejectedValue(new Error('GraphQL error')); +const mockMutationLoading = jest.fn().mockReturnValue(new Promise(() => {})); + +const localVue = createLocalVue(); + +describe('NewBranchForm', () => { + let wrapper; + + const findSourceBranchDropdown = () => wrapper.findComponent(SourceBranchDropdown); + const findProjectDropdown = () => wrapper.findComponent(ProjectDropdown); + const findAlert = () => wrapper.findComponent(GlAlert); + const findForm = () => wrapper.findComponent(GlForm); + const findInput = () => wrapper.findComponent(GlFormInput); + const findButton = () => wrapper.findComponent(GlButton); + + const completeForm = async () => { + await findInput().vm.$emit('input', 'cool-branch-name'); + await findProjectDropdown().vm.$emit('change', mockProject); + await findSourceBranchDropdown().vm.$emit('change', 'source-branch'); + }; + + function createMockApolloProvider({ + mockCreateBranchMutation = mockCreateBranchMutationSuccess, + } = {}) { + localVue.use(VueApollo); + + const mockApollo = createMockApollo([[createBranchMutation, mockCreateBranchMutation]]); + + return mockApollo; + } + + function createComponent({ mockApollo } = {}) { + wrapper = shallowMount(NewBranchForm, { + localVue, + apolloProvider: mockApollo || createMockApolloProvider(), + }); + } + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when selecting items from dropdowns', () => { + describe('when a project is selected', () => { + it('sets the `selectedProject` prop for ProjectDropdown and SourceBranchDropdown', async () => { + createComponent(); + + const projectDropdown = findProjectDropdown(); + await projectDropdown.vm.$emit('change', mockProject); + + expect(projectDropdown.props('selectedProject')).toEqual(mockProject); + expect(findSourceBranchDropdown().props('selectedProject')).toEqual(mockProject); + }); + }); + + describe('when a source branch is selected', () => { + it('sets the `selectedBranchName` prop for SourceBranchDropdown', async () => { + createComponent(); + + const mockBranchName = 'main'; + const sourceBranchDropdown = findSourceBranchDropdown(); + await sourceBranchDropdown.vm.$emit('change', mockBranchName); + + expect(sourceBranchDropdown.props('selectedBranchName')).toBe(mockBranchName); + }); + }); + }); + + describe('when submitting form', () => { + describe('when form submission is loading', () => { + it('sets submit button `loading` prop to `true`', async () => { + createComponent({ + mockApollo: createMockApolloProvider({ + mockCreateBranchMutation: mockMutationLoading, + }), + }); + + await completeForm(); + + await findForm().vm.$emit('submit', new Event('submit')); + await waitForPromises(); + + expect(findButton().props('loading')).toBe(true); + }); + }); + + describe('when form submission is successful', () => { + beforeEach(async () => { + createComponent(); + + await completeForm(); + + await findForm().vm.$emit('submit', new Event('submit')); + await waitForPromises(); + }); + + it('displays a success message', () => { + const alert = findAlert(); + expect(alert.exists()).toBe(true); + expect(alert.text()).toBe(CREATE_BRANCH_SUCCESS_ALERT.message); + expect(alert.props()).toMatchObject({ + title: CREATE_BRANCH_SUCCESS_ALERT.title, + variant: 'success', + }); + }); + + it('called `createBranch` mutation correctly', () => { + expect(mockCreateBranchMutationSuccess).toHaveBeenCalledWith({ + name: 'cool-branch-name', + projectPath: mockProject.fullPath, + ref: 'source-branch', + }); + }); + + it('sets submit button `loading` prop to `false`', () => { + expect(findButton().props('loading')).toBe(false); + }); + }); + + describe('when form submission fails', () => { + describe.each` + scenario | mutation | alertTitle | alertText + ${'with errors-as-data'} | ${mockCreateBranchMutationWithErrors} | ${CREATE_BRANCH_ERROR_WITH_CONTEXT} | ${mockCreateBranchMutationResponseWithErrors.data.createBranch.errors[0]} + ${'top-level error'} | ${mockCreateBranchMutationFailed} | ${''} | ${CREATE_BRANCH_ERROR_GENERIC} + `('', ({ mutation, alertTitle, alertText }) => { + beforeEach(async () => { + createComponent({ + mockApollo: createMockApolloProvider({ + mockCreateBranchMutation: mutation, + }), + }); + + await completeForm(); + + await findForm().vm.$emit('submit', new Event('submit')); + await waitForPromises(); + }); + + it('displays an alert', () => { + const alert = findAlert(); + expect(alert.exists()).toBe(true); + expect(alert.text()).toBe(alertText); + expect(alert.props()).toMatchObject({ title: alertTitle, variant: 'danger' }); + }); + + it('sets submit button `loading` prop to `false`', () => { + expect(findButton().props('loading')).toBe(false); + }); + }); + }); + }); + + describe('error handling', () => { + describe.each` + component | componentName + ${SourceBranchDropdown} | ${'SourceBranchDropdown'} + ${ProjectDropdown} | ${'ProjectDropdown'} + `('when $componentName emits error', ({ component }) => { + const mockErrorMessage = 'oh noes!'; + + beforeEach(async () => { + createComponent(); + await wrapper.findComponent(component).vm.$emit('error', { message: mockErrorMessage }); + }); + + it('displays an alert', () => { + const alert = findAlert(); + expect(alert.exists()).toBe(true); + expect(alert.text()).toBe(mockErrorMessage); + expect(alert.props('variant')).toBe('danger'); + }); + + describe('when alert is dismissed', () => { + it('hides alert', async () => { + const alert = findAlert(); + expect(alert.exists()).toBe(true); + + await alert.vm.$emit('dismiss'); + + expect(alert.exists()).toBe(false); + }); + }); + }); + }); +}); diff --git a/spec/frontend/token_access/token_access_spec.js b/spec/frontend/token_access/token_access_spec.js index c7323eb19fe..c4e29a52f1c 100644 --- a/spec/frontend/token_access/token_access_spec.js +++ b/spec/frontend/token_access/token_access_spec.js @@ -1,7 +1,8 @@ import { GlToggle, GlLoadingIcon } from '@gitlab/ui'; -import { createLocalVue, shallowMount, mount } from '@vue/test-utils'; +import { createLocalVue } from '@vue/test-utils'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; +import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import createFlash from '~/flash'; import TokenAccess from '~/token_access/components/token_access.vue'; @@ -41,15 +42,15 @@ describe('TokenAccess component', () => { const findToggle = () => wrapper.findComponent(GlToggle); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); - const findAddProjectBtn = () => wrapper.find('[data-testid="add-project-button"]'); - const findRemoveProjectBtn = () => wrapper.find('[data-testid="remove-project-button"]'); + const findAddProjectBtn = () => wrapper.findByRole('button', { name: 'Add project' }); + const findRemoveProjectBtn = () => wrapper.findByRole('button', { name: 'Remove access' }); const findTokenSection = () => wrapper.find('[data-testid="token-section"]'); const createMockApolloProvider = (requestHandlers) => { return createMockApollo(requestHandlers); }; - const createComponent = (requestHandlers, mountFn = shallowMount) => { + const createComponent = (requestHandlers, mountFn = shallowMountExtended) => { wrapper = mountFn(TokenAccess, { localVue, provide: { @@ -138,7 +139,7 @@ describe('TokenAccess component', () => { [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope], [addProjectCIJobTokenScopeMutation, addProjectSuccessHandler], ], - mount, + mountExtended, ); await waitForPromises(); @@ -160,7 +161,7 @@ describe('TokenAccess component', () => { [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope], [addProjectCIJobTokenScopeMutation, addProjectFailureHandler], ], - mount, + mountExtended, ); await waitForPromises(); @@ -181,7 +182,7 @@ describe('TokenAccess component', () => { [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope], [removeProjectCIJobTokenScopeMutation, removeProjectSuccessHandler], ], - mount, + mountExtended, ); await waitForPromises(); @@ -203,7 +204,7 @@ describe('TokenAccess component', () => { [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope], [removeProjectCIJobTokenScopeMutation, removeProjectFailureHandler], ], - mount, + mountExtended, ); await waitForPromises(); diff --git a/spec/views/registrations/welcome/show.html.haml_spec.rb b/spec/views/registrations/welcome/show.html.haml_spec.rb index ecdef7918de..d9c5d348e15 100644 --- a/spec/views/registrations/welcome/show.html.haml_spec.rb +++ b/spec/views/registrations/welcome/show.html.haml_spec.rb @@ -3,8 +3,6 @@ require 'spec_helper' RSpec.describe 'registrations/welcome/show' do - let(:is_gitlab_com) { false } - let_it_be(:user) { create(:user) } before do @@ -13,7 +11,6 @@ RSpec.describe 'registrations/welcome/show' do allow(view).to receive(:in_trial_flow?).and_return(false) allow(view).to receive(:user_has_memberships?).and_return(false) allow(view).to receive(:in_oauth_flow?).and_return(false) - allow(Gitlab).to receive(:com?).and_return(is_gitlab_com) render end @@ -22,24 +19,5 @@ RSpec.describe 'registrations/welcome/show' do it { is_expected.not_to have_selector('label[for="user_setup_for_company"]') } it { is_expected.to have_button('Get started!') } - it { is_expected.to have_selector('input[name="user[email_opted_in]"]') } - - describe 'email opt in' do - context 'when on gitlab.com' do - let(:is_gitlab_com) { true } - - it 'hides the email-opt in by default' do - expect(subject).to have_css('.js-email-opt-in.hidden') - end - end - - context 'when not on gitlab.com' do - let(:is_gitlab_com) { false } - - it 'hides the email-opt in by default' do - expect(subject).not_to have_css('.js-email-opt-in.hidden') - expect(subject).to have_css('.js-email-opt-in') - end - end - end + it { is_expected.not_to have_selector('input[name="user[email_opted_in]"]') } end