summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2018-08-16 12:11:04 +0100
committerFilipa Lacerda <filipa@gitlab.com>2018-08-16 12:11:04 +0100
commit1934401877884c22a0bfbad1a305d248e533841e (patch)
treeae2586c114296ff404501bac119c22652036f159
parent2b23357e0e91ddcea7593299b53c301222a2c5ee (diff)
parentbd9e5c9bcce90bd2289ee818bfac58ea3a090aaf (diff)
downloadgitlab-ce-50101-truncated-job-information.tar.gz
Merge branch 'master' into 50101-truncated-job-information50101-truncated-job-information
* master: (23 commits) Creates empty state component for job log view Documentation process at GitLab Remove feature flag for FindAllRemoteBranches Added missing html_safe on text messages. Enable frozen in app/mailers/**/*.rb Add Acceptance testing issue template Fixed deleting new files creating wrong state in IDE Improved padding of top bar in IDE job trace panel Documentation for JUnit XML Test Summary In MR widget Add Czech as an available language Reduce number of model instances needed in test. Changes /admin/application_settings to support template repository selection in EE Allow the project_select_tag to specify an initial value and for the selection to be clear-able Convert the license template API to use the new LicenseTemplateFinder Convert BlobHelper#licenses_for_select to use the new LicenseTemplateFinder Introduce a LicenseTemplate model and LicenseTemplateFinder helper Combines emoji award spec files into single user_interacts_with_awards_in_issue_spec Fix bugs in Gitlab::Template::Finders preventing instances from BaseTemplate.all from loading content Add author-link style from common.scss in EE regenerate .pot ...
-rw-r--r--.gitlab/issue_templates/Acceptance_Testing.md100
-rw-r--r--.gitlab/issue_templates/Documentation.md54
-rw-r--r--.gitlab/merge_request_templates/Change documentation location.md32
-rw-r--r--.gitlab/merge_request_templates/Documentation.md35
-rw-r--r--PROCESS.md33
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js7
-rw-r--r--app/assets/javascripts/jobs/components/empty_state.vue76
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/index.js2
-rw-r--r--app/assets/javascripts/project_select.js8
-rw-r--r--app/assets/stylesheets/framework/common.scss4
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss1
-rw-r--r--app/finders/license_template_finder.rb36
-rw-r--r--app/helpers/application_settings_helper.rb1
-rw-r--r--app/helpers/blob_helper.rb12
-rw-r--r--app/helpers/button_helper.rb6
-rw-r--r--app/helpers/projects_helper.rb5
-rw-r--r--app/mailers/abuse_report_mailer.rb2
-rw-r--r--app/mailers/base_mailer.rb2
-rw-r--r--app/mailers/devise_mailer.rb9
-rw-r--r--app/mailers/email_rejection_mailer.rb2
-rw-r--r--app/mailers/emails/issues.rb2
-rw-r--r--app/mailers/emails/members.rb2
-rw-r--r--app/mailers/emails/merge_requests.rb2
-rw-r--r--app/mailers/emails/notes.rb2
-rw-r--r--app/mailers/emails/pages_domains.rb2
-rw-r--r--app/mailers/emails/pipelines.rb8
-rw-r--r--app/mailers/emails/profile.rb2
-rw-r--r--app/mailers/emails/projects.rb2
-rw-r--r--app/mailers/notify.rb16
-rw-r--r--app/mailers/previews/devise_mailer_preview.rb2
-rw-r--r--app/mailers/previews/email_rejection_mailer_preview.rb2
-rw-r--r--app/mailers/previews/notify_preview.rb2
-rw-r--r--app/mailers/previews/repository_check_mailer_preview.rb2
-rw-r--r--app/mailers/repository_check_mailer.rb2
-rw-r--r--app/models/application_setting.rb3
-rw-r--r--app/models/license_template.rb53
-rw-r--r--app/models/namespace.rb2
-rw-r--r--app/views/admin/application_settings/_account_and_limit.html.haml6
-rw-r--r--app/views/admin/application_settings/show.html.haml2
-rw-r--r--app/views/projects/mirrors/_instructions.html.haml2
-rw-r--r--app/views/projects/settings/ci_cd/_form.html.haml4
-rw-r--r--changelogs/unreleased/49953-add-user_show_add_ssh_key_message-setting.yml5
-rw-r--r--changelogs/unreleased/50101-empty-state-component.yml5
-rw-r--r--changelogs/unreleased/bvl-add-czech.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-mailers.yml5
-rw-r--r--changelogs/unreleased/ide-delete-new-files-state.yml5
-rw-r--r--changelogs/unreleased/ide-job-top-bar-ui-polish.yml5
-rw-r--r--changelogs/unreleased/n8rzz-consolidate-specs-testing-emoji-awards.yml6
-rw-r--r--db/migrate/20180808162000_add_user_show_add_ssh_key_message_to_application_settings.rb19
-rw-r--r--db/schema.rb3
-rw-r--r--doc/README.md1
-rw-r--r--doc/administration/operations/ssh_certificates.md17
-rw-r--r--doc/api/settings.md8
-rw-r--r--doc/ci/examples/README.md4
-rw-r--r--doc/ci/img/junit_test_report.pngbin0 -> 9572 bytes
-rw-r--r--doc/ci/junit_test_reports.md102
-rw-r--r--doc/ci/yaml/README.md46
-rw-r--r--doc/development/documentation/index.md66
-rw-r--r--doc/development/documentation/structure.md149
-rw-r--r--doc/development/documentation/styleguide.md26
-rw-r--r--doc/development/documentation/workflow.md186
-rw-r--r--doc/development/feature_flags.md35
-rw-r--r--doc/user/project/merge_requests/index.md3
-rw-r--r--lib/api/entities.rb2
-rw-r--r--lib/api/templates.rb44
-rw-r--r--lib/gitlab/git/repository_mirroring.rb29
-rw-r--r--lib/gitlab/i18n.rb3
-rw-r--r--lib/gitlab/template/finders/base_template_finder.rb2
-rw-r--r--lib/gitlab/template/finders/repo_template_finder.rb10
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/features/issues/award_emoji_spec.rb146
-rw-r--r--spec/features/issues/award_spec.rb51
-rw-r--r--spec/features/issues/user_interacts_with_awards_spec.rb347
-rw-r--r--spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb172
-rw-r--r--spec/finders/license_template_finder_spec.rb49
-rw-r--r--spec/helpers/button_helper_spec.rb12
-rw-r--r--spec/javascripts/ide/stores/mutations_spec.js27
-rw-r--r--spec/javascripts/jobs/empty_state_spec.js90
-rw-r--r--spec/lib/gitlab/template/finders/repo_template_finders_spec.rb37
-rw-r--r--spec/models/internal_id_spec.rb4
-rw-r--r--spec/models/license_template_spec.rb59
-rw-r--r--spec/requests/api/templates_spec.rb3
82 files changed, 1795 insertions, 543 deletions
diff --git a/.gitlab/issue_templates/Acceptance_Testing.md b/.gitlab/issue_templates/Acceptance_Testing.md
new file mode 100644
index 00000000000..f1fbb96ce61
--- /dev/null
+++ b/.gitlab/issue_templates/Acceptance_Testing.md
@@ -0,0 +1,100 @@
+## Details
+- **Feature Toggle Name**: `FEATURE_NAME`
+- **Required GitLab Version**: `vX.X`
+
+--------------------------------------------------------------------------------
+
+## 1. Preparation
+
+- [ ] **Controllers and workers**:
+ 1. Please link to dashboards of the workers, and the controllers and actions that can be impacted
+ 2. ...
+ 3. ...
+
+## 2. Development Trial
+
+#### Check Dev Server Versions
+- [ ] GitLab: https://dev.gitlab.org/help
+
+#### Enable on `dev.gitlab.org`:
+- [ ] `/chatops feature set FEATURE_NAME true --dev` in [`#dev-gitlab`](https://gitlab.slack.com/messages/C6WQ87MU3)
+
+Then leave running while monitoring and performing some testing through web, api or SSH.
+
+#### Monitor
+
+- [ ] [Monitor Using Grafana](https://dashboards.gitlab.net)
+- [ ] [Inspect logs in ELK](https://log.gitlab.net/app/kibana)
+- [ ] [Check for errors in GitLab Dev Sentry](https://sentry.gitlap.com/gitlab/devgitlaborg/?query=is%3Aunresolved)
+
+## 2. Staging Trial
+
+#### Check Staging Server Versions
+- [ ] GitLab: https://staging.gitlab.com/help
+
+#### Enable on `staging.gitlab.com`
+- [ ] `/chatops run feature set FEATURE_NAME true --staging` in [`#development`](https://gitlab.slack.com/messages/C02PF508L/)
+
+Then leave running while monitoring for at least **15 minutes** while performing some testing through web, api or SSH.
+
+#### Monitor
+
+- [ ] [Monitor Using Grafana](https://dashboards.gitlab.net)
+- [ ] [Inspect logs in ELK](https://log.gitlab.net/app/kibana)
+- [ ] [Check for errors in GitLab Sentry](https://sentry.gitlap.com/gitlab/gitlabcom/?query=is%3Aunresolved)
+
+## 4. Production Server Version Check
+
+- [ ] GitLab: https://gitlab.com/help
+
+## 5. Initial Impact Check
+
+- [ ] Enable for a subset of users, when using percentage gates: 1%.
+
+Then leave running while monitoring for at least **15 minutes** while performing some testing through web, api or SSH.
+
+#### Monitor
+
+- [ ] [Monitor Using Grafana](https://dashboards.gitlab.net)
+- [ ] [Inspect logs in ELK](https://log.gitlab.net/app/kibana)
+- [ ] [Check for errors in GitLab Sentry](https://sentry.gitlap.com/gitlab/gitlabcom/?query=is%3Aunresolved)
+
+## 6. Low Impact Check
+
+- [ ] Enable for a bigger subset of users, when using percentage gates: 10%.
+
+Then leave running while monitoring for at least **30 minutes** while performing some testing through web, api or SSH.
+
+#### Monitor
+
+- [ ] [Monitor Using Grafana](https://dashboards.gitlab.net)
+- [ ] [Inspect logs in ELK](https://log.gitlab.net/app/kibana)
+- [ ] [Check for errors in GitLab Sentry](https://sentry.gitlap.com/gitlab/gitlabcom/?query=is%3Aunresolved)
+
+## 7. Mid Impact Trial
+
+- [ ] Enable for a big subset of users, when using percentage gates: 50%.
+
+Then leave running while monitoring for at least **12 hours** while performing some testing through web, api or SSH.
+
+#### Monitor
+
+- [ ] [Monitor Using Grafana](https://dashboards.gitlab.net)
+- [ ] [Inspect logs in ELK](https://log.gitlab.net/app/kibana)
+- [ ] [Check for errors in GitLab Sentry](https://sentry.gitlap.com/gitlab/gitlabcom/?query=is%3Aunresolved)
+
+## 8. Full Impact Trial
+
+- [ ] Enable for all users: `/chatops run feature set FEATURE_NAME true
+
+Then leave running while monitoring for at least **1 week**.
+
+#### Monitor
+
+- [ ] [Monitor Using Grafana](https://dashboards.gitlab.net)
+- [ ] [Inspect logs in ELK](https://log.gitlab.net/app/kibana)
+- [ ] [Check for errors in GitLab Dev Sentry](https://sentry.gitlap.com/gitlab/devgitlaborg/?query=is%3Aunresolved)
+
+#### Success?
+
+- [ ] Remove the feature gate from the code, and close this issue with that MR.
diff --git a/.gitlab/issue_templates/Documentation.md b/.gitlab/issue_templates/Documentation.md
new file mode 100644
index 00000000000..b33ed5bcaa8
--- /dev/null
+++ b/.gitlab/issue_templates/Documentation.md
@@ -0,0 +1,54 @@
+<!--See the general documentation guidelines https://docs.gitlab.com/ee/development/documentation -->
+
+<!-- Mention "documentation" or "docs" in the issue title -->
+
+<!-- Use this description template for new docs or updates to existing docs. -->
+
+<!-- Check the documentation structure guidelines for guidance: https://docs.gitlab.com/ee/development/documentation/structure.html-->
+
+- [ ] Documents Feature A <!-- feature name -->
+- [ ] Follow-up from: #XXX, !YYY <!-- Mention related issues, MRs, and epics when available -->
+
+## New doc or update?
+
+<!-- Mark either of these boxes: -->
+
+- [ ] New documentation
+- [ ] Update existing documentation
+
+## Checklists
+
+### Product Manager
+
+<!-- Reference: https://docs.gitlab.com/ee/development/documentation/workflow.html#1-product-manager-s-role-in-the-documentation-process -->
+
+- [ ] Add the correct labels
+- [ ] Add the correct milestone
+- [ ] Indicate the correct document/directory for this feature <!-- (ping the tech writers for help if you're not sure) -->
+- [ ] Fill the doc blurb below
+
+#### Documentation blurb
+
+<!-- Documentation template: https://docs.gitlab.com/ee/development/documentation/structure.html#documentation-template-for-new-docs -->
+
+- Doc **title**
+
+ <!-- write the doc title here -->
+
+- Feature **overview/description**
+
+ <!-- Write the feature overview here -->
+
+- Feature **use cases**
+
+ <!-- Write the use cases here -->
+
+### Developer
+
+<!-- Reference: https://docs.gitlab.com/ee/development/documentation/workflow.html#2-developer-s-role-in-the-documentation-process -->
+
+- [ ] Copy the doc blurb above and paste it into the doc
+- [ ] Write the tutorial - explain how to use the feature
+- [ ] Submit the MR using the appropriate MR description template
+
+/label ~Documentation
diff --git a/.gitlab/merge_request_templates/Change documentation location.md b/.gitlab/merge_request_templates/Change documentation location.md
new file mode 100644
index 00000000000..b4a6d2bd3b4
--- /dev/null
+++ b/.gitlab/merge_request_templates/Change documentation location.md
@@ -0,0 +1,32 @@
+<!--See the general Documentation guidelines https://docs.gitlab.com/ee/development/documentation/ -->
+
+<!-- Use this description template for changing documentation location. For new docs or updates to existing docs, use the "Documentation" template -->
+
+## What does this MR do?
+
+<!-- Briefly describe what this MR is about -->
+
+## Related issues
+
+<!-- Mention the issue(s) this MR closes or is related to -->
+
+Closes
+
+## Moving docs to a new location?
+
+Read the guidelines:
+https://docs.gitlab.com/ce/development/documentation/index.html#changing-document-location
+
+- [ ] Make sure the old link is not removed and has its contents replaced with
+ a link to the new location.
+- [ ] Make sure internal links pointing to the document in question are not broken.
+- [ ] Search and replace any links referring to old docs in GitLab Rails app,
+ specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
+- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ce/development/writing_documentation.html#redirections-for-pages-with-disqus-comments)
+ to the new document if there are any Disqus comments on the old document thread.
+- [ ] Update the link in `features.yml` (if applicable)
+- [ ] If working on CE and the `ee-compat-check` jobs fails, submit an MR to EE
+ with the changes as well (https://docs.gitlab.com/ce/development/writing_documentation.html#cherry-picking-from-ce-to-ee).
+- [ ] Ping one of the technical writers for review.
+
+/label ~Documentation
diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md
index 531035b3766..ca38c881c66 100644
--- a/.gitlab/merge_request_templates/Documentation.md
+++ b/.gitlab/merge_request_templates/Documentation.md
@@ -1,4 +1,8 @@
-<!--See the general Documentation guidelines https://docs.gitlab.com/ee/development/documentation/index.html -->
+<!--See the general documentation guidelines https://docs.gitlab.com/ee/development/documentation -->
+
+<!-- Mention "documentation" or "docs" in the MR title -->
+
+<!-- Use this description template for new docs or updates to existing docs. For changing documentation location use the "Change documentation location" template -->
## What does this MR do?
@@ -10,20 +14,19 @@
Closes
-## Moving docs to a new location?
-
-Read the guidelines:
-https://docs.gitlab.com/ee/development/documentation/#changing-document-location
-
-- [ ] Make sure the old link is not removed and has its contents replaced with
- a link to the new location.
-- [ ] Make sure internal links pointing to the document in question are not broken.
-- [ ] Search and replace any links referring to the old docs in the GitLab Rails app,
- specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
-- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ee/development/documentation/index.html#redirections-for-pages-with-disqus-comments)
- to the new document if there are any Disqus comments on the old document thread.
-- [ ] If working on CE and the `ee-compat-check` jobs fails, [submit an MR to EE
- with the changes](https://docs.gitlab.com/ee/development/documentation/index.html#cherry-picking-from-ce-to-ee) as well.
-- [ ] Ping one of the technical writers for review.
+## Author's checklist
+
+- [ ] [Apply the correct labels and milestone](https://docs.gitlab.com/ee/development/documentation/workflow.html#2-developer-s-role-in-the-documentation-process)
+- [ ] Crosslink the document from the higher-level index
+- [ ] Crosslink the document from other subject-related docs
+- [ ] Correctly apply the product [badges](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges) and [tiers](https://docs.gitlab.com/ee/development/documentation/styleguide.html#gitlab-versions-and-tiers)
+- [ ] [Port the MR to EE (or backport from CE)](https://docs.gitlab.com/ee/development/documentation/index.html#cherry-picking-from-ce-to-ee): _always recommended, required when the `ee-compat-check` job fails_
+
+## Review checklist
+
+- [ ] Your team's review (required)
+- [ ] PM's review (recommended, but not a blocker)
+- [ ] Technical writer's review (required)
+- [ ] Merge the EE-MR first, CE-MR afterwards
/label ~Documentation
diff --git a/PROCESS.md b/PROCESS.md
index 5f50d472bd7..583f36b820f 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -116,6 +116,11 @@ target. However, if one does and falls into either of the above categories, it's
the reviewer's responsibility to manage the above communication and assignment
on behalf of the community member.
+Every new feature or change should be shipped with its corresponding documentation
+in accordance with the
+[documentation process](https://docs.gitlab.com/ee/development/documentation/workflow.html)
+and [structure](https://docs.gitlab.com/ee/development/documentation/structure.html).
+
#### What happens if these deadlines are missed?
If a small or large feature is _not_ with a maintainer or reviewer by the
@@ -141,14 +146,9 @@ and to prevent any last minute surprises.
### On the 7th
-Merge requests should still be complete, following the
-[definition of done][done]. The single exception is documentation, and this can
-only be left until after the freeze if:
+Merge requests should still be complete, following the [definition of done][done].
-* There is a follow-up issue to add documentation.
-* It is assigned to the person writing documentation for this feature, and they
- are aware of it.
-* It is in the correct milestone, with the ~Deliverable label.
+#### Feature merge requests
If a merge request is not ready, but the developers and Product Manager
responsible for the feature think it is essential that it is in the release,
@@ -164,6 +164,23 @@ information, see
[Automatic CE->EE merge][automatic_ce_ee_merge] and
[Guidelines for implementing Enterprise Edition features][ee_features].
+#### Documentation merge requests
+
+Documentation is part of the product and must be shipped with the feature.
+
+The single exception for the feature freeze is documentation, and it can
+be left to be **merged up to the 14th** if:
+
+* There is a follow-up issue to add documentation.
+* It is assigned to the developer writing documentation for this feature, and they
+ are aware of it.
+* It is in the correct milestone, with the labels ~Documentation, ~Deliverable,
+~missed-deliverable, and "pick into X.Y" applied.
+* It must be reviewed and approved by a technical writer.
+
+For more information read the process for
+[documentation shipped late](https://docs.gitlab.com/ee/development/documentation/workflow.html#documentation-shipped-late).
+
### After the 7th
Once the stable branch is frozen, the only MRs that can be cherry-picked into
@@ -172,7 +189,7 @@ the stable branch are:
* Fixes for [regressions](#regressions) where the affected version `xx.x` in `regression:xx.x` is the current release. See [Managing bugs](#managing-bugs) section.
* Fixes for security issues
* Fixes or improvements to automated QA scenarios
-* Documentation updates for changes in the same release
+* [Documentation updates](https://docs.gitlab.com/ee/development/documentation/workflow.html#documentation-shipped-late) for changes in the same release
* New or updated translations (as long as they do not touch application code)
During the feature freeze all merge requests that are meant to go into the
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
index 1eda5768709..56a8d9430c7 100644
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ b/app/assets/javascripts/ide/stores/mutations.js
@@ -200,6 +200,7 @@ export default {
},
[types.DELETE_ENTRY](state, path) {
const entry = state.entries[path];
+ const { tempFile = false } = entry;
const parent = entry.parentPath
? state.entries[entry.parentPath]
: state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
@@ -209,7 +210,11 @@ export default {
parent.tree = parent.tree.filter(f => f.path !== entry.path);
if (entry.type === 'blob') {
- state.changedFiles = state.changedFiles.concat(entry);
+ if (tempFile) {
+ state.changedFiles = state.changedFiles.filter(f => f.path !== path);
+ } else {
+ state.changedFiles = state.changedFiles.concat(entry);
+ }
}
},
[types.RENAME_ENTRY](state, { path, name, entryPath = null }) {
diff --git a/app/assets/javascripts/jobs/components/empty_state.vue b/app/assets/javascripts/jobs/components/empty_state.vue
new file mode 100644
index 00000000000..4faf08387fb
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/empty_state.vue
@@ -0,0 +1,76 @@
+<script>
+ export default {
+ props: {
+ illustrationPath: {
+ type: String,
+ required: true,
+ },
+ illustrationSizeClass: {
+ type: String,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ content: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ action: {
+ type: Object,
+ required: false,
+ default: null,
+ validator(value) {
+ return (
+ value === null ||
+ (Object.prototype.hasOwnProperty.call(value, 'link') &&
+ Object.prototype.hasOwnProperty.call(value, 'method') &&
+ Object.prototype.hasOwnProperty.call(value, 'title'))
+ );
+ },
+ },
+ },
+ };
+</script>
+<template>
+ <div class="row empty-state">
+ <div class="col-12">
+ <div
+ :class="illustrationSizeClass"
+ class="svg-content"
+ >
+ <img :src="illustrationPath" />
+ </div>
+ </div>
+
+ <div class="col-12">
+ <div class="text-content">
+ <h4 class="js-job-empty-state-title text-center">
+ {{ title }}
+ </h4>
+
+ <p
+ v-if="content"
+ class="js-job-empty-state-content"
+ >
+ {{ content }}
+ </p>
+
+ <div
+ v-if="action"
+ class="text-center"
+ >
+ <a
+ :href="action.link"
+ :data-method="action.method"
+ class="js-job-empty-state-action btn btn-primary"
+ >
+ {{ action.title }}
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pages/admin/application_settings/index.js b/app/assets/javascripts/pages/admin/application_settings/index.js
index 48d75f5443b..47bd70537f1 100644
--- a/app/assets/javascripts/pages/admin/application_settings/index.js
+++ b/app/assets/javascripts/pages/admin/application_settings/index.js
@@ -1,6 +1,8 @@
import initSettingsPanels from '~/settings_panels';
+import projectSelect from '~/project_select';
document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
initSettingsPanels();
+ projectSelect();
});
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index bce7556bd40..6f3b32f8eea 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -14,6 +14,7 @@ export default function projectSelect() {
this.orderBy = $(select).data('orderBy') || 'id';
this.withIssuesEnabled = $(select).data('withIssuesEnabled');
this.withMergeRequestsEnabled = $(select).data('withMergeRequestsEnabled');
+ this.allowClear = $(select).data('allowClear') || false;
placeholder = "Search for project";
if (this.includeGroups) {
@@ -71,6 +72,13 @@ export default function projectSelect() {
text: function (project) {
return project.name_with_namespace || project.name;
},
+
+ initSelection: function(el, callback) {
+ return Api.project(el.val()).then(({ data }) => callback(data));
+ },
+
+ allowClear: this.allowClear,
+
dropdownCssClass: "ajax-project-dropdown"
});
if (simpleFilter) return select;
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 268e68dbb15..48a87ea8616 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -117,6 +117,10 @@ hr {
color: $blue-600;
}
+.author-link:hover {
+ text-decoration: none;
+}
+
.back-link {
font-size: 14px;
}
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
index af91497d0ea..eac1345742d 100644
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -1293,6 +1293,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
&.build-page .top-bar {
top: 0;
+ height: auto;
font-size: 12px;
border-top-right-radius: $border-radius-default;
}
diff --git a/app/finders/license_template_finder.rb b/app/finders/license_template_finder.rb
new file mode 100644
index 00000000000..fad33f0eca2
--- /dev/null
+++ b/app/finders/license_template_finder.rb
@@ -0,0 +1,36 @@
+# LicenseTemplateFinder
+#
+# Used to find license templates, which may come from a variety of external
+# sources
+#
+# Arguments:
+# popular: boolean. When set to true, only "popular" licenses are shown. When
+# false, all licenses except popular ones are shown. When nil (the
+# default), *all* licenses will be shown.
+class LicenseTemplateFinder
+ attr_reader :params
+
+ def initialize(params = {})
+ @params = params
+ end
+
+ def execute
+ Licensee::License.all(featured: popular_only?).map do |license|
+ LicenseTemplate.new(
+ id: license.key,
+ name: license.name,
+ nickname: license.nickname,
+ category: (license.featured? ? :Popular : :Other),
+ content: license.content,
+ url: license.url,
+ meta: license.meta
+ )
+ end
+ end
+
+ private
+
+ def popular_only?
+ params.fetch(:popular, nil)
+ end
+end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 2bdf2c2c120..1e05f07e676 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -254,6 +254,7 @@ module ApplicationSettingsHelper
:usage_ping_enabled,
:instance_statistics_visibility_private,
:user_default_external,
+ :user_show_add_ssh_key_message,
:user_oauth_applications,
:version_check_enabled,
:web_ide_clientside_preview_enabled
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 7eb45ddd117..b61cbd5418a 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -182,12 +182,14 @@ module BlobHelper
def licenses_for_select
return @licenses_for_select if defined?(@licenses_for_select)
- licenses = Licensee::License.all
+ grouped_licenses = LicenseTemplateFinder.new.execute.group_by(&:category)
+ categories = grouped_licenses.keys
- @licenses_for_select = {
- Popular: licenses.select(&:featured).map { |license| { name: license.name, id: license.key } },
- Other: licenses.reject(&:featured).map { |license| { name: license.name, id: license.key } }
- }
+ @licenses_for_select = categories.each_with_object({}) do |category, hash|
+ hash[category] = grouped_licenses[category].map do |license|
+ { name: license.name, id: license.id }
+ end
+ end
end
def ref_project
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index 0171a880164..7adc882bc47 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -73,7 +73,11 @@ module ButtonHelper
end
def ssh_clone_button(project, append_link: true)
- dropdown_description = _("You won't be able to pull or push project code via SSH until you add an SSH key to your profile") if current_user.try(:require_ssh_key?)
+ if Gitlab::CurrentSettings.user_show_add_ssh_key_message? &&
+ current_user.try(:require_ssh_key?)
+ dropdown_description = _("You won't be able to pull or push project code via SSH until you add an SSH key to your profile")
+ end
+
append_url = project.ssh_url_to_repo if append_link
dropdown_item_with_description('SSH', dropdown_description, href: append_url)
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index aaf9dff43ee..6b4079b4113 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -192,7 +192,10 @@ module ProjectsHelper
end
def show_no_ssh_key_message?
- cookies[:hide_no_ssh_message].blank? && !current_user.hide_no_ssh_key && current_user.require_ssh_key?
+ Gitlab::CurrentSettings.user_show_add_ssh_key_message? &&
+ cookies[:hide_no_ssh_message].blank? &&
+ !current_user.hide_no_ssh_key &&
+ current_user.require_ssh_key?
end
def show_no_password_message?
diff --git a/app/mailers/abuse_report_mailer.rb b/app/mailers/abuse_report_mailer.rb
index fe5f68ba3d5..e032f568913 100644
--- a/app/mailers/abuse_report_mailer.rb
+++ b/app/mailers/abuse_report_mailer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AbuseReportMailer < BaseMailer
def notify(abuse_report_id)
return unless deliverable?
diff --git a/app/mailers/base_mailer.rb b/app/mailers/base_mailer.rb
index 654468bc7fe..5fd209c4761 100644
--- a/app/mailers/base_mailer.rb
+++ b/app/mailers/base_mailer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BaseMailer < ActionMailer::Base
around_action :render_with_default_locale
diff --git a/app/mailers/devise_mailer.rb b/app/mailers/devise_mailer.rb
index 962570a0efd..7aa75ee30e6 100644
--- a/app/mailers/devise_mailer.rb
+++ b/app/mailers/devise_mailer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeviseMailer < Devise::Mailer
default from: "#{Gitlab.config.gitlab.email_display_name} <#{Gitlab.config.gitlab.email_from}>"
default reply_to: Gitlab.config.gitlab.email_reply_to
@@ -9,8 +11,9 @@ class DeviseMailer < Devise::Mailer
protected
def subject_for(key)
- subject = super
- subject << " | #{Gitlab.config.gitlab.email_subject_suffix}" if Gitlab.config.gitlab.email_subject_suffix.present?
- subject
+ subject = [super]
+ subject << Gitlab.config.gitlab.email_subject_suffix if Gitlab.config.gitlab.email_subject_suffix.present?
+
+ subject.join(' | ')
end
end
diff --git a/app/mailers/email_rejection_mailer.rb b/app/mailers/email_rejection_mailer.rb
index 76db31a4c45..45fc5a6c383 100644
--- a/app/mailers/email_rejection_mailer.rb
+++ b/app/mailers/email_rejection_mailer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EmailRejectionMailer < BaseMailer
def rejection(reason, original_raw, can_retry = false)
@reason = reason
diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb
index 392cc0bee03..c8b1ab5033a 100644
--- a/app/mailers/emails/issues.rb
+++ b/app/mailers/emails/issues.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module Issues
def new_issue_email(recipient_id, issue_id, reason = nil)
diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb
index 75cf56a51f2..91dfdf58982 100644
--- a/app/mailers/emails/members.rb
+++ b/app/mailers/emails/members.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module Members
extend ActiveSupport::Concern
diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb
index 70509e9066d..70f65d4e58d 100644
--- a/app/mailers/emails/merge_requests.rb
+++ b/app/mailers/emails/merge_requests.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module MergeRequests
def new_merge_request_email(recipient_id, merge_request_id, reason = nil)
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index d9a6fe2a41e..d3284e90568 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module Notes
def note_commit_email(recipient_id, note_id)
diff --git a/app/mailers/emails/pages_domains.rb b/app/mailers/emails/pages_domains.rb
index 0027dfdc36b..ce449237ef6 100644
--- a/app/mailers/emails/pages_domains.rb
+++ b/app/mailers/emails/pages_domains.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module PagesDomains
def pages_domain_enabled_email(domain, recipient)
diff --git a/app/mailers/emails/pipelines.rb b/app/mailers/emails/pipelines.rb
index f9f45ab987b..31e183640ad 100644
--- a/app/mailers/emails/pipelines.rb
+++ b/app/mailers/emails/pipelines.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module Pipelines
def pipeline_success_email(pipeline, recipients)
@@ -39,10 +41,10 @@ module Emails
end
def pipeline_subject(status)
- commit = @pipeline.short_sha
- commit << " in #{@merge_request.to_reference}" if @merge_request
+ commit = [@pipeline.short_sha]
+ commit << "in #{@merge_request.to_reference}" if @merge_request
- subject("Pipeline ##{@pipeline.id} has #{status} for #{@pipeline.ref}", commit)
+ subject("Pipeline ##{@pipeline.id} has #{status} for #{@pipeline.ref}", commit.join(' '))
end
end
end
diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb
index 4f5edeb9bda..40d7b9ccd7a 100644
--- a/app/mailers/emails/profile.rb
+++ b/app/mailers/emails/profile.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module Profile
def new_user_email(user_id, token = nil)
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index 761d873c01c..d7e6c2ba7b2 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
module Projects
def project_was_moved_email(project_id, user_id, old_path_with_namespace)
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 0e1e39501f5..f4eeb85270e 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Notify < BaseMailer
include ActionDispatch::Routing::PolymorphicRoutes
include GitlabRoutingHelper
@@ -92,12 +94,14 @@ class Notify < BaseMailer
# >> subject('Lorem ipsum', 'Dolor sit amet')
# => "Lorem ipsum | Dolor sit amet"
def subject(*extra)
- subject = ""
- subject << "#{@project.name} | " if @project
- subject << "#{@group.name} | " if @group
- subject << extra.join(' | ') if extra.present?
- subject << " | #{Gitlab.config.gitlab.email_subject_suffix}" if Gitlab.config.gitlab.email_subject_suffix.present?
- subject
+ subject = []
+
+ subject << @project.name if @project
+ subject << @group.name if @group
+ subject.concat(extra) if extra.present?
+ subject << Gitlab.config.gitlab.email_subject_suffix if Gitlab.config.gitlab.email_subject_suffix.present?
+
+ subject.join(' | ')
end
# Return a string suitable for inclusion in the 'Message-Id' mail header.
diff --git a/app/mailers/previews/devise_mailer_preview.rb b/app/mailers/previews/devise_mailer_preview.rb
index d6588efc486..3b9ef0d3ac0 100644
--- a/app/mailers/previews/devise_mailer_preview.rb
+++ b/app/mailers/previews/devise_mailer_preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeviseMailerPreview < ActionMailer::Preview
def confirmation_instructions_for_signup
DeviseMailer.confirmation_instructions(unsaved_user, 'faketoken', {})
diff --git a/app/mailers/previews/email_rejection_mailer_preview.rb b/app/mailers/previews/email_rejection_mailer_preview.rb
index 639e8471232..402066151ef 100644
--- a/app/mailers/previews/email_rejection_mailer_preview.rb
+++ b/app/mailers/previews/email_rejection_mailer_preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EmailRejectionMailerPreview < ActionMailer::Preview
def rejection
EmailRejectionMailer.rejection("some rejection reason", "From: someone@example.com\nraw email here").message
diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb
index 3615cde8026..df470930e9e 100644
--- a/app/mailers/previews/notify_preview.rb
+++ b/app/mailers/previews/notify_preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NotifyPreview < ActionMailer::Preview
def note_merge_request_email_for_individual_note
note_email(:note_merge_request_email) do
diff --git a/app/mailers/previews/repository_check_mailer_preview.rb b/app/mailers/previews/repository_check_mailer_preview.rb
index 19d4eab1805..834d7594719 100644
--- a/app/mailers/previews/repository_check_mailer_preview.rb
+++ b/app/mailers/previews/repository_check_mailer_preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepositoryCheckMailerPreview < ActionMailer::Preview
def notify
RepositoryCheckMailer.notify(3).message
diff --git a/app/mailers/repository_check_mailer.rb b/app/mailers/repository_check_mailer.rb
index 22a9f5da646..4bcf371cfc0 100644
--- a/app/mailers/repository_check_mailer.rb
+++ b/app/mailers/repository_check_mailer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepositoryCheckMailer < BaseMailer
def notify(failed_count)
@message =
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index bbe7811841a..c77faa4b71d 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -298,7 +298,8 @@ class ApplicationSetting < ActiveRecord::Base
unique_ips_limit_time_window: 3600,
usage_ping_enabled: Settings.gitlab['usage_ping_enabled'],
instance_statistics_visibility_private: false,
- user_default_external: false
+ user_default_external: false,
+ user_show_add_ssh_key_message: true
}
end
diff --git a/app/models/license_template.rb b/app/models/license_template.rb
new file mode 100644
index 00000000000..0ad75b27827
--- /dev/null
+++ b/app/models/license_template.rb
@@ -0,0 +1,53 @@
+class LicenseTemplate
+ PROJECT_TEMPLATE_REGEX =
+ %r{[\<\{\[]
+ (project|description|
+ one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
+ [\>\}\]]}xi.freeze
+ YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze
+ FULLNAME_TEMPLATE_REGEX =
+ %r{[\<\{\[]
+ (fullname|name\sof\s(author|copyright\sowner))
+ [\>\}\]]}xi.freeze
+
+ attr_reader :id, :name, :category, :nickname, :url, :meta
+
+ alias_method :key, :id
+
+ def initialize(id:, name:, category:, content:, nickname: nil, url: nil, meta: {})
+ @id = id
+ @name = name
+ @category = category
+ @content = content
+ @nickname = nickname
+ @url = url
+ @meta = meta
+ end
+
+ def popular?
+ category == :Popular
+ end
+ alias_method :featured?, :popular?
+
+ # Returns the text of the license
+ def content
+ if @content.respond_to?(:call)
+ @content = @content.call
+ else
+ @content
+ end
+ end
+
+ # Populate placeholders in the LicenseTemplate content
+ def resolve!(project_name: nil, fullname: nil, year: Time.now.year.to_s)
+ # Ensure the string isn't shared with any other instance of LicenseTemplate
+ new_content = content.dup
+ new_content.gsub!(YEAR_TEMPLATE_REGEX, year) if year.present?
+ new_content.gsub!(PROJECT_TEMPLATE_REGEX, project_name) if project_name.present?
+ new_content.gsub!(FULLNAME_TEMPLATE_REGEX, fullname) if fullname.present?
+
+ @content = new_content
+
+ self
+ end
+end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index b974309aeb6..0deb44d7916 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -10,6 +10,7 @@ class Namespace < ActiveRecord::Base
include Storage::LegacyNamespace
include Gitlab::SQL::Pattern
include IgnorableColumn
+ include FeatureGate
ignore_column :deleted_at
@@ -124,7 +125,6 @@ class Namespace < ActiveRecord::Base
def to_param
full_path
end
- alias_method :flipper_id, :to_param
def human_name
owner_name
diff --git a/app/views/admin/application_settings/_account_and_limit.html.haml b/app/views/admin/application_settings/_account_and_limit.html.haml
index 7c8243a7a90..622cb11010e 100644
--- a/app/views/admin/application_settings/_account_and_limit.html.haml
+++ b/app/views/admin/application_settings/_account_and_limit.html.haml
@@ -29,5 +29,11 @@
= f.check_box :user_default_external, class: 'form-check-input'
= f.label :user_default_external, class: 'form-check-label' do
Newly registered users will by default be external
+ .form-group
+ = f.label :user_show_add_ssh_key_message, 'Prompt users to upload SSH keys', class: 'label-bold'
+ .form-check
+ = f.check_box :user_show_add_ssh_key_message, class: 'form-check-input'
+ = f.label :user_show_add_ssh_key_message, class: 'form-check-label' do
+ Inform users without uploaded SSH keys that they can't push over SSH until one is added
= f.submit 'Save changes', class: 'btn btn-success'
diff --git a/app/views/admin/application_settings/show.html.haml b/app/views/admin/application_settings/show.html.haml
index 258d50ad676..6133a7646f4 100644
--- a/app/views/admin/application_settings/show.html.haml
+++ b/app/views/admin/application_settings/show.html.haml
@@ -325,6 +325,8 @@
.settings-content
= render partial: 'repository_mirrors_form'
+= render_if_exists 'admin/application_settings/templates', expanded: expanded
+
%section.settings.as-third-party-offers.no-animate#js-third-party-offers-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
diff --git a/app/views/projects/mirrors/_instructions.html.haml b/app/views/projects/mirrors/_instructions.html.haml
index 3d811be3fe3..e051f9e6331 100644
--- a/app/views/projects/mirrors/_instructions.html.haml
+++ b/app/views/projects/mirrors/_instructions.html.haml
@@ -4,7 +4,7 @@
= _('The repository must be accessible over <code>http://</code>,
<code>https://</code>, <code>ssh://</code> and <code>git://</code>.').html_safe
%li= _('Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>.').html_safe
- %li= _('The update action will time out after 15 minutes. For big repositories, use a clone/push combination.')
+ %li= _("The update action will time out after #{import_will_timeout_message(Gitlab.config.gitlab_shell.git_timeout)} minutes. For big repositories, use a clone/push combination.")
%li= _('The Git LFS objects will <strong>not</strong> be synced.').html_safe
%li
= _('This user will be the author of all events in the activity feed that are the result of an update,
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index 434aed2f603..9134257b631 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -17,7 +17,7 @@
%h5.prepend-top-0
= _("Git strategy for pipelines")
%p
- = _("Choose between <code>clone</code> or <code>fetch</code> to get the recent application code")
+ = _("Choose between <code>clone</code> or <code>fetch</code> to get the recent application code").html_safe
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'git-strategy'), target: '_blank'
.form-check
= f.radio_button :build_allow_git_fetch, 'false', { class: 'form-check-input' }
@@ -47,7 +47,7 @@
= f.label :ci_config_path, _('Custom CI config path'), class: 'label-bold'
= f.text_field :ci_config_path, class: 'form-control', placeholder: '.gitlab-ci.yml'
%p.form-text.text-muted
- = _("The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>")
+ = _("The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>").html_safe
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'custom-ci-config-path'), target: '_blank'
%hr
diff --git a/changelogs/unreleased/49953-add-user_show_add_ssh_key_message-setting.yml b/changelogs/unreleased/49953-add-user_show_add_ssh_key_message-setting.yml
new file mode 100644
index 00000000000..82423092792
--- /dev/null
+++ b/changelogs/unreleased/49953-add-user_show_add_ssh_key_message-setting.yml
@@ -0,0 +1,5 @@
+---
+title: Add ability to suppress the global "You won't be able to use SSH" message
+merge_request: 21027
+author: Ævar Arnfjörð Bjarmason
+type: added
diff --git a/changelogs/unreleased/50101-empty-state-component.yml b/changelogs/unreleased/50101-empty-state-component.yml
new file mode 100644
index 00000000000..ee99b65d964
--- /dev/null
+++ b/changelogs/unreleased/50101-empty-state-component.yml
@@ -0,0 +1,5 @@
+---
+title: Creates empty state vue component for job view
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/bvl-add-czech.yml b/changelogs/unreleased/bvl-add-czech.yml
new file mode 100644
index 00000000000..49e0e4a74b7
--- /dev/null
+++ b/changelogs/unreleased/bvl-add-czech.yml
@@ -0,0 +1,5 @@
+---
+title: Add Czech as an available language.
+merge_request: 21201
+author:
+type: added
diff --git a/changelogs/unreleased/frozen-string-enable-app-mailers.yml b/changelogs/unreleased/frozen-string-enable-app-mailers.yml
new file mode 100644
index 00000000000..2cd247ca76c
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-mailers.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen in app/mailers/**/*.rb
+merge_request: 21147
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/ide-delete-new-files-state.yml b/changelogs/unreleased/ide-delete-new-files-state.yml
new file mode 100644
index 00000000000..500115d19d0
--- /dev/null
+++ b/changelogs/unreleased/ide-delete-new-files-state.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed IDE deleting new files creating wrong state
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/ide-job-top-bar-ui-polish.yml b/changelogs/unreleased/ide-job-top-bar-ui-polish.yml
new file mode 100644
index 00000000000..d917c14e5f8
--- /dev/null
+++ b/changelogs/unreleased/ide-job-top-bar-ui-polish.yml
@@ -0,0 +1,5 @@
+---
+title: Improved styling of top bar in IDE job trace pane
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/n8rzz-consolidate-specs-testing-emoji-awards.yml b/changelogs/unreleased/n8rzz-consolidate-specs-testing-emoji-awards.yml
new file mode 100644
index 00000000000..bcf3d2c8e16
--- /dev/null
+++ b/changelogs/unreleased/n8rzz-consolidate-specs-testing-emoji-awards.yml
@@ -0,0 +1,6 @@
+---
+title: Combines emoji award spec files into single user_interacts_with_awards_in_issue_spec.rb
+ file
+merge_request: 21126
+author: Nate Geslin
+type: other
diff --git a/db/migrate/20180808162000_add_user_show_add_ssh_key_message_to_application_settings.rb b/db/migrate/20180808162000_add_user_show_add_ssh_key_message_to_application_settings.rb
new file mode 100644
index 00000000000..e3019af2cc9
--- /dev/null
+++ b/db/migrate/20180808162000_add_user_show_add_ssh_key_message_to_application_settings.rb
@@ -0,0 +1,19 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddUserShowAddSshKeyMessageToApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :application_settings, :user_show_add_ssh_key_message, :boolean, default: true, allow_null: false
+ end
+
+ def down
+ remove_column :application_settings, :user_show_add_ssh_key_message
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f1d8f4df3b7..1288a98745c 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180807153545) do
+ActiveRecord::Schema.define(version: 20180808162000) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -170,6 +170,7 @@ ActiveRecord::Schema.define(version: 20180807153545) do
t.boolean "hide_third_party_offers", default: false, null: false
t.boolean "instance_statistics_visibility_private", default: false, null: false
t.boolean "web_ide_clientside_preview_enabled", default: false, null: false
+ t.boolean "user_show_add_ssh_key_message", default: true, null: false
end
create_table "audit_events", force: :cascade do |t|
diff --git a/doc/README.md b/doc/README.md
index a814c787f94..4248f62c08c 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -133,6 +133,7 @@ scales to run your tests faster.
- [GitLab CI/CD](ci/README.md): Explore the features and capabilities of Continuous Integration, Continuous Delivery, and Continuous Deployment with GitLab.
- [Review Apps](ci/review_apps/index.md): Preview changes to your app right from a merge request.
- [Pipeline Graphs](ci/pipelines.md#pipeline-graphs)
+- [JUnit test reports](ci/junit_test_reports.md)
### Package
diff --git a/doc/administration/operations/ssh_certificates.md b/doc/administration/operations/ssh_certificates.md
index 8968afba01b..9edccd25ced 100644
--- a/doc/administration/operations/ssh_certificates.md
+++ b/doc/administration/operations/ssh_certificates.md
@@ -163,3 +163,20 @@ Such a restriction can currently be hacked in by e.g. providing a
custom `AuthorizedKeysCommand` which checks if the discovered key-ID
returned from `gitlab-shell-authorized-keys-check` is a deploy key or
not (all non-deploy keys should be refused).
+
+## Disabling the global warning about users lacking SSH keys
+
+By default GitLab will show a "You won't be able to pull or push
+project code via SSH" warning to users who have not uploaded an SSH
+key to their profile.
+
+This is counterproductive when using SSH certificates, since users
+aren't expected to upload their own keys.
+
+To disable this warning globally, go to "Application settings ->
+Account and limit settings" and disable the "Show user add SSH key
+message" setting.
+
+This setting was added specifically for use with SSH certificates, but
+can be turned off without using them if you'd like to hide the warning
+for some other reason.
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 68fc56b1fa3..b480d62e16a 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -56,7 +56,8 @@ Example response:
"enforce_terms": true,
"terms": "Hello world!",
"performance_bar_allowed_group_id": 42,
- "instance_statistics_visibility_private": false
+ "instance_statistics_visibility_private": false,
+ "user_show_add_ssh_key_message": true
}
```
@@ -161,6 +162,8 @@ PUT /application/settings
| `enforce_terms` | boolean | no | Enforce application ToS to all users |
| `terms` | text | yes (if `enforce_terms` is true) | Markdown content for the ToS |
| `instance_statistics_visibility_private` | boolean | no | When set to `true` Instance statistics will only be available to admins |
+| `user_show_add_ssh_key_message` | boolean | no | When set to `false` disable the "You won't be able to pull or push
++project code via SSH" warning shown to users with no uploaded SSH key |
```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/application/settings?signup_enabled=false&default_project_visibility=internal
@@ -206,6 +209,7 @@ Example response:
"enforce_terms": true,
"terms": "Hello world!",
"performance_bar_allowed_group_id": 42,
- "instance_statistics_visibility_private": false
+ "instance_statistics_visibility_private": false,
+ "user_show_add_ssh_key_message": true
}
```
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index 811f4d1f07a..8eb96ae10b2 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -43,6 +43,10 @@ There's also a collection of repositories with [example projects](https://gitlab
- [Using `dpl` as deployment tool](deployment/README.md)
- [The `.gitlab-ci.yml` file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
+## Test Reports
+
+[Collect test reports in Verify stage](../junit_test_reports.md).
+
## Code Quality analysis
**(Starter)** [Analyze your project's Code Quality](code_quality.md).
diff --git a/doc/ci/img/junit_test_report.png b/doc/ci/img/junit_test_report.png
new file mode 100644
index 00000000000..ad098eb457f
--- /dev/null
+++ b/doc/ci/img/junit_test_report.png
Binary files differ
diff --git a/doc/ci/junit_test_reports.md b/doc/ci/junit_test_reports.md
new file mode 100644
index 00000000000..5ae8ecaafa6
--- /dev/null
+++ b/doc/ci/junit_test_reports.md
@@ -0,0 +1,102 @@
+# JUnit test reports
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/45318) in GitLab 11.2.
+Requires GitLab Runner 11.2 and above.
+
+## Overview
+
+It is very common that a [CI/CD pipeline](pipelines.md) contains a
+test job that will verify your code.
+If the tests fail, the pipeline fails and users get notified. The person that
+works on the merge request will have to check the job logs and see where the
+tests failed so that they can fix them.
+
+You can configure your job to use JUnit test reports, and GitLab will display a
+report on the merge request so that it's easier and faster to identify the
+failure without having to check the entire log.
+
+## Use cases
+
+Consider the following workflow:
+
+1. Your `master` branch is rock solid, your project is using GitLab CI/CD and
+ your pipelines indicate that there isn't anything broken.
+1. Someone from you team submits a merge request, a test fails and the pipeline
+ gets the known red icon. To investigate more, you have to go through the job
+ logs to figure out the cause of the failed test, which usually contain
+ thousands of lines.
+1. You configure the JUnit test reports and immediately GitLab collects and
+ exposes them in the merge request. No more searching in the job logs.
+1. Your development and debugging workflow becomes easier, faster and efficient.
+
+## How it works
+
+First, GitLab Runner uploads all JUnit XML files as artifacts to GitLab. Then,
+when you visit a merge request, GitLab starts comparing the head and base branch's
+JUnit test reports, where:
+
+- The base branch is the target branch (usually `master`).
+- The head branch is the source branch (the latest pipeline in each merge request).
+
+The reports panel has a summary showing how many tests failed and how many were fixed.
+If no comparison can be done because data for the base branch is not available,
+the panel will just show the list of failed tests for head.
+
+There are three types of results:
+
+1. **Newly failed tests:** Test cases which passed on base branch and failed on head branch
+1. **Existing failures:** Test cases which failed on base branch and failed on head branch
+1. **Resolved failures:** Test cases which failed on base branch and passed on head branch
+
+Each entry in the panel will show the test name and its type from the list
+above. Clicking on the test name will open a modal window with details of its
+execution time and the error output.
+
+![Test Reports Widget](img/junit_test_report.png)
+
+## How to set it up
+
+NOTE: **Note:**
+For a list of supported languages on JUnit tests, check the
+[Wikipedia article](https://en.wikipedia.org/wiki/JUnit#Ports).
+
+To enable the JUnit reports in merge requests, you need to add
+[`artifacts:reports:junit`](yaml/README.md#artifacts-reports-junit)
+in `.gitlab-ci.yml`, and specify the path(s) of the generated test reports.
+
+In the following examples, the job in the `test` stage runs and GitLab
+collects the JUnit test report from each job. After each job is executed, the
+XML reports are stored in GitLab as artifacts and their results are shown in the
+merge request widget.
+
+### Ruby example
+
+Use the following job in `.gitlab-ci.yml`:
+
+```yaml
+## Use https://github.com/sj26/rspec_junit_formatter to generate a JUnit report with rspec
+ruby:
+ stage: test
+ script:
+ - bundle install
+ - rspec spec/lib/ --format RspecJunitFormatter --out rspec.xml
+ artifacts:
+ reports:
+ junit: rspec.xml
+```
+
+### Go example
+
+Use the following job in `.gitlab-ci.yml`:
+
+```yaml
+## Use https://github.com/jstemmer/go-junit-report to generate a JUnit report with go
+golang:
+ stage: test
+ script:
+ - go get -u github.com/jstemmer/go-junit-report
+ - go test -v 2>&1 | go-junit-report > report.xml
+ artifacts:
+ reports:
+ junit: report.xml
+```
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 95d705d3a3d..ef740ab1c5e 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1092,6 +1092,52 @@ job:
expire_in: 1 week
```
+### `artifacts:reports`
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20390) in
+GitLab 11.2. Requires GitLab Runner 11.2 and above.
+
+The `reports` keyword is used for collecting test reports from jobs and
+exposing them in GitLab's UI (merge requests, pipeline views). Read how to use
+this with [JUnit reports](#artifacts-reports-junit).
+
+NOTE: **Note:**
+The test reports are collected regardless of the job results (success or failure).
+You can use [`artifacts:expire_in`](#artifacts-expire_in) to set up an expiration
+date for their artifacts.
+
+#### `artifacts:reports:junit`
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20390) in
+GitLab 11.2. Requires GitLab Runner 11.2 and above.
+
+The `junit` report collects [JUnit XML files](https://www.ibm.com/support/knowledgecenter/en/SSQ2R2_14.1.0/com.ibm.rsar.analysis.codereview.cobol.doc/topics/cac_useresults_junit.html)
+as artifacts. Although JUnit was originally developed in Java, there are many
+[third party ports](https://en.wikipedia.org/wiki/JUnit#Ports) for other
+languages like Javascript, Python, Ruby, etc.
+
+Below is an example of collecting a JUnit XML file from Ruby's RSpec test tool:
+
+```yaml
+rspec:
+ stage: test
+ script:
+ - bundle install
+ - rspec --format RspecJunitFormatter --out rspec.xml
+ artifacts:
+ reports:
+ junit: rspec.xml
+```
+
+The collected JUnit reports will be uploaded to GitLab as an artifact and will
+be automatically [shown in merge requests](../junit_test_reports.md).
+
+NOTE: **Note:**
+In case the JUnit tool you use exports to multiple XML files, you can specify
+multiple test report paths within a single job
+(`junit: [rspec-1.xml, rspec-2.xml, rspec-3.xml]`) and they will be automatically
+concatenated into a single file.
+
## `dependencies`
> Introduced in GitLab 8.6 and GitLab Runner v1.1.1.
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index f5cdd310f6f..f46c171d9f1 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -25,52 +25,23 @@ them to review it for you.
We use the [monthly release blog post](https://about.gitlab.com/handbook/marketing/blog/release-posts/#monthly-releases) as a changelog checklist to ensure everything
is documented.
-Whenever you submit a merge request for the documentation, use the documentation MR description template.
+Whenever you submit a merge request for the documentation, use the
+"Documentation" MR description template. If you're changing documentation
+location, use the MR description template called "Change documentation
+location" instead.
-Please check the [documentation workflow](https://about.gitlab.com/handbook/product/technical-writing/workflow/) before getting started.
+## Documentation workflow
-## Documentation structure
-
-- Overview and use cases: what it is, why it is necessary, why one would use it
-- Requirements: what do we need to get started
-- Tutorial: how to set it up, how to use it
-
-Always link a new document from its topic-related index, otherwise, it will
-not be included it in the documentation site search.
-
-_Note: to be extended._
-
-### Feature overview and use cases
-
-Every major feature (regardless if present in GitLab Community or Enterprise editions)
-should present, at the beginning of the document, two main sections: **overview** and
-**use cases**. Every GitLab EE-only feature should also contain these sections.
+Please read through the [documentation workflow](workflow.md) before getting started.
-**Overview**: as the name suggests, the goal here is to provide an overview of the feature.
-Describe what is it, what it does, why it is important/cool/nice-to-have,
-what problem it solves, and what you can do with this feature that you couldn't
-do before.
-
-**Use cases**: provide at least two, ideally three, use cases for every major feature.
-You should answer this question: what can you do with this feature/change? Use cases
-are examples of how this feature or change can be used in real life.
-
-Examples:
-- CE and EE: [Issues](../user/project/issues/index.md#use-cases)
-- CE and EE: [Merge Requests](../user/project/merge_requests/index.md#overview)
-- EE-only: [Geo](https://docs.gitlab.com/ee/gitlab-geo/README.html#overview)
-- EE-only: [Jenkins integration](https://docs.gitlab.com/ee/integration/jenkins.md#overview)
-
-Note that if you don't have anything to add between the doc title (`<h1>`) and
-the header `## Overview`, you can omit the header, but keep the content of the
-overview there.
+## Documentation structure
-> **Overview** and **use cases** are required to **every** Enterprise Edition feature,
-and for every **major** feature present in Community Edition.
+Follow through the [documentation structure guide](structure.md) for learning
+how to structure GitLab docs.
## Markdown and styles
-Currently GitLab docs use Redcarpet as [markdown](../user/markdown.md) engine, but there's an [open discussion](https://gitlab.com/gitlab-com/gitlab-docs/issues/50) for implementing Kramdown in the near future.
+Currently GitLab docs use Redcarpet as [markdown](../../user/markdown.md) engine, but there's an [open discussion](https://gitlab.com/gitlab-com/gitlab-docs/issues/50) for implementing Kramdown in the near future.
All the docs follow the [documentation style guidelines](styleguide.md).
@@ -84,9 +55,18 @@ In order to have a [solid site structure](https://searchengineland.com/seo-benef
all docs should be linked. Every new document should be cross-linked to its related documentation, and linked from its topic-related index, when existent.
The directories `/workflow/`, `/gitlab-basics/`, `/university/`, and `/articles/` have
-been deprecated and the majority their docs have been moved to their correct location
+been **deprecated** and the majority their docs have been moved to their correct location
in small iterations. Please don't create new docs in these folders.
+### Documentation files
+
+- When you create a new directory, always start with an `index.md` file.
+Do not use another file name and **do not** create `README.md` files
+- **Do not** use special chars and spaces, or capital letters in file names,
+directory names, branch names, and anything that generates a path.
+- Max screenshot size: 100KB
+- We do not support videos (yet)
+
### Location and naming documents
The documentation hierarchy can be vastly improved by providing a better layout
@@ -116,7 +96,7 @@ The table below shows what kind of documentation goes where.
---
-**General rules:**
+**General rules & best practices:**
1. The correct naming and location of a new document, is a combination
of the relative URL of the document in question and the GitLab Map design
@@ -203,7 +183,7 @@ Things to note:
documentation, sometimes it might be useful to search a path deeper.
- The `*.md` extension is not used when a document is linked to GitLab's
built-in help page, that's why we omit it in `git grep`.
-- Use the checklist on the documentation MR description template.
+- Use the checklist on the "Change documentation location" MR description template.
#### Alternative redirection method
@@ -514,7 +494,7 @@ Suppose there's a process to go from point A to point B in 5 steps: `(A) 1 > 2 >
A **guide** can be understood as a description of certain processes to achieve a particular objective. A guide brings you from A to B describing the characteristics of that process, but not necessarily going over each step. It can mention, for example, steps 2 and 3, but does not necessarily explain how to accomplish them.
-- Live example: "[Static sites and GitLab Pages domains (Part 1)](../user/project/pages/getting_started_part_one.md) to [Creating and Tweaking GitLab CI/CD for GitLab Pages (Part 4)](../../user/project/pages/getting_started_part_four.md)"
+- Live example: "[Static sites and GitLab Pages domains (Part 1)](../../user/project/pages/getting_started_part_one.md) to [Creating and Tweaking GitLab CI/CD for GitLab Pages (Part 4)](../../user/project/pages/getting_started_part_four.md)"
A **tutorial** requires a clear **step-by-step** guidance to achieve a singular objective. It brings you from A to B, describing precisely all the necessary steps involved in that process, showing each of the 5 steps to go from A to B.
It does not only describes steps 2 and 3, but also shows you how to accomplish them.
diff --git a/doc/development/documentation/structure.md b/doc/development/documentation/structure.md
new file mode 100644
index 00000000000..1002836096a
--- /dev/null
+++ b/doc/development/documentation/structure.md
@@ -0,0 +1,149 @@
+---
+description: Learn the how to correctly structure GitLab documentation.
+---
+
+# Documentation structure
+
+For consistency throughout the documentation, it's important to maintain the same
+structure among the docs.
+
+Before getting started, read through the following docs:
+
+- [Contributing to GitLab documentation](index.md#contributing-to-docs)
+- [Merge requests for GitLab documentation](index.md#merge-requests-for-gitlab-documentation)
+- [Branch naming for docs-only changes](index.md#branch-naming)
+- [Documentation directory structure](index.md#documentation-directory-structure)
+- [Documentation style guidelines](styleguide.md)
+- [Documentation workflow](workflow.md)
+
+## Documentation blurb
+
+Every document should include the following content in the following sequence:
+
+- **Feature name**: defines an intuitive name for the feature that clearly
+states what it is and is consistent with any relevant UI text.
+- **Feature overview** and description: describe what it is, what it does, and in what context it should be used.
+- **Use cases**: describes real use case scenarios for that feature.
+- **Requirements**: describes what software and/or configuration is required to be able to
+use the feature and, if applicable, prerequisite knowledge for being able to follow/implement the tutorial.
+For example, familiarity with GitLab CI/CD, an account on a third-party service, dependencies installed, etc.
+Link each one to its most relevant resource; i.e., where the reader can go to begin to fullfil that requirement.
+(Another doc page, a third party application's site, etc.)
+- **Instructions**: clearly describes the steps to use the feature, leaving no gaps.
+- **Troubleshooting** guide (recommended but not required): if you know beforehand what issues
+one might have when setting it up, or when something is changed, or on upgrading, it's
+important to describe those too. Think of things that may go wrong and include them in the
+docs. This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask. Answering them beforehand only makes your
+document better and more approachable.
+
+For additional details, see the subsections below, as well as the [Documentation template for new docs](#Documentation-template-for-new-docs).
+
+### Feature overview and use cases
+
+Every major feature (regardless if present in GitLab Community or Enterprise editions)
+should present, at the beginning of the document, two main sections: **overview** and
+**use cases**. Every GitLab EE-only feature should also contain these sections.
+
+**Overview**: as the name suggests, the goal here is to provide an overview of the feature.
+Describe what is it, what it does, why it is important/cool/nice-to-have,
+what problem it solves, and what you can do with this feature that you couldn't
+do before.
+
+**Use cases**: provide at least two, ideally three, use cases for every major feature.
+You should answer this question: what can you do with this feature/change? Use cases
+are examples of how this feature or change can be used in real life.
+
+Examples:
+- CE and EE: [Issues](../user/project/issues/index.md#use-cases)
+- CE and EE: [Merge Requests](../user/project/merge_requests/index.md#overview)
+- EE-only: [Geo](https://docs.gitlab.com/ee/gitlab-geo/README.html#overview)
+- EE-only: [Jenkins integration](https://docs.gitlab.com/ee/integration/jenkins.md#overview)
+
+Note that if you don't have anything to add between the doc title (`<h1>`) and
+the header `## Overview`, you can omit the header, but keep the content of the
+overview there.
+
+> **Overview** and **use cases** are required to **every** Enterprise Edition feature,
+and for every **major** feature present in Community Edition.
+
+### Discoverability
+
+Your new document will be discoverable by the user only if:
+
+- Crosslinked from the higher-level index (e.g., Issue Boards docs
+should be linked from Issues; Prometheus docs should be linked from
+Monitoring; CI/CD tutorials should be linked from CI/CD examples).
+ - When referencing other GitLab products and features, link to their
+respective docs; when referencing third-party products or technologies,
+link out to their external sites, documentation, and resources.
+- The headings are clear. E.g., "App testing" is a bad heading, "Testing
+an application with GitLab CI/CD" is much better. Think of something
+someone will search for and use these keywords in the headings.
+
+## Documentation template for new docs
+
+To start a new document, respect the file tree and file name guidelines,
+as well as the style guidelines. Use the following template:
+
+```md
+---
+description: "short document description." # Up to ~200 chars long. They will be displayed in Google Search Snippets.
+---
+
+# Feature Name **[TIER]** (1)
+
+> [Introduced](link_to_issue_or_mr) in GitLab Tier X.Y (2).
+
+A short description for the feature (can be the same used in the frontmatter's
+`description`).
+
+## Overview
+
+To write the feature overview, you should consider answering the following questions:
+
+- What is it?
+- Who is it for?
+- What is the context in which it is used and are there any prerequisites/requirements?
+- What can the user do with it? (Be sure to consider multiple audiences, like GitLab admin and developer-user.)
+- What are the benefits to using it over any alternatives?
+
+## Use cases
+
+Describe one to three use cases for that feature. Give real-life examples.
+
+## Requirements
+
+State any requirements, if any, for using the feature and/or following along with the tutorial.
+
+The only assumption that is redundant and doesn't need to be mentioned is having an account
+on GitLab.
+
+## Instructions
+
+("Instructions" is not necessarily the name of the heading)
+
+- Write a step-by-step guide, with no gaps between the steps.
+- Start with an h2 (`##`), break complex steps into small steps using
+subheadings h3 > h4 > h5 > h6. _Never skip the hierarchy level, such
+as h2 > h4_, as it will break the TOC and may affect the breadcrumbs.
+- Use short and descriptive headings (up to ~50 chars). You can use one
+single heading `## How it works` for the instructions when the feature
+is simple and the document is short.
+- Be clear, concise, and stick to the goal of the doc: explain how to
+use that feature.
+- Use inclusive language and avoid jargons, as well as uncommon and
+fancy words. The docs should be clear and very easy to understand.
+- Write in the 3rd person (use "we", "you", "us", "one", instead of "I" or "me").
+- Always provide internal and external reference links.
+- Always link the doc from its higher-level index.
+
+<!-- ## Troubleshooting
+
+Add a troubleshooting guide when possible/applicable. -->
+```
+
+Notes:
+
+- (1): Apply the [tier badges](styleguide.md#product-badges) accordingly
+- (2): Apply the correct format for the [GitLab version introducing the feature](styleguide.md#gitlab-versions-and-tiers)
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index ad49c77aac8..6c60a517b6d 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -10,6 +10,22 @@ GitLab documentation. Check the
Check the GitLab handbook for the [writing styles guidelines](https://about.gitlab.com/handbook/communication/#writing-style-guidelines).
+## Files
+
+- [Directory structure](index.md#location-and-naming-documents): place the docs
+in the correct location
+- [Documentation files](index.md#documentation-files): name the files accordingly
+- [Markdown](../../user/markdown.md): use the GitLab Flavored Markdown in the
+documentation
+
+NOTE: **Note:**
+**Do not** use capital letters, spaces, or special chars in file names,
+branch names, directory names, headings, or in anything that generates a path.
+
+NOTE: **Note:**
+**Do not** create new `README.md` files, name them `index.md` instead. There's
+a test that will fail if it spots a new `README.md` file.
+
## Text
- Split up long lines (wrap text), this makes it much easier to review and edit. Only
@@ -61,7 +77,8 @@ For punctuation rules, please refer to the [GitLab UX guide](https://design.gitl
- Add **only one H1** in each document, by adding `#` at the beginning of
it (when using markdown). The `h1` will be the document `<title>`.
-- For subheadings, use `##`, `###` and so on
+- Start with an h2 (`##`), and respect the order h2 > h3 > h4 > h5 > h6.
+ Never skip the hierarchy level, such as h2 > h4
- Avoid putting numbers in headings. Numbers shift, hence documentation anchor
links shift too, which eventually leads to dead links. If you think it is
compelling to add numbers in headings, make sure to at least discuss it with
@@ -115,10 +132,7 @@ needs to expand the tab to find the settings you're referring to
the `.md` document that you're working on is located. Always prepend their
names with the name of the document that they will be included in. For
example, if there is a document called `twitter.md`, then a valid image name
- could be `twitter_login_screen.png`. [**Exception**: images for
- [articles](index.md#technical-articles) should be
- put in a directory called `img` underneath `/articles/article_title/img/`, therefore,
- there's no need to prepend the document name to their filenames.]
+ could be `twitter_login_screen.png`.
- Images should have a specific, non-generic name that will differentiate them.
- Keep all file names in lower case.
- Consider using PNG images instead of JPEG.
@@ -126,6 +140,8 @@ needs to expand the tab to find the settings you're referring to
- Compress gifs with <https://ezgif.com/optimize> or similar tool.
- Images should be used (only when necessary) to _illustrate_ the description
of a process, not to _replace_ it.
+- Max image size: 100KB (gifs included).
+- The GitLab docs do not support videos yet.
Inside the document:
diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md
new file mode 100644
index 00000000000..339ec80f889
--- /dev/null
+++ b/doc/development/documentation/workflow.md
@@ -0,0 +1,186 @@
+---
+description: Learn the process of shipping documentation for GitLab.
+---
+
+# Documentation process at GitLab
+
+At GitLab, developers contribute new or updated documentation along with their code, but product managers and technical writers also have essential roles in the process.
+
+- Product Managers (PMs): in the issue for all new and updated features,
+PMs include specific documentation requirements that the developer who is
+writing or updating the docs must meet, along with feature descriptions
+and use cases. They call out any specific areas where collaborating with
+a technical writer is recommended, and usually act as the first reviewer
+of the docs.
+- Developers: author documentation and merge it on time (up to a week after
+the feature freeze).
+- Technical Writers: review each issue to ensure PM's requirements are complete,
+help developers with any questions throughout the process, and act as the final
+reviewer of all new and updated docs content before it's merged.
+
+## Requirements
+
+Documentation must be delivered whenever:
+
+- A new feature is shipped
+- There are changes to the UI
+- A process, workflow, or previously documented feature is changed
+
+Documentation is not required when a feature is changed on the backend
+only and does not directly affect the way that any regular user or
+administrator would interact with GitLab.
+
+NOTE: **Note:**
+When refactoring documentation in needed, it should be submitted it in its own MR.
+**Do not** join new features' MRs with refactoring existing docs, as they might have
+different priorities.
+
+NOTE: **Note:**
+[Smaller MRs are better](https://gitlab.com/gitlab-com/blog-posts/issues/185#note_4401010)! Do not mix subjects, and ship the smallest MR possible.
+
+### Documentation review process
+
+The docs shipped by the developer should be reviewed by the PM (for accuracy) and a Technical Writer (for clarity and structure).
+
+#### Documentation updates that require Technical Writer review
+
+Every documentation change that meets the criteria below must be reviewed by a Technical Writer
+to ensure clarity and discoverability, and avoid redundancy, bad file locations, typos, broken links, etc.
+Within the GitLab issue or MR, ping the relevant technical writer for the subject area. If you're not sure who that is,
+ping any of them or all of them (`@gl\-docsteam`).
+
+A Technical Writer must review documentation updates that involve:
+
+- Docs introducing new features
+- Changing documentation location
+- Refactoring existing documentation
+- Creating new documentation files
+
+If you need any help to choose the correct place for a doc, discuss a documentation
+idea or outline, or request any other help, ping a Technical Writer on your issue, MR,
+or on Slack in `#docs`.
+
+#### Skip the PM's review
+
+When there's a non-significant change to the docs, you can skip the review
+of the PM. Add the same labels as you would for a regular doc change and
+assign the correct milestone. In these cases, assign a Technical Writer
+for approval/merge, or mention `@gl\-docsteam` in case you don't know
+which Tech Writer to assign for.
+
+#### Skip the entire review
+
+When the MR only contains corrections to the content (typos, grammar,
+broken links, etc), it can be merged without the PM's and Tech Writer's review.
+
+## Documentation structure
+
+Read through the [documentation structure](structure.md) docs for an overview.
+
+## Documentation workflow
+
+To follow a consistent workflow every month, documentation changes
+involve the Product Managers, the developer who shipped the feature,
+and the Technical Writing team. Each role is described below.
+
+### 1. Product Manager's role in the documentation process
+
+The Product Manager (PM) should add to the feature issue:
+
+- Feature name, overview/description, and use cases, for the [documentation blurb](structure.md#documentation-blurb)
+- The documentation requirements for the developer working on the docs
+ - What new page, new subsection of an existing page, or other update to an existing page/subsection is needed.
+ - Just one page/section/update or multiple (perhaps there's an end user and admin change needing docs, or we need to update a previously recommended workflow, or we want to link the new feature from various places; consider and mention all ways documentation should be affected
+ - Suggested title of any page or subsection, if applicable
+- Label the issue with `Documentation`, `Deliverable`, `docs:P1`, and assign
+ the correct milestone
+
+### 2. Developer's role in the documentation process
+
+As a developer, or as a community contributor, you should ship the documentation
+with the feature, as in GitLab the documentation is part of the product.
+
+The docs can either be shipped along with the MR introducing the code, or,
+alternatively, created from a follow-up issue and MR.
+
+The docs should be shipped **by the feature freeze date**. Justified
+exceptions are accepted, as long as the [following process](#documentation-shipped-late)
+and the missed-deliverable due date (the 14th of each month) are both respected.
+
+#### Documentation shipped in the feature MR
+
+The developer should add to the feature MR the documentation containing:
+
+- The [documentation blurb](structure.md#documentation-blurb): copy the
+feature name, overview/description, and use cases from the feature issue
+- Instructions: write how to use the feature, step by step, with no gaps.
+- [Crosslink for discoverability](structure.md#discoverability): link with
+internal docs and external resources (if applicable)
+- Index: link the new doc or the new heading from the higher-level index
+for [discoverability](#discoverability)
+- [Screenshots](styleguide.md#images): when necessary, add screenshots for:
+ - Illustrating a step of the process
+ - Indicating the location of a navigation menu
+- Label the MR with `Documentation`, `Deliverable`, `docs-P1`, and assign
+the correct milestone
+- Assign the PM for review
+- When done, mention the `@gl\-docsteam` in the MR asking for review
+- **Due date**: feature freeze date and time
+
+#### Documentation shipped in a follow-up MR
+
+If the docs aren't being shipped within the feature MR:
+
+- Create a new issue mentioning "docs" or "documentation" in the title (use the Documentation issue description template)
+- Label the issue with: `Documentation`, `Deliverable`, `docs-P1`, `<product-label>`
+(product label == CI/CD, Pages, Prometheus, etc)
+- Add the correct milestone
+- Create a new MR for shipping the docs changes and follow the same
+process [described above](#documentation-shipped-in-the-feature-mr)
+- Use the MR description template called "Documentation"
+- Add the same labels and milestone as you did for the issue
+- Assign the PM for review
+- When done, mention the `@gl\-docsteam` in the MR asking for review
+- **Due date**: feature freeze date and time
+
+#### Documentation shipped late
+
+Shipping late means that you are affecting the whole feature workflow
+as well as other teams' priorities (PMs, tech writers, release managers,
+release post reviewers), so every effort should be made to avoid this.
+
+If you did not ship the docs within the feature freeze, proceed as
+[described above](#documentation-shipped-in-a-follow-up-mr) and,
+besides the regular labels, include the labels `Pick into X.Y` and
+`missed-deliverable` in the issue and the MR, and assign them the correct
+milestone.
+
+The **due date** for **merging** `missed-deliverable` MRs is on the
+**14th** of each month.
+
+### 3. Technical Writer's role in the documentation process
+
+- **Planning**
+ - Once an issue contains a Documentation label and the current milestone, a
+technical writer reviews the Product Manager's documentation requirements
+ - Once the documentation requirements are approved, the technical writer can
+work with the developer to discuss any documentation questions and plans/outlines, as needed.
+
+- **Review** - A technical writer must review the documentation for:
+ - Clarity
+ - Relevance (make sure the content is appropriate given the impact of the feature)
+ - Location (make sure the doc is in the correct dir and has the correct name)
+ - Syntax, typos, and broken links
+ - Improvements to the content
+ - Accordance to the [docs style guide](styleguide.md)
+
+<!-- TBA: issue and MR description templates as part of the process -->
+
+<!--
+## New features vs feature updates
+
+- TBA:
+ - Describe the difference between new features and feature updates
+ - Creating a new doc vs updating an existing doc
+-->
+
diff --git a/doc/development/feature_flags.md b/doc/development/feature_flags.md
index 5d1f657015c..09ea8c05be6 100644
--- a/doc/development/feature_flags.md
+++ b/doc/development/feature_flags.md
@@ -20,7 +20,40 @@ dynamic (querying the DB etc.).
Once defined in `lib/feature.rb`, you will be able to activate a
feature for a given feature group via the [`feature_group` param of the features API](../api/features.md#set-or-create-a-feature)
+For GitLab.com, team members have access to feature flags through chatops. Only
+percentage gates are supported at this time. Setting a feature to be used 50% of
+the time, you should execute `/chatops run feature set my_feature_flag 50`.
+
## Feature flags for user applications
GitLab does not yet support the use of feature flags in deployed user applications.
-You can follow the progress on that [in the issue on our issue tracker](https://gitlab.com/gitlab-org/gitlab-ee/issues/779). \ No newline at end of file
+You can follow the progress on that [in the issue on our issue tracker](https://gitlab.com/gitlab-org/gitlab-ee/issues/779).
+
+## Developing with feature flags
+
+In general, it's better to have a group- or user-based gate, and you should prefer
+it over the use of percentage gates. This would make debugging easier, as you
+filter for example logs and errors based on actors too. Futhermore, this allows
+for enabling for the `gitlab-org` group first, while the rest of the users
+aren't impacted.
+
+```ruby
+# Good
+Feature.enabled?(:feature_flag, project)
+
+# Avoid, if possible
+Feature.enabled?(:feature_flag)
+```
+
+To use feature gates based on actors, the model needs to respond to
+`flipper_id`. For example, to enable for the Foo model:
+
+```ruby
+class Foo < ActiveRecord::Base
+ include FeatureGate
+end
+```
+
+Features that are developed and are intended to be merged behind a feature flag
+should not include a changelog entry. The entry should be added in the merge
+request removing the feature flags.
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index 86ecf33ed31..43ca498d006 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -43,8 +43,7 @@ A. Consider you are a software developer working in a team:
1. You checkout a new branch, and submit your changes through a merge request
1. You gather feedback from your team
-1. You work on the implementation optimizing code with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html) **[STARTER]**
-1. You build and test your changes with GitLab CI/CD
+1. You verify your changes with [JUnit test reports](../../../ci/junit_test_reports.md) in GitLab CI/CD
1. You request the approval from your manager
1. Your manager pushes a commit with his final review, [approves the merge request](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html), and set it to [merge when pipeline succeeds](#merge-when-pipeline-succeeds) (Merge Request Approvals are available in GitLab Starter)
1. Your changes get deployed to production with [manual actions](../../../ci/yaml/README.md#manual-actions) for GitLab CI/CD
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 458ee320099..b6393fdef19 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -1159,7 +1159,7 @@ module API
class License < Grape::Entity
expose :key, :name, :nickname
- expose :featured, as: :popular
+ expose :popular?, as: :popular
expose :url, as: :html_url
expose(:source_url) { |license| license.meta['source'] }
expose(:description) { |license| license.meta['description'] }
diff --git a/lib/api/templates.rb b/lib/api/templates.rb
index 41862768a3f..927baaea652 100644
--- a/lib/api/templates.rb
+++ b/lib/api/templates.rb
@@ -16,31 +16,8 @@ module API
gitlab_version: 8.15
}
}.freeze
- PROJECT_TEMPLATE_REGEX =
- %r{[\<\{\[]
- (project|description|
- one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
- [\>\}\]]}xi.freeze
- YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze
- FULLNAME_TEMPLATE_REGEX =
- %r{[\<\{\[]
- (fullname|name\sof\s(author|copyright\sowner))
- [\>\}\]]}xi.freeze
helpers do
- def parsed_license_template
- # We create a fresh Licensee::License object since we'll modify its
- # content in place below.
- template = Licensee::License.new(params[:name])
-
- template.content.gsub!(YEAR_TEMPLATE_REGEX, Time.now.year.to_s)
- template.content.gsub!(PROJECT_TEMPLATE_REGEX, params[:project]) if params[:project].present?
-
- fullname = params[:fullname].presence || current_user.try(:name)
- template.content.gsub!(FULLNAME_TEMPLATE_REGEX, fullname) if fullname
- template
- end
-
def render_response(template_type, template)
not_found!(template_type.to_s.singularize) unless template
present template, with: Entities::Template
@@ -56,11 +33,12 @@ module API
use :pagination
end
get "templates/licenses" do
- options = {
- featured: declared(params)[:popular].present? ? true : nil
- }
- licences = ::Kaminari.paginate_array(Licensee::License.all(options))
- present paginate(licences), with: Entities::License
+ popular = declared(params)[:popular]
+ popular = to_boolean(popular) if popular.present?
+
+ templates = LicenseTemplateFinder.new(popular: popular).execute
+
+ present paginate(::Kaminari.paginate_array(templates)), with: ::API::Entities::License
end
desc 'Get the text for a specific license' do
@@ -71,9 +49,15 @@ module API
requires :name, type: String, desc: 'The name of the template'
end
get "templates/licenses/:name", requirements: { name: /[\w\.-]+/ } do
- not_found!('License') unless Licensee::License.find(declared(params)[:name])
+ templates = LicenseTemplateFinder.new.execute
+ template = templates.find { |template| template.key == params[:name] }
+
+ not_found!('License') unless template.present?
- template = parsed_license_template
+ template.resolve!(
+ project_name: params[:project].presence,
+ fullname: params[:fullname].presence || current_user&.name
+ )
present template, with: ::API::Entities::License
end
diff --git a/lib/gitlab/git/repository_mirroring.rb b/lib/gitlab/git/repository_mirroring.rb
index 65eb5cc18cf..752a91fbb60 100644
--- a/lib/gitlab/git/repository_mirroring.rb
+++ b/lib/gitlab/git/repository_mirroring.rb
@@ -2,34 +2,7 @@ module Gitlab
module Git
module RepositoryMirroring
def remote_branches(remote_name)
- gitaly_migrate(:ref_find_all_remote_branches) do |is_enabled|
- if is_enabled
- gitaly_ref_client.remote_branches(remote_name)
- else
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- rugged_remote_branches(remote_name)
- end
- end
- end
- end
-
- private
-
- def rugged_remote_branches(remote_name)
- branches = []
-
- rugged.references.each("refs/remotes/#{remote_name}/*").map do |ref|
- name = ref.name.sub(%r{\Arefs/remotes/#{remote_name}/}, '')
-
- begin
- target_commit = Gitlab::Git::Commit.find(self, ref.target.oid)
- branches << Gitlab::Git::Branch.new(self, name, ref.target, target_commit)
- rescue Rugged::ReferenceError
- # Omit invalid branch
- end
- end
-
- branches
+ gitaly_ref_client.remote_branches(remote_name)
end
end
end
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index 343487bc361..b8213929c6a 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -22,7 +22,8 @@ module Gitlab
'tr_TR' => 'Türkçe',
'id_ID' => 'Bahasa Indonesia',
'fil_PH' => 'Filipino',
- 'pl_PL' => 'Polski'
+ 'pl_PL' => 'Polski',
+ 'cs_CZ' => 'Čeština'
}.freeze
def available_locales
diff --git a/lib/gitlab/template/finders/base_template_finder.rb b/lib/gitlab/template/finders/base_template_finder.rb
index 473b05257c6..a5105439b12 100644
--- a/lib/gitlab/template/finders/base_template_finder.rb
+++ b/lib/gitlab/template/finders/base_template_finder.rb
@@ -21,7 +21,7 @@ module Gitlab
def category_directory(category)
return @base_dir unless category.present?
- @base_dir + @categories[category]
+ File.join(@base_dir, @categories[category])
end
class << self
diff --git a/lib/gitlab/template/finders/repo_template_finder.rb b/lib/gitlab/template/finders/repo_template_finder.rb
index 33f07fa0120..29bc2393ff9 100644
--- a/lib/gitlab/template/finders/repo_template_finder.rb
+++ b/lib/gitlab/template/finders/repo_template_finder.rb
@@ -27,7 +27,7 @@ module Gitlab
directory = select_directory(file_name)
raise FileNotFoundError if directory.nil?
- category_directory(directory) + file_name
+ File.join(category_directory(directory), file_name)
end
def list_files_for(dir)
@@ -37,8 +37,8 @@ module Gitlab
entries = @repository.tree(:head, dir).entries
- names = entries.map(&:name)
- names.select { |f| f =~ self.class.filter_regex(@extension) }
+ paths = entries.map(&:path)
+ paths.select { |f| f =~ self.class.filter_regex(@extension) }
end
private
@@ -47,10 +47,10 @@ module Gitlab
return [] unless @commit
# Insert root as directory
- directories = ["", @categories.keys]
+ directories = ["", *@categories.keys]
directories.find do |category|
- path = category_directory(category) + file_name
+ path = File.join(category_directory(category), file_name)
@repository.blob_at(@commit.id, path)
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index ff719ca8d41..b370cc13f11 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5399,9 +5399,6 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
-msgid "The update action will time out after 15 minutes. For big repositories, use a clone/push combination."
-msgstr ""
-
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
deleted file mode 100644
index bf60b18873c..00000000000
--- a/spec/features/issues/award_emoji_spec.rb
+++ /dev/null
@@ -1,146 +0,0 @@
-require 'rails_helper'
-
-describe 'Awards Emoji' do
- let!(:project) { create(:project, :public) }
- let!(:user) { create(:user) }
- let(:issue) do
- create(:issue,
- assignees: [user],
- project: project)
- end
-
- context 'authorized user' do
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
-
- describe 'visiting an issue with a legacy award emoji that is not valid anymore' do
- before do
- # The `heart_tip` emoji is not valid anymore so we need to skip validation
- issue.award_emoji.build(user: user, name: 'heart_tip').save!(validate: false)
- visit project_issue_path(project, issue)
- wait_for_requests
- end
-
- # Regression test: https://gitlab.com/gitlab-org/gitlab-ce/issues/29529
- it 'does not shows a 500 page', :js do
- expect(page).to have_text(issue.title)
- end
- end
-
- describe 'Click award emoji from issue#show' do
- let!(:note) { create(:note_on_issue, noteable: issue, project: issue.project, note: "Hello world") }
-
- before do
- visit project_issue_path(project, issue)
- wait_for_requests
- end
-
- it 'increments the thumbsdown emoji', :js do
- find('[data-name="thumbsdown"]').click
- wait_for_requests
- expect(thumbsdown_emoji).to have_text("1")
- end
-
- context 'click the thumbsup emoji' do
- it 'increments the thumbsup emoji', :js do
- find('[data-name="thumbsup"]').click
- wait_for_requests
- expect(thumbsup_emoji).to have_text("1")
- end
-
- it 'decrements the thumbsdown emoji', :js do
- expect(thumbsdown_emoji).to have_text("0")
- end
- end
-
- context 'click the thumbsdown emoji' do
- it 'increments the thumbsdown emoji', :js do
- find('[data-name="thumbsdown"]').click
- wait_for_requests
- expect(thumbsdown_emoji).to have_text("1")
- end
-
- it 'decrements the thumbsup emoji', :js do
- expect(thumbsup_emoji).to have_text("0")
- end
- end
-
- it 'toggles the smiley emoji on a note', :js do
- toggle_smiley_emoji(true)
-
- within('.note-body') do
- expect(find(emoji_counter)).to have_text("1")
- end
-
- toggle_smiley_emoji(false)
-
- within('.note-body') do
- expect(page).not_to have_selector(emoji_counter)
- end
- end
-
- context 'execute /award quick action' do
- it 'toggles the emoji award on noteable', :js do
- execute_quick_action('/award :100:')
-
- expect(find(noteable_award_counter)).to have_text("1")
-
- execute_quick_action('/award :100:')
-
- expect(page).not_to have_selector(noteable_award_counter)
- end
- end
- end
- end
-
- context 'unauthorized user', :js do
- before do
- visit project_issue_path(project, issue)
- end
-
- it 'has disabled emoji button' do
- expect(first('.award-control')[:class]).to have_text('disabled')
- end
- end
-
- def execute_quick_action(cmd)
- within('.js-main-target-form') do
- fill_in 'note[note]', with: cmd
- click_button 'Comment'
- end
-
- wait_for_requests
- end
-
- def thumbsup_emoji
- page.all(emoji_counter).first
- end
-
- def thumbsdown_emoji
- page.all(emoji_counter).last
- end
-
- def emoji_counter
- 'span.js-counter'
- end
-
- def noteable_award_counter
- ".awards .active"
- end
-
- def toggle_smiley_emoji(status)
- within('.note') do
- find('.note-emoji-button').click
- end
-
- unless status
- first('[data-name="smiley"]').click
- else
- find('[data-name="smiley"]').click
- end
-
- wait_for_requests
- end
-end
diff --git a/spec/features/issues/award_spec.rb b/spec/features/issues/award_spec.rb
deleted file mode 100644
index e53a4ce49c7..00000000000
--- a/spec/features/issues/award_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require 'rails_helper'
-
-describe 'Issue awards', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, :public) }
- let(:issue) { create(:issue, project: project) }
-
- describe 'logged in' do
- before do
- sign_in(user)
- visit project_issue_path(project, issue)
- wait_for_requests
- end
-
- it 'adds award to issue' do
- first('.js-emoji-btn').click
- expect(page).to have_selector('.js-emoji-btn.active')
- expect(first('.js-emoji-btn')).to have_content '1'
-
- visit project_issue_path(project, issue)
- expect(first('.js-emoji-btn')).to have_content '1'
- end
-
- it 'removes award from issue' do
- first('.js-emoji-btn').click
- find('.js-emoji-btn.active').click
- expect(first('.js-emoji-btn')).to have_content '0'
-
- visit project_issue_path(project, issue)
- expect(first('.js-emoji-btn')).to have_content '0'
- end
-
- it 'only has one menu on the page' do
- first('.js-add-award').click
- expect(page).to have_selector('.emoji-menu')
-
- expect(page).to have_selector('.emoji-menu', count: 1)
- end
- end
-
- describe 'logged out' do
- before do
- visit project_issue_path(project, issue)
- wait_for_requests
- end
-
- it 'does not see award menu button' do
- expect(page).not_to have_selector('.js-award-holder')
- end
- end
-end
diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb
new file mode 100644
index 00000000000..afa425c2cec
--- /dev/null
+++ b/spec/features/issues/user_interacts_with_awards_spec.rb
@@ -0,0 +1,347 @@
+require 'spec_helper'
+
+describe 'User interacts with awards' do
+ let(:user) { create(:user) }
+
+ describe 'User interacts with awards in an issue', :js do
+ let(:issue) { create(:issue, project: project)}
+ let(:project) { create(:project) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ visit(project_issue_path(project, issue))
+ end
+
+ it 'toggles the thumbsup award emoji' do
+ page.within('.awards') do
+ thumbsup = page.first('.award-control')
+ thumbsup.click
+ thumbsup.hover
+
+ expect(page).to have_selector('.js-emoji-btn')
+ expect(page).to have_css(".js-emoji-btn.active[data-original-title='You']")
+ expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
+
+ thumbsup = page.first('.award-control')
+ thumbsup.click
+ thumbsup.hover
+
+ expect(page).to have_selector('.award-control.js-emoji-btn')
+ expect(page.all('.award-control.js-emoji-btn').size).to eq(2)
+
+ page.all('.award-control.js-emoji-btn').each do |element|
+ expect(element['title']).to eq('')
+ end
+
+ expect(page.all('.award-control .js-counter')).to all(have_content('0'))
+
+ thumbsup = page.first('.award-control')
+ thumbsup.click
+ thumbsup.hover
+
+ expect(page).to have_selector('.js-emoji-btn')
+ expect(page).to have_css(".js-emoji-btn.active[data-original-title='You']")
+ expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
+ end
+ end
+
+ it 'toggles a custom award emoji' do
+ page.within('.awards') do
+ page.find('.js-add-award').click
+ end
+
+ page.find('.emoji-menu.is-visible')
+
+ expect(page).to have_selector('.js-emoji-menu-search')
+ expect(page.evaluate_script("document.activeElement.classList.contains('js-emoji-menu-search')")).to eq(true)
+
+ page.within('.emoji-menu-content') do
+ emoji_button = page.first('.js-emoji-btn')
+ emoji_button.hover
+ emoji_button.click
+ end
+
+ page.within('.awards') do
+ expect(page).to have_selector('.js-emoji-btn')
+ expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
+ expect(page).to have_css(".js-emoji-btn.active[data-original-title='You']")
+
+ expect do
+ page.find('.js-emoji-btn.active').click
+ wait_for_requests
+ end.to change { page.all('.award-control.js-emoji-btn').size }.from(3).to(2)
+ end
+ end
+
+ it 'shows the list of award emoji categories' do
+ page.within('.awards') do
+ page.find('.js-add-award').click
+ end
+
+ page.find('.emoji-menu.is-visible')
+
+ expect(page).to have_selector('.js-emoji-menu-search')
+ expect(page.evaluate_script("document.activeElement.classList.contains('js-emoji-menu-search')")).to eq(true)
+
+ fill_in('emoji-menu-search', with: 'hand')
+
+ page.within('.emoji-menu-content') do
+ expect(page).to have_selector('[data-name="raised_hand"]')
+ end
+ end
+
+ it 'adds an award emoji by a comment' do
+ page.within('.js-main-target-form') do
+ fill_in('note[note]', with: ':smile:')
+
+ click_button('Comment')
+ end
+
+ expect(page).to have_emoji('smile')
+ end
+
+ context 'when a project is archived' do
+ let(:project) { create(:project, :archived) }
+
+ it 'hides the add award button' do
+ page.within('.awards') do
+ expect(page).not_to have_css('.js-add-award')
+ end
+ end
+ end
+
+ context 'User interacts with awards on a note' do
+ let!(:note) { create(:note, noteable: issue, project: issue.project) }
+ let!(:award_emoji) { create(:award_emoji, awardable: note, name: '100') }
+
+ it 'shows the award on the note' do
+ page.within('.note-awards') do
+ expect(page).to have_emoji('100')
+ end
+ end
+
+ it 'allows adding a vote to an award' do
+ page.within('.note-awards') do
+ find('gl-emoji[data-name="100"]').click
+ end
+ wait_for_requests
+
+ expect(note.reload.award_emoji.size).to eq(2)
+ end
+
+ it 'allows adding a new emoji' do
+ page.within('.note-actions') do
+ find('a.js-add-award').click
+ end
+ page.within('.emoji-menu-content') do
+ find('gl-emoji[data-name="8ball"]').click
+ end
+ wait_for_requests
+
+ page.within('.note-awards') do
+ expect(page).to have_emoji('8ball')
+ end
+ expect(note.reload.award_emoji.size).to eq(2)
+ end
+
+ context 'when the project is archived' do
+ let(:project) { create(:project, :archived) }
+
+ it 'hides the buttons for adding new emoji' do
+ page.within('.note-awards') do
+ expect(page).not_to have_css('.award-menu-holder')
+ end
+
+ page.within('.note-actions') do
+ expect(page).not_to have_css('a.js-add-award')
+ end
+ end
+
+ it 'does not allow toggling existing emoji' do
+ page.within('.note-awards') do
+ find('gl-emoji[data-name="100"]').click
+ end
+ wait_for_requests
+
+ expect(note.reload.award_emoji.size).to eq(1)
+ end
+ end
+ end
+ end
+
+ describe 'User interacts with awards on an issue', :js do
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, project: project) }
+
+ describe 'logged in' do
+ before do
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ it 'adds award to issue' do
+ first('.js-emoji-btn').click
+
+ expect(page).to have_selector('.js-emoji-btn.active')
+ expect(first('.js-emoji-btn')).to have_content '1'
+
+ visit project_issue_path(project, issue)
+
+ expect(first('.js-emoji-btn')).to have_content '1'
+ end
+
+ it 'removes award from issue' do
+ first('.js-emoji-btn').click
+ find('.js-emoji-btn.active').click
+
+ expect(first('.js-emoji-btn')).to have_content '0'
+
+ visit project_issue_path(project, issue)
+
+ expect(first('.js-emoji-btn')).to have_content '0'
+ end
+
+ it 'only has one menu on the page' do
+ first('.js-add-award').click
+
+ expect(page).to have_selector('.emoji-menu', count: 1)
+ end
+ end
+
+ describe 'logged out' do
+ before do
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ it 'does not see award menu button' do
+ expect(page).not_to have_selector('.js-award-holder')
+ end
+ end
+ end
+
+ describe 'Awards Emoji' do
+ let!(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, assignees: [user], project: project) }
+
+ context 'authorized user' do
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ describe 'visiting an issue with a legacy award emoji that is not valid anymore' do
+ before do
+ # The `heart_tip` emoji is not valid anymore so we need to skip validation
+ issue.award_emoji.build(user: user, name: 'heart_tip').save!(validate: false)
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ # Regression test: https://gitlab.com/gitlab-org/gitlab-ce/issues/29529
+ it 'does not shows a 500 page', :js do
+ expect(page).to have_text(issue.title)
+ end
+ end
+
+ describe 'Click award emoji from issue#show' do
+ let!(:note) { create(:note_on_issue, noteable: issue, project: issue.project, note: "Hello world") }
+
+ before do
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ context 'click the thumbsdown emoji' do
+ it 'increments the thumbsdown emoji', :js do
+ find('[data-name="thumbsdown"]').click
+ wait_for_requests
+ expect(thumbsdown_emoji).to have_text("1")
+ end
+
+ it 'decrements the thumbsup emoji', :js do
+ expect(thumbsup_emoji).to have_text("0")
+ end
+ end
+
+ it 'toggles the smiley emoji on a note', :js do
+ toggle_smiley_emoji(true)
+
+ within('.note-body') do
+ expect(find(emoji_counter)).to have_text("1")
+ end
+
+ toggle_smiley_emoji(false)
+
+ within('.note-body') do
+ expect(page).not_to have_selector(emoji_counter)
+ end
+ end
+
+ context 'execute /award quick action' do
+ it 'toggles the emoji award on noteable', :js do
+ execute_quick_action('/award :100:')
+
+ expect(find(noteable_award_counter)).to have_text("1")
+
+ execute_quick_action('/award :100:')
+
+ expect(page).not_to have_selector(noteable_award_counter)
+ end
+ end
+ end
+ end
+
+ context 'unauthorized user', :js do
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ it 'has disabled emoji button' do
+ expect(first('.award-control')[:class]).to have_text('disabled')
+ end
+ end
+
+ def execute_quick_action(cmd)
+ within('.js-main-target-form') do
+ fill_in 'note[note]', with: cmd
+ click_button 'Comment'
+ end
+
+ wait_for_requests
+ end
+
+ def thumbsup_emoji
+ page.all(emoji_counter).first
+ end
+
+ def thumbsdown_emoji
+ page.all(emoji_counter).last
+ end
+
+ def emoji_counter
+ 'span.js-counter'
+ end
+
+ def noteable_award_counter
+ ".awards .active"
+ end
+
+ def toggle_smiley_emoji(status)
+ within('.note') do
+ find('.note-emoji-button').click
+ end
+
+ if !status
+ first('[data-name="smiley"]').click
+ else
+ find('[data-name="smiley"]').click
+ end
+
+ wait_for_requests
+ end
+ end
+end
diff --git a/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb b/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb
deleted file mode 100644
index 4d860893abe..00000000000
--- a/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb
+++ /dev/null
@@ -1,172 +0,0 @@
-require 'spec_helper'
-
-describe 'User interacts with awards in an issue', :js do
- let(:issue) { create(:issue, project: project)}
- let(:project) { create(:project) }
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
-
- visit(project_issue_path(project, issue))
- end
-
- it 'toggles the thumbsup award emoji' do
- page.within('.awards') do
- thumbsup = page.first('.award-control')
- thumbsup.click
- thumbsup.hover
-
- expect(page).to have_selector('.js-emoji-btn')
- expect(page).to have_css(".js-emoji-btn.active[data-original-title='You']")
- expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
-
- thumbsup = page.first('.award-control')
- thumbsup.click
- thumbsup.hover
-
- expect(page).to have_selector('.award-control.js-emoji-btn')
- expect(page.all('.award-control.js-emoji-btn').size).to eq(2)
-
- page.all('.award-control.js-emoji-btn').each do |element|
- expect(element['title']).to eq('')
- end
-
- page.all('.award-control .js-counter').each do |element|
- expect(element).to have_content('0')
- end
-
- thumbsup = page.first('.award-control')
- thumbsup.click
- thumbsup.hover
-
- expect(page).to have_selector('.js-emoji-btn')
- expect(page).to have_css(".js-emoji-btn.active[data-original-title='You']")
- expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
- end
- end
-
- it 'toggles a custom award emoji' do
- page.within('.awards') do
- page.find('.js-add-award').click
- end
-
- page.find('.emoji-menu.is-visible')
-
- expect(page).to have_selector('.js-emoji-menu-search')
- expect(page.evaluate_script("document.activeElement.classList.contains('js-emoji-menu-search')")).to eq(true)
-
- page.within('.emoji-menu-content') do
- emoji_button = page.first('.js-emoji-btn')
- emoji_button.hover
- emoji_button.click
- end
-
- page.within('.awards') do
- expect(page).to have_selector('.js-emoji-btn')
- expect(page.find('.js-emoji-btn.active .js-counter')).to have_content('1')
- expect(page).to have_css(".js-emoji-btn.active[data-original-title='You']")
-
- expect do
- page.find('.js-emoji-btn.active').click
- wait_for_requests
- end.to change { page.all('.award-control.js-emoji-btn').size }.from(3).to(2)
- end
- end
-
- it 'shows the list of award emoji categories' do
- page.within('.awards') do
- page.find('.js-add-award').click
- end
-
- page.find('.emoji-menu.is-visible')
-
- expect(page).to have_selector('.js-emoji-menu-search')
- expect(page.evaluate_script("document.activeElement.classList.contains('js-emoji-menu-search')")).to eq(true)
-
- fill_in('emoji-menu-search', with: 'hand')
-
- page.within('.emoji-menu-content') do
- expect(page).to have_selector('[data-name="raised_hand"]')
- end
- end
-
- it 'adds an award emoji by a comment' do
- page.within('.js-main-target-form') do
- fill_in('note[note]', with: ':smile:')
-
- click_button('Comment')
- end
-
- expect(page).to have_emoji('smile')
- end
-
- context 'when a project is archived' do
- let(:project) { create(:project, :archived) }
-
- it 'hides the add award button' do
- page.within('.awards') do
- expect(page).not_to have_css('.js-add-award')
- end
- end
- end
-
- context 'awards on a note' do
- let!(:note) { create(:note, noteable: issue, project: issue.project) }
- let!(:award_emoji) { create(:award_emoji, awardable: note, name: '100') }
-
- it 'shows the award on the note' do
- page.within('.note-awards') do
- expect(page).to have_emoji('100')
- end
- end
-
- it 'allows adding a vote to an award' do
- page.within('.note-awards') do
- find('gl-emoji[data-name="100"]').click
- end
- wait_for_requests
-
- expect(note.reload.award_emoji.size).to eq(2)
- end
-
- it 'allows adding a new emoji' do
- page.within('.note-actions') do
- find('a.js-add-award').click
- end
- page.within('.emoji-menu-content') do
- find('gl-emoji[data-name="8ball"]').click
- end
- wait_for_requests
-
- page.within('.note-awards') do
- expect(page).to have_emoji('8ball')
- end
- expect(note.reload.award_emoji.size).to eq(2)
- end
-
- context 'when the project is archived' do
- let(:project) { create(:project, :archived) }
-
- it 'hides the buttons for adding new emoji' do
- page.within('.note-awards') do
- expect(page).not_to have_css('.award-menu-holder')
- end
-
- page.within('.note-actions') do
- expect(page).not_to have_css('a.js-add-award')
- end
- end
-
- it 'does not allow toggling existing emoji' do
- page.within('.note-awards') do
- find('gl-emoji[data-name="100"]').click
- end
- wait_for_requests
-
- expect(note.reload.award_emoji.size).to eq(1)
- end
- end
- end
-end
diff --git a/spec/finders/license_template_finder_spec.rb b/spec/finders/license_template_finder_spec.rb
new file mode 100644
index 00000000000..a97903103c9
--- /dev/null
+++ b/spec/finders/license_template_finder_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+
+describe LicenseTemplateFinder do
+ describe '#execute' do
+ subject(:result) { described_class.new(params).execute }
+
+ let(:categories) { categorised_licenses.keys }
+ let(:categorised_licenses) { result.group_by(&:category) }
+
+ context 'popular: true' do
+ let(:params) { { popular: true } }
+
+ it 'only returns popular licenses' do
+ expect(categories).to contain_exactly(:Popular)
+ expect(categorised_licenses[:Popular]).to be_present
+ end
+ end
+
+ context 'popular: false' do
+ let(:params) { { popular: false } }
+
+ it 'only returns unpopular licenses' do
+ expect(categories).to contain_exactly(:Other)
+ expect(categorised_licenses[:Other]).to be_present
+ end
+ end
+
+ context 'popular: nil' do
+ let(:params) { { popular: nil } }
+
+ it 'returns all licenses known by the Licensee gem' do
+ from_licensee = Licensee::License.all.map { |l| l.key }
+
+ expect(result.map(&:id)).to match_array(from_licensee)
+ end
+
+ it 'correctly copies all attributes' do
+ licensee = Licensee::License.all.first
+ found = result.find { |r| r.key == licensee.key }
+
+ aggregate_failures do
+ %i[key name content nickname url meta featured?].each do |k|
+ expect(found.public_send(k)).to eq(licensee.public_send(k))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/helpers/button_helper_spec.rb b/spec/helpers/button_helper_spec.rb
index 630f3eff258..0c0a0003231 100644
--- a/spec/helpers/button_helper_spec.rb
+++ b/spec/helpers/button_helper_spec.rb
@@ -79,6 +79,18 @@ describe ButtonHelper do
end
end
+ context 'without an ssh key on the user and user_show_add_ssh_key_message unset' do
+ before do
+ stub_application_setting(user_show_add_ssh_key_message: false)
+ end
+
+ it 'there is no warning on the dropdown description' do
+ description = element.search('.dropdown-menu-inner-content').first
+
+ expect(description).to be_nil
+ end
+ end
+
context 'with an ssh key on the user' do
before do
create(:key, user: user)
diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js
index 1e836dbc3f9..6ce76aaa03b 100644
--- a/spec/javascripts/ide/stores/mutations_spec.js
+++ b/spec/javascripts/ide/stores/mutations_spec.js
@@ -213,6 +213,33 @@ describe('Multi-file store mutations', () => {
expect(localState.changedFiles).toEqual([localState.entries.filePath]);
});
+
+ it('does not add tempFile into changedFiles', () => {
+ localState.entries.filePath = {
+ deleted: false,
+ type: 'blob',
+ tempFile: true,
+ };
+
+ mutations.DELETE_ENTRY(localState, 'filePath');
+
+ expect(localState.changedFiles).toEqual([]);
+ });
+
+ it('removes tempFile from changedFiles when deleted', () => {
+ localState.entries.filePath = {
+ path: 'filePath',
+ deleted: false,
+ type: 'blob',
+ tempFile: true,
+ };
+
+ localState.changedFiles.push({ ...localState.entries.filePath });
+
+ mutations.DELETE_ENTRY(localState, 'filePath');
+
+ expect(localState.changedFiles).toEqual([]);
+ });
});
describe('UPDATE_FILE_AFTER_COMMIT', () => {
diff --git a/spec/javascripts/jobs/empty_state_spec.js b/spec/javascripts/jobs/empty_state_spec.js
new file mode 100644
index 00000000000..f8feb069fe0
--- /dev/null
+++ b/spec/javascripts/jobs/empty_state_spec.js
@@ -0,0 +1,90 @@
+import Vue from 'vue';
+import component from '~/jobs/components/empty_state.vue';
+import mountComponent from '../helpers/vue_mount_component_helper';
+
+describe('Empty State', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const props = {
+ illustrationPath: 'illustrations/pending_job_empty.svg',
+ illustrationSizeClass: 'svg-430',
+ title: 'This job has not started yet',
+ };
+
+ const content = 'This job is in pending state and is waiting to be picked by a runner';
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('renders image and title', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ ...props,
+ content,
+ });
+ });
+
+ it('renders img with provided path and size', () => {
+ expect(vm.$el.querySelector('img').getAttribute('src')).toEqual(props.illustrationPath);
+ expect(vm.$el.querySelector('.svg-content').classList).toContain(props.illustrationSizeClass);
+ });
+
+ it('renders provided title', () => {
+ expect(vm.$el.querySelector('.js-job-empty-state-title').textContent.trim()).toEqual(
+ props.title,
+ );
+ });
+ });
+
+ describe('with content', () => {
+ it('renders content', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ content,
+ });
+
+ expect(vm.$el.querySelector('.js-job-empty-state-content').textContent.trim()).toEqual(
+ content,
+ );
+ });
+ });
+
+ describe('without content', () => {
+ it('does not render content', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ });
+ expect(vm.$el.querySelector('.js-job-empty-state-content')).toBeNull();
+ });
+ });
+
+ describe('with action', () => {
+ it('renders action', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ content,
+ action: {
+ link: 'runner',
+ title: 'Check runner',
+ method: 'post',
+ },
+ });
+
+ expect(vm.$el.querySelector('.js-job-empty-state-action').getAttribute('href')).toEqual(
+ 'runner',
+ );
+ });
+ });
+
+ describe('without action', () => {
+ it('does not render action', () => {
+ vm = mountComponent(Component, {
+ ...props,
+ content,
+ });
+ expect(vm.$el.querySelector('.js-job-empty-state-action')).toBeNull();
+ });
+ });
+});
diff --git a/spec/lib/gitlab/template/finders/repo_template_finders_spec.rb b/spec/lib/gitlab/template/finders/repo_template_finders_spec.rb
new file mode 100644
index 00000000000..2eabccd5dff
--- /dev/null
+++ b/spec/lib/gitlab/template/finders/repo_template_finders_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe Gitlab::Template::Finders::RepoTemplateFinder do
+ set(:project) { create(:project, :repository) }
+
+ let(:categories) { { 'HTML' => 'html' } }
+
+ subject(:finder) { described_class.new(project, 'files/', '.html', categories) }
+
+ describe '#read' do
+ it 'returns the content of the given path' do
+ result = finder.read('files/html/500.html')
+
+ expect(result).to be_present
+ end
+
+ it 'raises an error if the path does not exist' do
+ expect { finder.read('does/not/exist') }.to raise_error(described_class::FileNotFoundError)
+ end
+ end
+
+ describe '#find' do
+ it 'returns the full path of the found template' do
+ result = finder.find('500')
+
+ expect(result).to eq('files/html/500.html')
+ end
+ end
+
+ describe '#list_files_for' do
+ it 'returns the full path of the found files' do
+ result = finder.list_files_for('files/html')
+
+ expect(result).to contain_exactly('files/html/500.html')
+ end
+ end
+end
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index 20600f5fa38..f2aad455d5f 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -30,7 +30,7 @@ describe InternalId do
context 'with existing issues' do
before do
- rand(1..10).times { create(:issue, project: project) }
+ create_list(:issue, 2, project: project)
described_class.delete_all
end
@@ -54,7 +54,7 @@ describe InternalId do
end
it 'generates a strictly monotone, gapless sequence' do
- seq = (0..rand(100)).map do
+ seq = Array.new(10).map do
described_class.generate_next(issue, scope, usage, init)
end
normalized = seq.map { |i| i - seq.min }
diff --git a/spec/models/license_template_spec.rb b/spec/models/license_template_spec.rb
new file mode 100644
index 00000000000..c633e1908d4
--- /dev/null
+++ b/spec/models/license_template_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+
+describe LicenseTemplate do
+ describe '#content' do
+ it 'calls a proc exactly once if provided' do
+ lazy = build_template(-> { 'bar' })
+ content = lazy.content
+
+ expect(content).to eq('bar')
+ expect(content.object_id).to eq(lazy.content.object_id)
+
+ content.replace('foo')
+ expect(lazy.content).to eq('foo')
+ end
+
+ it 'returns a string if provided' do
+ lazy = build_template('bar')
+
+ expect(lazy.content).to eq('bar')
+ end
+ end
+
+ describe '#resolve!' do
+ let(:content) do
+ <<~TEXT
+ Pretend License
+
+ [project]
+
+ Copyright (c) [year] [fullname]
+ TEXT
+ end
+
+ let(:expected) do
+ <<~TEXT
+ Pretend License
+
+ Foo Project
+
+ Copyright (c) 1985 Nick Thomas
+ TEXT
+ end
+
+ let(:template) { build_template(content) }
+
+ it 'updates placeholders in a copy of the template content' do
+ expect(template.content.object_id).to eq(content.object_id)
+
+ template.resolve!(project_name: "Foo Project", fullname: "Nick Thomas", year: "1985")
+
+ expect(template.content).to eq(expected)
+ expect(template.content.object_id).not_to eq(content.object_id)
+ end
+ end
+
+ def build_template(content)
+ described_class.new(id: 'foo', name: 'foo', category: :Other, content: content)
+ end
+end
diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb
index 6bb53fdc98d..d1e16ab9ca9 100644
--- a/spec/requests/api/templates_spec.rb
+++ b/spec/requests/api/templates_spec.rb
@@ -56,6 +56,8 @@ describe API::Templates do
end
it 'returns a license template' do
+ expect(response).to have_gitlab_http_status(200)
+
expect(json_response['key']).to eq('mit')
expect(json_response['name']).to eq('MIT License')
expect(json_response['nickname']).to be_nil
@@ -181,6 +183,7 @@ describe API::Templates do
it 'replaces the copyright owner placeholder with the name of the current user' do
get api('/templates/licenses/mit', user)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['content']).to include("Copyright (c) #{Time.now.year} #{user.name}")
end
end