Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-04-21 18:09:26 +00:00
parent 71c6e099a8
commit 07d811cd3c
58 changed files with 649 additions and 326 deletions

View File

@ -115,12 +115,6 @@ Dangerfile @gl-quality/eng-prod
/ee/spec/frontend/license_compliance/components/detected_licenses_table_spec.js @gitlab-org/govern/threat-insights-frontend-team
^[Secure]
/ee/lib/gitlab/ci/parsers/license_compliance/ @gitlab-org/secure/composition-analysis-be
/ee/lib/gitlab/ci/parsers/security/ @gitlab-org/secure/composition-analysis-be @gitlab-org/secure/dynamic-analysis-be @gitlab-org/secure/static-analysis-be @gitlab-org/secure/fuzzing-be
/ee/lib/gitlab/ci/reports/coverage_fuzzing/ @gitlab-org/secure/fuzzing-be
/ee/lib/gitlab/ci/reports/dependency_list/ @gitlab-org/secure/composition-analysis-be
/ee/lib/gitlab/ci/reports/license_scanning/ @gitlab-org/secure/composition-analysis-be
/ee/lib/gitlab/ci/reports/security/ @gitlab-org/secure/composition-analysis-be @gitlab-org/secure/dynamic-analysis-be @gitlab-org/secure/static-analysis-be @gitlab-org/secure/fuzzing-be
/ee/app/services/app_sec/dast/ @gitlab-org/secure/dynamic-analysis-be
^[Security Policies]
@ -1372,6 +1366,16 @@ lib/gitlab/checks/** @proglottis @toon
/lib/gitlab/ci/templates/Jobs/SAST.*.yml @gitlab-org/secure/static-analysis
/lib/gitlab/ci/templates/Jobs/Secret-Detection.*.yml @gitlab-org/secure/static-analysis
# Overrides for Verify. These files below require approval from teams outside Verify.
/**/ci/reports/**/ @gitlab-org/maintainers/rails-backend
/**/ci/parsers/**/ @gitlab-org/maintainers/rails-backend
/ee/lib/gitlab/ci/parsers/license_compliance/ @gitlab-org/secure/composition-analysis-be
/ee/lib/gitlab/ci/parsers/security/ @gitlab-org/govern/threat-insights-backend-team
/ee/lib/gitlab/ci/reports/coverage_fuzzing/ @gitlab-org/secure/fuzzing-be
/ee/lib/gitlab/ci/reports/dependency_list/ @gitlab-org/secure/composition-analysis-be
/ee/lib/gitlab/ci/reports/license_scanning/ @gitlab-org/secure/composition-analysis-be
/ee/lib/gitlab/ci/reports/security/ @gitlab-org/govern/threat-insights-backend-team
[Manage::Workspace]
lib/api/entities/basic_project_details.rb @gitlab-org/manage/manage-workspace/backend-approvers
lib/api/entities/project_with_access.rb @gitlab-org/manage/manage-workspace/backend-approvers

View File

@ -619,6 +619,8 @@
.rails:rules:ee-and-foss-default-rules:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request
changes: *core-backend-patterns
@ -631,6 +633,8 @@
.rails:rules:system-default-rules:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request
changes: *core-backend-patterns
@ -645,6 +649,8 @@
.rails:rules:previous-failed-tests-default-rules:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-security-merge-request
when: never
- <<: *if-merge-request-labels-run-all-rspec
@ -811,6 +817,8 @@
rules:
- <<: *if-not-canonical-namespace
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-targeting-stable-branch
- <<: *if-merge-request-labels-run-review-app
- <<: *if-dot-com-gitlab-org-and-security-merge-request
@ -859,6 +867,8 @@
rules:
- <<: *if-not-canonical-namespace
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-targeting-stable-branch
- <<: *if-ruby2-branch
- <<: *if-merge-request-labels-run-review-app
@ -954,6 +964,8 @@
######################
.dev-fixtures:rules:ee-and-foss:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-default-refs
changes: *code-backstage-patterns
@ -961,6 +973,8 @@
rules:
- <<: *if-not-ee
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-default-refs
changes: *code-backstage-patterns
@ -1006,6 +1020,8 @@
rules:
# The `glfm-verify` job has dependencies on EE, so only run it for EE
- !reference [".strict-ee-only-rules", rules]
- <<: *if-merge-request-labels-pipeline-expedite
when: never
# If any of the files that are DIRECTLY related to generating or managing the GLFM specification change,
# run `glfm-verify` to get quick feedback on any needed updates, even if the MR is not yet approved
- changes: *glfm-patterns
@ -1032,7 +1048,6 @@
##################
# Frontend rules #
##################
.frontend:rules:predictive-default-rules:
rules:
- <<: *if-merge-request-approved
@ -1041,11 +1056,15 @@
when: never
- <<: *if-security-merge-request
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
.frontend:rules:compile-production-assets:
rules:
- <<: *if-not-canonical-namespace
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-targeting-stable-branch
- <<: *if-merge-request-labels-run-review-app
- <<: *if-merge-request-labels-run-all-e2e
@ -1064,8 +1083,6 @@
when: never
- <<: *if-not-ee
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- !reference [.frontend:rules:compile-production-assets, rules]
.frontend:rules:compile-test-assets:
@ -1092,6 +1109,8 @@
.frontend:rules:default-frontend-jobs:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request-labels-frontend-and-feature-flag
- <<: *if-default-refs
@ -1100,6 +1119,8 @@
.frontend:rules:default-frontend-jobs-as-if-foss:
rules:
- !reference [".strict-ee-only-rules", rules]
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-security-merge-request
changes: *code-backstage-patterns
- <<: *if-merge-request-labels-as-if-foss
@ -1136,6 +1157,8 @@
.frontend:rules:jest:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-fork-merge-request
when: never
- <<: *if-merge-request-labels-run-all-jest
@ -1175,6 +1198,8 @@
rules:
- !reference [".strict-ee-only-rules", rules]
- <<: *if-merge-request-labels-as-if-foss
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-labels-run-all-jest
- <<: *if-merge-request
changes: *frontend-dependency-patterns
@ -1205,6 +1230,8 @@
rules:
- !reference [".strict-ee-only-rules", rules]
- <<: *if-merge-request-labels-as-if-foss
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request
changes: *frontend-patterns-for-as-if-foss
@ -1223,6 +1250,8 @@
rules:
- <<: *if-not-canonical-namespace
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-default-branch-refs
changes: *frontend-build-patterns
allow_failure: true
@ -1235,6 +1264,8 @@
################
.memory:rules:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-default-refs
changes: *code-patterns
@ -1295,6 +1326,8 @@
rules:
- <<: *if-not-ee
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-targeting-stable-branch
allow_failure: true
- <<: *if-dot-com-gitlab-org-and-security-merge-request
@ -1431,6 +1464,8 @@
.rails:rules:single-db:
rules:
- <<: *if-merge-request-labels-run-single-db
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request
changes: *db-patterns
- <<: *if-merge-request
@ -1440,6 +1475,8 @@
.rails:rules:db:check-migrations-single-db:
rules:
- <<: *if-merge-request-labels-run-single-db
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request
changes: *db-patterns
- <<: *if-merge-request
@ -1448,6 +1485,8 @@
.rails:rules:single-db-ci-connection:
rules:
- <<: *if-merge-request-labels-run-single-db
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request
changes: *db-patterns
- <<: *if-merge-request
@ -1457,6 +1496,8 @@
.rails:rules:db:check-migrations-single-db-ci-connection:
rules:
- <<: *if-merge-request-labels-run-single-db
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request
changes: *db-patterns
- <<: *if-merge-request
@ -1464,6 +1505,8 @@
.rails:rules:db-backup:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-labels-run-all-rspec
- <<: *if-default-refs
changes: *db-backup-patterns
@ -1490,6 +1533,8 @@
rules:
- <<: *if-fork-merge-request
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request
changes: *core-backend-patterns
@ -1504,6 +1549,8 @@
.rails:rules:rspec-predictive:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-approved
when: never
- <<: *if-automated-merge-request
@ -1523,6 +1570,8 @@
.rails:rules:ee-and-foss-mr-with-migration:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request
changes: *db-patterns
- <<: *if-merge-request-labels-run-all-rspec
@ -1579,6 +1628,8 @@
rules:
- <<: *if-not-ee
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request
changes: *core-backend-patterns
@ -1635,6 +1686,8 @@
rules:
- <<: *if-not-ee
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request
changes: *core-backend-patterns
@ -1661,6 +1714,8 @@
when: never
- <<: *if-fork-merge-request
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- !reference [".rails:rules:ee-and-foss-default-rules", rules]
- <<: *if-merge-request-labels-as-if-foss
changes: *backend-patterns
@ -1671,6 +1726,8 @@
when: never
- <<: *if-fork-merge-request
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- !reference [".rails:rules:ee-and-foss-default-rules", rules]
- <<: *if-merge-request-labels-as-if-foss
changes: *backend-patterns
@ -1681,12 +1738,16 @@
when: never
- <<: *if-fork-merge-request
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- !reference [".rails:rules:system-default-rules", rules]
- <<: *if-merge-request-labels-as-if-foss
changes: *code-backstage-patterns
.rails:rules:ee-and-foss-db-library-code:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-default-refs
changes: *db-library-patterns
- <<: *if-merge-request-labels-run-all-rspec
@ -1696,6 +1757,8 @@
rules:
- <<: *if-not-ee
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-merge-request-labels-run-all-rspec
- <<: *if-merge-request
changes: *code-backstage-patterns
@ -1724,6 +1787,8 @@
when: never
- <<: *if-merge-request-labels-as-if-foss
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-security-merge-request
changes: *code-backstage-patterns
- <<: *if-dot-com-gitlab-org-merge-request
@ -1753,6 +1818,8 @@
rules:
- <<: *if-not-ee
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-default-branch-schedule-nightly
- <<: *if-ruby2-branch-schedule-nightly
- <<: *if-merge-request-labels-run-all-rspec
@ -1791,6 +1858,8 @@
.rails:rules:default-branch-schedule-nightly--code-backstage-default-rules:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-default-branch-schedule-nightly
- <<: *if-merge-request
changes: [".gitlab/ci/rails.gitlab-ci.yml"]
@ -1809,6 +1878,8 @@
rules:
- <<: *if-not-ee
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-default-refs
changes: *code-backstage-patterns
@ -2025,6 +2096,8 @@
#################
.reports:rules:code_quality:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: '$CODE_QUALITY_DISABLED'
when: never
# Run code_quality on master until https://gitlab.com/gitlab-org/gitlab/-/issues/363747 is resolved
@ -2036,6 +2109,8 @@
.reports:rules:brakeman-sast:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: $SAST_DISABLED
when: never
- if: $SAST_EXCLUDED_ANALYZERS =~ /brakeman/
@ -2047,6 +2122,8 @@
.reports:rules:semgrep-sast:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: $SAST_DISABLED
when: never
- if: $SAST_EXCLUDED_ANALYZERS =~ /semgrep/
@ -2064,6 +2141,8 @@
.reports:rules:secret_detection:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: '$SECRET_DETECTION_DISABLED'
when: never
# Scan each commit on master to feed the Vulnerability Reports with detected secrets
@ -2073,6 +2152,8 @@
.reports:rules:gemnasium-dependency_scanning:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /gemnasium([^-]|$)/'
when: never
# Run Dependency Scanning on master until https://gitlab.com/gitlab-org/gitlab/-/issues/361657 is resolved
@ -2082,6 +2163,8 @@
.reports:rules:gemnasium-python-dependency_scanning:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /gemnasium-python/'
when: never
# Run Dependency Scanning on master until https://gitlab.com/gitlab-org/gitlab/-/issues/361657 is resolved
@ -2091,6 +2174,8 @@
.reports:rules:yarn-audit-dependency_scanning:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/'
when: never
# Run Dependency Scanning on master until https://gitlab.com/gitlab-org/gitlab/-/issues/361657 is resolved
@ -2100,6 +2185,8 @@
.reports:rules:test-dast:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: '$DAST_DISABLED || $GITLAB_FEATURES !~ /\bdast\b/'
when: never
- <<: *if-merge-request
@ -2281,6 +2368,8 @@
.setup:rules:gitlab_git_test:
rules:
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-default-refs
changes: *code-backstage-patterns
@ -2311,6 +2400,8 @@
rules:
- <<: *if-not-ee
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-default-refs
changes: *code-backstage-patterns

View File

@ -1,8 +1,18 @@
<!-- AI Project Proposal title format: 🤖 [AI Proposal] {`Need/outcome` } + {`Beneficiary`} + {`Job/Small Job`}
<!--
HOW TO USE THIS TEMPLATE
To propose an AI experiment, focus on completing the “Experiment” section first. As you refine the idea and gather feedback on your experiment, use the “Feature release” section to define how it will evolve as a Beta or GA capability. It's important that we link experiment to feature release. Feel free to add sections, but keep the existing ones.
The title should be something that is easily understood that quickly communicates the intent of the project allowing team members to easily understand and recognize the expected work that will be done.
You can choose how to get started with this template. For example, the proposal can start as an issue, and then be promoted to an epic to house all the work related to the experiment/prototype and feature release. If you prefer to start with an epic, you have to manually apply the proposal template. Regardless, if the experiment is eventually prioritized for development, the template content will need to appear in a top-level epic so it can be tracked alongside other prioritized AI experiments.
A proposal title should combine the beneficiary of the feature/UI, the job it will allow them to accomplish, and their expected outcome when the work is delivered. Well-defined statements are concise without sacrificing the substance of the proposal so that anyone can understand it at a glance. (e.g.🤖 {Reduce the effort} + {for security teams} + {when prioritizing business-critical risks in their assets}) -->
TITLE FORMAT
🤖 [AI Proposal] {Need/outcome} {Beneficiary} {Job/Small Job}
The title should be something that is easily understood that quickly communicates the intent of the project allowing team members to easily understand and recognize the expected work that will be done. A proposal title should combine the beneficiary of the feature/UI, the job it will allow them to accomplish (see https://about.gitlab.com/handbook/product/ux/jobs-to-be-done/#how-to-write-a-jtbd), and their expected outcome when the work is delivered. Well-defined statements are concise without sacrificing the substance of the proposal so that anyone can understand it at a glance. (e.g. {Reduce the effort} {for security teams} {when prioritizing business-critical risks in their assets}).
-->
# Experiment
This section should be completed prior to work on the Experiment beginning.
# [Experiment](https://docs.gitlab.com/ee/policy/alpha-beta-support.html#experiment)
@ -21,19 +31,22 @@ _What assumptions are you making about this problem and the solution?_
_What [personas](https://about.gitlab.com/handbook/product/personas/#list-of-user-personas) have this problem, who is the intended user?_
## Proposal
<!-- Use this section to explain the proposed changes, including details around usage and business drivers. -->
<!-- Explain the proposed changes, including details around usage and business drivers. -->
### Success
_How will you measure whether this experiment is a success?_
# [General Availability](https://docs.gitlab.com/ee/policy/alpha-beta-support.html#generally-available-ga)
## Main Job story
# Feature release
<!-- DO NOT REMOVE THIS SECTION
Although the initial focus is on the “Experiment” section, do not remove this “Feature release” section. It's important that we link experiment to feature release. Fill this section as you progress.
-->
### Main Job story
_What job to be done will this solve?_
<!-- What is the [Main Job story](https://about.gitlab.com/handbook/product/ux/jobs-to-be-done/#how-to-write-a-jtbd) that this proposal was derived from? (e.g. When I am on triage rotation, I want to address all the business-critical risks in my assets, So I can minimize the likelihood of my organization being compromised by a security breach.) -->
### Proposal updates/additions
<!-- Use this section to explain any changes or updates to the original proposal, including details around usage, business drivers, and reasonings that drove the updates/additions. -->
## Proposal updates/additions
<!-- Explain any changes or updates to the original proposal from the experiment, including details around usage, business drivers, and reasonings that drove the updates/additions. -->
### Problem validation
_What validation exists that customers have this problem?_
@ -59,34 +72,33 @@ _What tasks or actions should the user be capable of performing with this featur
#### The user needs to be able to:
- ...
- ...
- ...
## Checklist
### Experiment
<details>
<summary> Issue information </summary>
<details> <summary> Issue information </summary>
- [ ] Add information to the issue body about:
- [ ] The user problem being solved
- [ ] Your assumptions
- [ ] Who it's for, list of personas impacted
- [ ] Your proposal
- [ ] The user problem being solved
- [ ] Your assumptions
- [ ] Who it's for, list of personas impacted
- [ ] Your proposal
- [ ] Add relevant designs to the Design Management area of the issue if available
- [ ] Confirm that an unexpected outage of this feature will not negatively impact the application or other features
- [ ] Add a feature flag so that this feature can be quickly disabled if/when needed
- [ ] If this experiment introduces a new service or data store, ensure it is not processing or storing [red data](https://about.gitlab.com/handbook/security/data-classification-standard.html#data-classification-levels) without a security and if needed legal review
- *NOTE*: We recommend using one of the already adopted models or data stores. If you need to use something else, be aware that using other models or data stores will require additional review during the feature stage for operational fitness and compliance.
- [ ] Ensure this issue has the ~wg-ai-integration label to ensure visibility to various teams working on this
</details>
### General Availability
<details>
<summary>Issue information</summary>
### Feature release
<details> <summary> Issue information </summary>
- [ ] Add information to the issue body about:
- [ ] Your proposal
- [ ] The Job Statement it's expected to satisfy
- [ ] Details about the user problem and provide any research or problem validation
- [ ] Your proposal
- [ ] The Job Statement it's expected to satisfy
- [ ] Details about the user problem and provide any research or problem validation
- [ ] List the personas impacted by the proposal.
- [ ] Add all relevant solution validation issues to the Linked items section that shows this proposal will solve the customer problem, or details explaining why it's not possible to provide that validation.
- [ ] Add relevant designs to the Design Management area of the issue.
@ -95,30 +107,27 @@ _What tasks or actions should the user be capable of performing with this featur
</details>
<details>
<summary>Technical needs</summary>
<details> <summary> Technical needs </summary>
- [ ] [Operational Requirements Review - Checklist - #note_1337519985](https://gitlab.com/gitlab-org/gitlab/-/issues/403859#note_1337519985)
- [ ] Please consider the operational aspects of the feature you are creating. A list of things to think about is in: https://gitlab.com/gitlab-org/gitlab/-/issues/403859. We will be improving this process in the future: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117637#note_1353253349.
1. **Work estimate and skills needs to build an ML viable feature:** To build any ML feature depending on the work, there are many personas that contribute including, Data Scientist, NLP engineer, ML Engineer, MLOps Engineer, ML Infra engineers, and Fullstack engineer to integrate the ML Services with Gitlab. Post-prototype we would assess the skills needed to build a production-grade ML feature for the prototype
2. **Data Limitation:** We would like to upfront validate if we have viable data for the feature including whether we can use the DataOps pipeline of ModelOps or create a custom one. We would want to understand the training data, test data, and feedback data to dial up the accuracy and the limitations of the data.
3. **Model Limitation:** We would want to understand if we can use an open-source pre-trained model, tune and customize it or start a model from scratch as well. Further, we would asses based on the ModelOps model evaluation framework which would be the right model to use based on the use case.
4. **Cost, Scalability, Reliability:** We would want to estimate the cost of hosting, serving, inference of the model, and the full end-to-end infrastructure including monitoring and observability.
5. **Legal and Ethical Framework:** We would want to align with legal and ethical framework like any other ModelOps features to cover across the nine principles of responsible ML and any legal support needed.
1. Work estimate and skills needs to build an ML viable feature: To build any ML feature depending on the work, there are many personas that contribute including, Data Scientist, NLP engineer, ML Engineer, MLOps Engineer, ML Infra engineers, and Fullstack engineer to integrate the ML Services with Gitlab. Post-prototype we would assess the skills needed to build a production-grade ML feature for the prototype.
2. Data Limitation: We would like to upfront validate if we have viable data for the feature including whether we can use the DataOps pipeline of ModelOps or create a custom one. We would want to understand the training data, test data, and feedback data to dial up the accuracy and the limitations of the data.
3. Model Limitation: We would want to understand if we can use an open-source pre-trained model, tune and customize it or start a model from scratch as well. Further, we would assess based on the ModelOps model evaluation framework which would be the right model to use based on the use case.
4. Cost, Scalability, Reliability: We would want to estimate the cost of hosting, serving, inference of the model, and the full end-to-end infrastructure including monitoring and observability.
5. Legal and Ethical Framework: We would want to align with legal and ethical framework like any other ModelOps features to cover across the nine principles of responsible ML and any legal support needed.
</details>
<details>
<summary>Dependency needs</summary>
<details> <summary> Dependency needs </summary>
- [ ] [Operational Requirements Review - Checklist - #note_1337519985](https://gitlab.com/gitlab-org/gitlab/-/issues/403859#note_1337519985)
- [ ] Please consider the operational aspects of the service you are creating. A list of things to think about is in: https://gitlab.com/gitlab-org/gitlab/-/issues/403859. We will be improving this process in the future: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117637#note_1353253349.
</details>
<details>
<summary>Legal needs</summary>
<details> <summary> Legal needs </summary>
- [ ] TBD
- [ ] TBD
</details>
@ -134,5 +143,3 @@ _What tasks or actions should the user be capable of performing with this featur
/label ~wg-ai-integration
/cc @tmccaslin @hbenson @wayne @pedroms @jmandell
/confidential
[Make change to this template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/AI%20Project%20Proposal.md)

View File

@ -103,6 +103,8 @@ InternalAffairs/DeprecateCopHelper:
Layout/LineLength:
AllowedPatterns: ['^RSpec\.describe\s.*\sdo']
Exclude:
- 'ee/spec/controllers/concerns/routable_actions_spec.rb'
Lint/LastKeywordArgument:
Safe: false

View File

@ -77,8 +77,13 @@ export default {
class="issuable-milestone gl-mr-3 gl-text-truncate gl-max-w-26 gl-display-inline-block gl-vertical-align-bottom"
data-testid="issuable-milestone"
>
<gl-link v-gl-tooltip :href="milestoneLink" :title="milestoneDate" class="gl-font-sm">
<gl-icon name="clock" />
<gl-link
v-gl-tooltip
:href="milestoneLink"
:title="milestoneDate"
class="gl-font-sm gl-text-gray-500!"
>
<gl-icon name="clock" :size="12" />
{{ issue.milestone.title }}
</gl-link>
</span>
@ -90,7 +95,7 @@ export default {
:title="__('Due date')"
data-testid="issuable-due-date"
>
<gl-icon name="calendar" />
<gl-icon name="calendar" :size="12" />
{{ dueDate }}
</span>
<span
@ -100,7 +105,7 @@ export default {
:title="__('Estimate')"
data-testid="time-estimate"
>
<gl-icon name="timer" />
<gl-icon name="timer" :size="12" />
{{ timeEstimate }}
</span>
<slot></slot>

View File

@ -1,5 +1,7 @@
<script>
import { mapState, mapActions } from 'vuex';
import { scrollToElement } from '~/lib/utils/common_utils';
import { getLocationHash } from '~/lib/utils/url_utility';
import CollapsibleLogSection from './collapsible_section.vue';
import LogLine from './line.vue';
@ -25,13 +27,25 @@ export default {
},
updated() {
this.$nextTick(() => {
this.handleScrollDown();
if (!window.location.hash) {
this.handleScrollDown();
}
});
},
mounted() {
this.$nextTick(() => {
this.handleScrollDown();
});
if (window.location.hash) {
const lineNumber = getLocationHash();
this.unwatchJobLog = this.$watch('jobLog', async () => {
if (this.jobLog.length) {
await this.$nextTick();
const el = document.getElementById(lineNumber);
scrollToElement(el);
this.unwatchJobLog();
}
});
}
},
methods: {
...mapActions(['toggleCollapsibleLine', 'scrollBottom']),

View File

@ -1,8 +1,9 @@
<script>
import { GlAlert, GlSkeletonLoader, GlIntersectionObserver, GlLoadingIcon } from '@gitlab/ui';
import { GlAlert, GlIntersectionObserver, GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
import { createAlert } from '~/alert';
import { setUrlParams, updateHistory, queryToObject } from '~/lib/utils/url_utility';
import JobsSkeletonLoader from '~/pages/admin/jobs/components/jobs_skeleton_loader.vue';
import JobsFilteredSearch from '../filtered_search/jobs_filtered_search.vue';
import { validateQueryString } from '../filtered_search/utils';
import GetJobs from './graphql/queries/get_jobs.query.graphql';
@ -22,13 +23,13 @@ export default {
'gl-my-0 gl-p-5 gl-bg-gray-10 gl-text-gray-900 gl-border-b gl-border-gray-100',
components: {
GlAlert,
GlSkeletonLoader,
JobsFilteredSearch,
JobsTable,
JobsTableEmptyState,
JobsTableTabs,
GlIntersectionObserver,
GlLoadingIcon,
JobsSkeletonLoader,
},
inject: {
fullPath: {
@ -217,22 +218,7 @@ export default {
/>
</div>
<div v-if="showSkeletonLoader" class="gl-mt-5">
<gl-skeleton-loader :width="1248" :height="73">
<circle cx="748.031" cy="37.7193" r="15.0307" />
<circle cx="787.241" cy="37.7193" r="15.0307" />
<circle cx="827.759" cy="37.7193" r="15.0307" />
<circle cx="866.969" cy="37.7193" r="15.0307" />
<circle cx="380" cy="37" r="18" />
<rect x="432" y="19" width="126.587" height="15" />
<rect x="432" y="41" width="247" height="15" />
<rect x="158" y="19" width="86.1" height="15" />
<rect x="158" y="41" width="168" height="15" />
<rect x="22" y="19" width="96" height="36" />
<rect x="924" y="30" width="96" height="15" />
<rect x="1057" y="20" width="166" height="35" />
</gl-skeleton-loader>
</div>
<jobs-skeleton-loader v-if="showSkeletonLoader" class="gl-mt-5" />
<jobs-table-empty-state v-else-if="showEmptyState" />

View File

@ -33,8 +33,7 @@ export default {
// the job log response will not have a defined
// html or size. We keep the old value otherwise these
// will be set to `null`
state.jobLog = log.lines ? logLinesParser(log.lines) : state.jobLog;
state.jobLog = log.lines ? logLinesParser(log.lines, [], window.location.hash) : state.jobLog;
state.jobLogSize = log.size || state.jobLogSize;
}

View File

@ -18,12 +18,26 @@ export const parseLine = (line = {}, lineNumber) => ({
* @param Object line
* @param Number lineNumber
*/
export const parseHeaderLine = (line = {}, lineNumber) => ({
isClosed: parseBoolean(line.section_options?.collapsed),
isHeader: true,
line: parseLine(line, lineNumber),
lines: [],
});
export const parseHeaderLine = (line = {}, lineNumber, hash) => {
// if a hash is present in the URL then we ensure
// all sections are visible so we can scroll to the hash
// in the DOM
if (hash) {
return {
isClosed: false,
isHeader: true,
line: parseLine(line, lineNumber),
lines: [],
};
}
return {
isClosed: parseBoolean(line.section_options?.collapsed),
isHeader: true,
line: parseLine(line, lineNumber),
lines: [],
};
};
/**
* Finds the matching header section
@ -104,7 +118,7 @@ export const getIncrementalLineNumber = (acc) => {
* @param Array accumulator
* @returns Array parsed log lines
*/
export const logLinesParser = (lines = [], accumulator = []) =>
export const logLinesParser = (lines = [], accumulator = [], hash = '') =>
lines.reduce(
(acc, line, index) => {
const lineNumber = accumulator.length > 0 ? getIncrementalLineNumber(acc) : index;
@ -113,7 +127,7 @@ export const logLinesParser = (lines = [], accumulator = []) =>
// If the object is an header, we parse it into another structure
if (line.section_header) {
acc.push(parseHeaderLine(line, lineNumber));
acc.push(parseHeaderLine(line, lineNumber, hash));
} else if (isCollapsibleSection(acc, last, line)) {
// if the object belongs to a nested section, we append it to the new `lines` array of the
// previously formatted header

View File

@ -0,0 +1,26 @@
<script>
import { GlSkeletonLoader } from '@gitlab/ui';
export default {
components: {
GlSkeletonLoader,
},
};
</script>
<template>
<gl-skeleton-loader :width="1248" :height="73">
<circle cx="748.031" cy="37.7193" r="15.0307" />
<circle cx="787.241" cy="37.7193" r="15.0307" />
<circle cx="827.759" cy="37.7193" r="15.0307" />
<circle cx="866.969" cy="37.7193" r="15.0307" />
<circle cx="380" cy="37" r="18" />
<rect x="432" y="19" width="126.587" height="15" />
<rect x="432" y="41" width="247" height="15" />
<rect x="158" y="19" width="86.1" height="15" />
<rect x="158" y="41" width="168" height="15" />
<rect x="22" y="19" width="96" height="36" />
<rect x="924" y="30" width="96" height="15" />
<rect x="1057" y="20" width="166" height="35" />
</gl-skeleton-loader>
</template>

View File

@ -7,6 +7,7 @@ import JobsTable from '~/jobs/components/table/jobs_table.vue';
import JobsTableTabs from '~/jobs/components/table/jobs_table_tabs.vue';
import JobsTableEmptyState from '~/jobs/components/table/jobs_table_empty_state.vue';
import { DEFAULT_FIELDS_ADMIN } from '../constants';
import JobsSkeletonLoader from '../jobs_skeleton_loader.vue';
import GetAllJobs from './graphql/queries/get_all_jobs.query.graphql';
export default {
@ -14,6 +15,7 @@ export default {
jobsFetchErrorMsg: __('There was an error fetching the jobs.'),
},
components: {
JobsSkeletonLoader,
JobsTableEmptyState,
GlAlert,
JobsTable,
@ -85,6 +87,12 @@ export default {
jobsCount() {
return this.jobs.count;
},
showLoadingSpinner() {
return this.loading && this.infiniteScrollingTriggered;
},
showSkeletonLoader() {
return this.loading && !this.showLoadingSpinner;
},
},
watch: {
// this watcher ensures that the count on the all tab
@ -106,7 +114,9 @@ export default {
<jobs-table-tabs :all-jobs-count="count" :loading="loading" />
<jobs-table-empty-state v-if="showEmptyState" />
<jobs-skeleton-loader v-if="showSkeletonLoader" class="gl-mt-5" />
<jobs-table-empty-state v-else-if="showEmptyState" />
<jobs-table
v-else

View File

@ -1,20 +1,13 @@
<script>
import { GlIcon, GlButton, GlTooltipDirective } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { s__ } from '~/locale';
import AccessorUtilities from '~/lib/utils/accessor';
import { getTopFrequentItems, formatContextSwitcherItems } from '../utils';
import ItemsList from './items_list.vue';
export default {
components: {
GlIcon,
GlButton,
ItemsList,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
title: {
type: String,
@ -36,16 +29,12 @@ export default {
data() {
return {
cachedFrequentItems: [],
isItemsListEditable: false,
};
},
computed: {
isEmpty() {
return !this.cachedFrequentItems.length;
},
allowItemsEditing() {
return !this.isEmpty && AccessorUtilities.canUseLocalStorage();
},
},
created() {
this.getItemsFromLocalStorage();
@ -63,9 +52,6 @@ export default {
Sentry.captureException(e);
}
},
toggleItemsListEditablity() {
this.isItemsListEditable = !this.isItemsListEditable;
},
handleItemRemove(item) {
try {
// Remove item from local storage
@ -82,9 +68,6 @@ export default {
}
},
},
i18n: {
frequentItemsEditToggle: s__('Navigation|Toggle edit mode'),
},
};
</script>
@ -96,29 +79,11 @@ export default {
class="gl-display-flex gl-align-items-center gl-text-transform-uppercase gl-text-secondary gl-font-weight-bold gl-font-xs gl-line-height-12 gl-letter-spacing-06em gl-my-3"
>
<span class="gl-flex-grow-1">{{ title }}</span>
<gl-button
v-if="allowItemsEditing"
v-gl-tooltip.left
size="small"
category="tertiary"
:aria-label="$options.i18n.frequentItemsEditToggle"
:title="$options.i18n.frequentItemsEditToggle"
:class="{ 'gl-bg-gray-100!': isItemsListEditable }"
class="gl-p-2!"
@click="toggleItemsListEditablity"
>
<gl-icon name="pencil" :class="{ 'gl-text-gray-900!': isItemsListEditable }" />
</gl-button>
</div>
<div v-if="isEmpty" data-testid="empty-text" class="gl-text-gray-500 gl-font-sm gl-my-3">
{{ pristineText }}
</div>
<items-list
:aria-label="title"
:items="cachedFrequentItems"
:editable="isItemsListEditable"
@remove-item="handleItemRemove"
>
<items-list :aria-label="title" :items="cachedFrequentItems" @remove-item="handleItemRemove">
<template #view-all-items>
<slot name="view-all-items"></slot>
</template>

View File

@ -1,5 +1,11 @@
<script>
import { GlBadge, GlButton, GlDisclosureDropdown, GlDisclosureDropdownGroup } from '@gitlab/ui';
import {
GlBadge,
GlButton,
GlIcon,
GlDisclosureDropdown,
GlDisclosureDropdownGroup,
} from '@gitlab/ui';
import GitlabVersionCheckBadge from '~/gitlab_version_check/components/gitlab_version_check_badge.vue';
import { helpPagePath } from '~/helpers/help_page_helper';
import { PROMO_URL } from 'jh_else_ce/lib/utils/url_utility';
@ -15,6 +21,7 @@ export default {
components: {
GlBadge,
GlButton,
GlIcon,
GlDisclosureDropdown,
GlDisclosureDropdownGroup,
GitlabVersionCheckBadge,
@ -31,6 +38,7 @@ export default {
shortcuts: __('Keyboard shortcuts'),
version: __('Your GitLab version'),
whatsnew: __("What's new"),
tanuki: __('Ask the Tanuki Bot'),
},
props: {
sidebarData: {
@ -60,6 +68,14 @@ export default {
},
helpLinks: {
items: [
this.sidebarData.show_tanuki_bot && {
icon: 'tanuki',
text: this.$options.i18n.tanuki,
action: this.showTanukiBotChat,
extraAttrs: {
...this.trackingAttrs('tanuki_bot_help_dropdown'),
},
},
{
text: this.$options.i18n.help,
href: helpPagePath(),
@ -109,7 +125,7 @@ export default {
...this.trackingAttrs('submit_feedback'),
},
},
],
].filter(Boolean),
},
helpActions: {
items: [
@ -159,6 +175,11 @@ export default {
this.$refs.dropdown.close();
},
async showTanukiBotChat() {
// This will be implemented in the following MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117930
return true;
},
async showWhatsNew() {
this.$refs.dropdown.close();
this.showWhatsNewNotification = false;
@ -236,7 +257,14 @@ export default {
<gl-disclosure-dropdown-group
:group="itemGroups.helpLinks"
:bordered="sidebarData.show_version_check"
/>
>
<template #list-item="{ item }">
<span class="gl-display-flex gl-justify-content-space-between gl-align-items-center">
{{ item.text }}
<gl-icon v-if="item.icon" :name="item.icon" class="gl-text-orange-500" />
</span>
</template>
</gl-disclosure-dropdown-group>
<gl-disclosure-dropdown-group :group="itemGroups.helpActions" bordered>
<template #list-item="{ item }">

View File

@ -1,11 +1,10 @@
<script>
import { GlIcon, GlButton, GlTooltipDirective } from '@gitlab/ui';
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
import NavItem from './nav_item.vue';
export default {
components: {
GlIcon,
GlButton,
ProjectAvatar,
NavItem,
@ -19,11 +18,6 @@ export default {
required: false,
default: () => [],
},
editable: {
type: Boolean,
required: false,
default: false,
},
},
};
</script>
@ -47,18 +41,16 @@ export default {
</template>
<template #actions>
<gl-button
v-if="editable"
v-gl-tooltip.left
v-gl-tooltip.right.viewport
size="small"
category="tertiary"
icon="dash"
:aria-label="__('Remove')"
:title="__('Remove')"
class="gl-align-self-center gl-p-1! gl-absolute gl-right-4"
data-testid="item-remove"
@click.stop.prevent="$emit('remove-item', item)"
>
<gl-icon name="close" />
</gl-button>
/>
</template>
</nav-item>
<slot name="view-all-items"></slot>

View File

@ -203,6 +203,11 @@ export default {
</gl-form-checkbox>
<div class="issuable-main-info">
<div data-testid="issuable-title" class="issue-title title">
<work-item-type-icon
v-if="showWorkItemTypeIcon"
:work-item-type="issuable.type"
show-tooltip-on-hover
/>
<gl-icon
v-if="issuable.confidential"
v-gl-tooltip
@ -236,11 +241,6 @@ export default {
</span>
</div>
<div class="issuable-info">
<work-item-type-icon
v-if="showWorkItemTypeIcon"
:work-item-type="issuable.type"
show-tooltip-on-hover
/>
<slot v-if="hasSlotContents('reference')" name="reference"></slot>
<span v-else data-testid="issuable-reference" class="issuable-reference">
{{ reference }}
@ -268,7 +268,7 @@ export default {
:data-avatar-url="author.avatarUrl"
:href="author.webUrl"
data-testid="issuable-author"
class="author-link js-user-link gl-font-sm"
class="author-link js-user-link gl-font-sm gl-text-gray-500!"
>
<span class="author">{{ author.name }}</span>
</gl-link>

View File

@ -12,6 +12,8 @@
- if display_whats_new?
#whats-new-app{ data: { version_digest: whats_new_version_digest } }
= render_if_exists "layouts/tanuki_bot_chat"
- elsif defined?(nav) && nav
= render "layouts/nav/sidebar/#{nav}"
.content-wrapper{ class: "#{@content_wrapper_class}" }

View File

@ -15,27 +15,27 @@
= hidden_merge_request_icon(merge_request)
= link_to merge_request.title, merge_request_path(merge_request), class: 'js-prefetch-document'
- if merge_request.tasks?
%span.task-status.d-none.d-sm-inline-block
%span.task-status.d-none.d-sm-inline-block.gl-font-sm
&nbsp;
= merge_request.task_status
.issuable-info
%span.issuable-reference
#{issuable_reference(merge_request)}
%span.issuable-authored.d-none.d-sm-inline-block
%span.issuable-authored.d-none.d-sm-inline-block.gl-text-gray-500!
&middot;
#{s_('IssueList|created %{timeAgoString} by %{user}').html_safe % { timeAgoString: time_ago_with_tooltip(merge_request.created_at, placement: 'bottom'), user: link_to_member(@project, merge_request.author, avatar: false) }}
#{s_('IssueList|created %{timeAgoString} by %{user}').html_safe % { timeAgoString: time_ago_with_tooltip(merge_request.created_at, placement: 'bottom'), user: link_to_member(@project, merge_request.author, avatar: false, extra_class: 'gl-text-gray-500!') }}
= render_if_exists 'shared/issuable/gitlab_team_member_badge', author: merge_request.author
- if merge_request.milestone
%span.issuable-milestone.d-none.d-sm-inline-block.gl-text-truncate.gl-max-w-26.gl-vertical-align-bottom
&nbsp;
= link_to project_merge_requests_path(merge_request.project, milestone_title: merge_request.milestone.title), data: { html: 'true', toggle: 'tooltip', title: milestone_tooltip_due_date(merge_request.milestone) } do
= sprite_icon('clock', css_class: 'gl-vertical-align-text-bottom')
= link_to project_merge_requests_path(merge_request.project, milestone_title: merge_request.milestone.title), class: 'gl-text-gray-500!', data: { html: 'true', toggle: 'tooltip', title: milestone_tooltip_due_date(merge_request.milestone) } do
= sprite_icon('clock', size: 12, css_class: 'gl-vertical-align-text-bottom')
= merge_request.milestone.title
- if merge_request.target_project.default_branch != merge_request.target_branch
%span.project-ref-path.has-tooltip.d-inline-block.gl-text-truncate.gl-max-w-26.gl-vertical-align-bottom{ title: _('Target branch: %{target_branch}') % {target_branch: merge_request.target_branch} }
&nbsp;
= link_to project_ref_path(merge_request.project, merge_request.target_branch), class: 'ref-name' do
= link_to project_ref_path(merge_request.project, merge_request.target_branch), class: 'ref-name gl-text-gray-500!' do
= sprite_icon('branch', size: 12, css_class: 'fork-sprite')
= merge_request.target_branch
- if merge_request.labels.any?

View File

@ -1,4 +1,5 @@
- page_title s_('WorkItem|Work Items')
- page_title "##{request.params['work_items_path']}"
- add_to_breadcrumbs _("Issues"), project_issues_path(@project)
- add_page_specific_style 'page_bundles/work_items'
- @gfm_form = true
- @noteable_type = 'WorkItem'

View File

@ -51,7 +51,7 @@ glab mr merge
- [`glab completion`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/completion)
- [`glab config`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/config)
- [`glab incident`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/incident)
- [`glab issue`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/ci)
- [`glab issue`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/issue)
- [`glab label`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/label)
- [`glab mr`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/mr)
- [`glab release`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/release)

View File

@ -6,16 +6,15 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# GitLab for Jira Cloud app **(FREE)**
With the [GitLab for Jira Cloud](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud) app,
you can integrate GitLab and Jira Cloud.
With the [GitLab for Jira Cloud](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud) app, you can connect GitLab and Jira Cloud and use the Jira development panel.
If you use GitLab.com and Jira Cloud, you can install the GitLab for Jira Cloud app.
If you do not use both of these environments, use the [Jira DVCS connector](dvcs/index.md) instead
or [install the GitLab for Jira Cloud app manually](#install-the-gitlab-for-jira-cloud-app-manually).
You should use the GitLab for Jira Cloud app because data is synchronized
in real time. The Jira DVCS connector updates data only once per hour.
- **For GitLab.com**:
- [Install the GitLab for Jira Cloud app](#install-the-gitlab-for-jira-cloud-app).
- **For self-managed GitLab**, do one of the following:
- [Connect the GitLab for Jira Cloud app for self-managed instances](#connect-the-gitlab-for-jira-cloud-app-for-self-managed-instances) (GitLab 15.7 and later).
- [Install the GitLab for Jira Cloud app manually](#install-the-gitlab-for-jira-cloud-app-manually).
This integration method supports [Smart Commits](dvcs/index.md#smart-commits).
If you use Jira Server or Jira Data Center, use the [Jira DVCS connector](dvcs/index.md) instead.
## Install the GitLab for Jira Cloud app **(FREE SAAS)**

View File

@ -9,10 +9,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
WARNING:
The Jira DVCS connector for Jira Cloud was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/362168) in GitLab 15.1
and is planned for removal in 16.0. Use the [GitLab for Jira Cloud app](../connect-app.md) instead.
The Jira DVCS connector was also deprecated for Jira 8.13 and earlier. You can only use the Jira DVCS connector with Jira Server or Jira Data Center in Jira 8.14 and later. Upgrade your Jira instance to Jira 8.14 or later, and reconfigure the Jira integration in your GitLab instance.
Use the Jira DVCS (distributed version control system) connector if you self-host
your Jira instance and want to sync information between GitLab and Jira.
If you're using the Jira DVCS connector with Jira Cloud, [migrate to the GitLab for Jira Cloud app](#migrate-to-the-gitlab-for-jira-cloud-app).
your Jira instance with Jira Server or Jira Data Center and want to sync information between GitLab and Jira.
If you're on Jira Cloud, [migrate to the GitLab for Jira Cloud app](#migrate-to-the-gitlab-for-jira-cloud-app).
When you configure the Jira DVCS connector, make sure your GitLab and Jira instances
are accessible.

View File

@ -6,7 +6,7 @@ module Gitlab
class DatabaseEventsSnowplow < Snowplow
extend ::Gitlab::Utils::Override
HOSTNAME = 'localhost:9091'
HOSTNAME = 'db-snowplow.trx.gitlab.net'
override :enabled?
# database events are only collected for SaaS instance
@ -16,7 +16,9 @@ module Gitlab
override :hostname
def hostname
HOSTNAME
return HOSTNAME if ::Gitlab.com?
'localhost:9091'
end
private

View File

@ -5874,6 +5874,9 @@ msgstr ""
msgid "Ask someone with write access to resolve it."
msgstr ""
msgid "Ask the Tanuki Bot"
msgstr ""
msgid "Ask your group owner to set up a group runner."
msgstr ""
@ -28888,9 +28891,6 @@ msgstr ""
msgid "Navigation|There was an error fetching search results."
msgstr ""
msgid "Navigation|Toggle edit mode"
msgstr ""
msgid "Navigation|Unpin item"
msgstr ""
@ -50341,9 +50341,6 @@ msgstr ""
msgid "WorkItem|View current version"
msgstr ""
msgid "WorkItem|Work Items"
msgstr ""
msgid "WorkItem|Work item"
msgstr ""

View File

@ -113,7 +113,7 @@ RSpec.describe MetricsDashboard do
it 'includes project_blob_path only for project dashboards' do
expect(system_dashboard['project_blob_path']).to be_nil
expect(project_dashboard['project_blob_path']).to eq("/#{project.namespace.path}/#{project.name}/-/blob/master/.gitlab/dashboards/test.yml")
expect(project_dashboard['project_blob_path']).to eq("/#{project.namespace.path}/#{project.path}/-/blob/master/.gitlab/dashboards/test.yml")
end
it 'allows editing only for project dashboards' do

View File

@ -171,7 +171,7 @@ RSpec.describe Projects::ClustersController, feature_category: :deployment_manag
{
id: cluster.id,
namespace_id: project.namespace.full_path,
project_id: project.name
project_id: project.path
}
end
end

View File

@ -46,7 +46,7 @@ RSpec.describe Projects::Environments::SampleMetricsController do
{
id: environment.id.to_s,
namespace_id: project.namespace.full_path,
project_id: project.name,
project_id: project.path,
identifier: 'sample_metric_query_result',
start: '2019-12-02T23:31:45.000Z',
end: '2019-12-03T00:01:45.000Z'

View File

@ -23,7 +23,7 @@ RSpec.describe Projects::GrafanaApiController, feature_category: :metrics do
let(:params) do
{
namespace_id: project.namespace.full_path,
project_id: project.name,
project_id: project.path,
proxy_path: 'api/v1/query_range',
datasource_id: '1',
query: 'rate(relevant_metric)',

View File

@ -6,7 +6,7 @@ RSpec.describe Projects::PerformanceMonitoring::DashboardsController do
let_it_be(:user) { create(:user) }
let_it_be(:namespace) { create(:namespace) }
let!(:project) { create(:project, :repository, name: 'dashboard-project', namespace: namespace) }
let_it_be(:project) { create(:project, :repository, namespace: namespace) }
let(:repository) { project.repository }
let(:branch) { double(name: branch_name) }
let(:commit_message) { 'test' }
@ -120,7 +120,7 @@ RSpec.describe Projects::PerformanceMonitoring::DashboardsController do
end
context 'project without repository feature' do
let!(:project) { create(:project, name: 'dashboard-project', namespace: namespace) }
let_it_be(:project) { create(:project, namespace: namespace) }
it 'responds with :not_found status code' do
post :create, params: params
@ -246,7 +246,7 @@ RSpec.describe Projects::PerformanceMonitoring::DashboardsController do
end
context 'project without repository feature' do
let!(:project) { create(:project, name: 'dashboard-project', namespace: namespace) }
let_it_be(:project) { create(:project, namespace: namespace) }
it 'responds with :not_found status code' do
put :update, params: params

View File

@ -157,7 +157,7 @@ RSpec.describe 'Admin updates settings', feature_category: :shared do
expect(user_internal_regex['placeholder']).to eq 'Regex pattern'
end
context 'Dormant users' do
context 'Dormant users', feature_category: :user_management do
context 'when Gitlab.com' do
let(:dot_com?) { true }

View File

@ -37,7 +37,7 @@ RSpec.describe 'Group Packages', feature_category: :package_registry do
end
context 'when there are packages' do
let_it_be(:second_project) { create(:project, name: 'second-project', group: group) }
let_it_be(:second_project) { create(:project, group: group) }
let_it_be(:npm_package) { create(:npm_package, project: project, name: 'zzz', created_at: 1.day.ago, version: '1.0.0') }
let_it_be(:maven_package) { create(:maven_package, project: second_project, name: 'aaa', created_at: 2.days.ago, version: '2.0.0') }
let_it_be(:packages) { [npm_package, maven_package] }
@ -50,10 +50,10 @@ RSpec.describe 'Group Packages', feature_category: :package_registry do
it_behaves_like 'package details link'
it 'allows you to navigate to the project page' do
find('[data-testid="root-link"]', text: project.name).click
find('[data-testid="root-link"]', text: project.path).click
expect(page).to have_current_path(project_path(project))
expect(page).to have_content(project.name)
expect(page).to have_content(project.path)
end
context 'sorting' do

View File

@ -195,10 +195,11 @@ RSpec.describe 'File blob', :js, feature_category: :projects do
end
end
it 'successfully changes ref when the ref name matches the project name' do
project.repository.create_branch(project.name)
# Regression test for https://gitlab.com/gitlab-org/gitlab/-/issues/330947
it 'successfully changes ref when the ref name matches the project path' do
project.repository.create_branch(project.path)
visit_blob('files/js/application.js', ref: project.name)
visit_blob('files/js/application.js', ref: project.path)
switch_ref_to('master')
aggregate_failures do

View File

@ -67,7 +67,7 @@ RSpec.describe 'Projects > Settings > User renames a project', feature_category:
end
context 'when changing project path' do
let(:project) { create(:project, :repository, namespace: user.namespace, name: 'gitlabhq') }
let(:project) { create(:project, :repository, namespace: user.namespace, path: 'gitlabhq') }
before(:context) do
TestEnv.clean_test_path

View File

@ -20,9 +20,15 @@ RSpec.describe 'Work item', :js, feature_category: :team_planning do
visit work_items_path
end
it 'shows project issues link in breadcrumbs' do
within('[data-testid="breadcrumb-links"]') do
expect(page).to have_link('Issues', href: project_issues_path(project))
end
end
it 'uses IID path in breadcrumbs' do
within('[data-testid="breadcrumb-current-link"]') do
expect(page).to have_link('Work Items', href: work_items_path)
expect(page).to have_link("##{work_item.iid}", href: work_items_path)
end
end

View File

@ -1,11 +1,18 @@
import { mount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import waitForPromises from 'helpers/wait_for_promises';
import { scrollToElement } from '~/lib/utils/common_utils';
import Log from '~/jobs/components/log/log.vue';
import LogLineHeader from '~/jobs/components/log/line_header.vue';
import { logLinesParser } from '~/jobs/store/utils';
import { jobLog } from './mock_data';
jest.mock('~/lib/utils/common_utils', () => ({
...jest.requireActual('~/lib/utils/common_utils'),
scrollToElement: jest.fn(),
}));
describe('Job Log', () => {
let wrapper;
let actions;
@ -36,13 +43,15 @@ describe('Job Log', () => {
actions,
state,
});
createComponent();
});
const findCollapsibleLine = () => wrapper.findComponent(LogLineHeader);
describe('line numbers', () => {
beforeEach(() => {
createComponent();
});
it('renders a line number for each open line', () => {
expect(wrapper.find('#L1').text()).toBe('1');
expect(wrapper.find('#L2').text()).toBe('2');
@ -55,6 +64,10 @@ describe('Job Log', () => {
});
describe('collapsible sections', () => {
beforeEach(() => {
createComponent();
});
it('renders a clickable header section', () => {
expect(findCollapsibleLine().attributes('role')).toBe('button');
});
@ -73,4 +86,49 @@ describe('Job Log', () => {
});
});
});
describe('anchor scrolling', () => {
afterEach(() => {
window.location.hash = '';
});
describe('when hash is not present', () => {
it('does not scroll to line number', async () => {
createComponent();
await waitForPromises();
expect(wrapper.find('#L6').exists()).toBe(false);
expect(scrollToElement).not.toHaveBeenCalled();
});
});
describe('when hash is present', () => {
beforeEach(() => {
window.location.hash = '#L6';
});
it('scrolls to line number', async () => {
createComponent();
state.jobLog = logLinesParser(jobLog, [], '#L6');
await waitForPromises();
expect(scrollToElement).toHaveBeenCalledTimes(1);
state.jobLog = logLinesParser(jobLog, [], '#L7');
await waitForPromises();
expect(scrollToElement).toHaveBeenCalledTimes(1);
});
it('line number within collapsed section is visible', () => {
state.jobLog = logLinesParser(jobLog, [], '#L6');
createComponent();
expect(wrapper.find('#L6').exists()).toBe(true);
});
});
});
});

View File

@ -22,6 +22,30 @@ export const jobLog = [
content: [{ text: 'Starting service postgres:9.6.14 ...', style: 'text-green' }],
section: 'prepare-executor',
},
{
offset: 1004,
content: [
{
text: 'Restore cache',
style: 'term-fg-l-cyan term-bold',
},
],
section: 'restore-cache',
section_header: true,
section_options: {
collapsed: 'true',
},
},
{
offset: 1005,
content: [
{
text: 'Checking cache for ruby-gems-debian-bullseye-ruby-3.0-16...',
style: 'term-fg-l-green term-bold',
},
],
section: 'restore-cache',
},
];
export const utilsMockData = [

View File

@ -1,10 +1,4 @@
import {
GlSkeletonLoader,
GlAlert,
GlEmptyState,
GlIntersectionObserver,
GlLoadingIcon,
} from '@gitlab/ui';
import { GlAlert, GlEmptyState, GlIntersectionObserver, GlLoadingIcon } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
@ -19,6 +13,7 @@ import JobsTable from '~/jobs/components/table/jobs_table.vue';
import JobsTableApp from '~/jobs/components/table/jobs_table_app.vue';
import JobsTableTabs from '~/jobs/components/table/jobs_table_tabs.vue';
import JobsFilteredSearch from '~/jobs/components/filtered_search/jobs_filtered_search.vue';
import JobsSkeletonLoader from '~/pages/admin/jobs/components/jobs_skeleton_loader.vue';
import * as urlUtils from '~/lib/utils/url_utility';
import {
mockJobsResponsePaginated,
@ -41,7 +36,7 @@ describe('Job table app', () => {
const countSuccessHandler = jest.fn().mockResolvedValue(mockJobsCountResponse);
const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
const findSkeletonLoader = () => wrapper.findComponent(JobsSkeletonLoader);
const findLoadingSpinner = () => wrapper.findComponent(GlLoadingIcon);
const findTable = () => wrapper.findComponent(JobsTable);
const findTabs = () => wrapper.findComponent(JobsTableTabs);

View File

@ -43,6 +43,14 @@ describe('Jobs Store Utils', () => {
expect(parsedHeaderLine.isClosed).toBe(true);
});
it('expands all pre-closed sections if hash is present', () => {
const headerLine = { content: [{ text: 'foo' }], section_options: { collapsed: 'true' } };
const parsedHeaderLine = parseHeaderLine(headerLine, 2, '#L33');
expect(parsedHeaderLine.isClosed).toBe(false);
});
});
describe('parseLine', () => {

View File

@ -0,0 +1,28 @@
import { GlSkeletonLoader } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import JobsSkeletonLoader from '~/pages/admin/jobs/components/jobs_skeleton_loader.vue';
describe('jobs_skeleton_loader.vue', () => {
let wrapper;
const findGlSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
const WIDTH = '1248';
const HEIGHT = '73';
beforeEach(() => {
wrapper = shallowMount(JobsSkeletonLoader);
});
it('renders a GlSkeletonLoader', () => {
expect(findGlSkeletonLoader().exists()).toBe(true);
});
it('has correct width', () => {
expect(findGlSkeletonLoader().attributes('width')).toBe(WIDTH);
});
it('has correct height', () => {
expect(findGlSkeletonLoader().attributes('height')).toBe(HEIGHT);
});
});

View File

@ -1,4 +1,4 @@
import { GlSkeletonLoader, GlLoadingIcon, GlEmptyState, GlAlert } from '@gitlab/ui';
import { GlLoadingIcon, GlEmptyState, GlAlert } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
@ -7,6 +7,8 @@ import waitForPromises from 'helpers/wait_for_promises';
import JobsTable from '~/jobs/components/table/jobs_table.vue';
import getJobsQuery from '~/pages/admin/jobs/components/table/graphql/queries/get_all_jobs.query.graphql';
import AdminJobsTableApp from '~/pages/admin/jobs/components/table/admin_jobs_table_app.vue';
import JobsTableTabs from '~/jobs/components/table/jobs_table_tabs.vue';
import JobsSkeletonLoader from '~/pages/admin/jobs/components/jobs_skeleton_loader.vue';
import {
mockAllJobsResponsePaginated,
@ -23,11 +25,12 @@ describe('Job table app', () => {
const emptyHandler = jest.fn().mockResolvedValue(mockJobsResponseEmpty);
const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error'));
const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
const findSkeletonLoader = () => wrapper.findComponent(JobsSkeletonLoader);
const findLoadingSpinner = () => wrapper.findComponent(GlLoadingIcon);
const findTable = () => wrapper.findComponent(JobsTable);
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findAlert = () => wrapper.findComponent(GlAlert);
const findTabs = () => wrapper.findComponent(JobsTableTabs);
const createMockApolloProvider = (handler) => {
const requestHandlers = [[getJobsQuery, handler]];
@ -53,6 +56,25 @@ describe('Job table app', () => {
});
};
describe('loading state', () => {
it('should display skeleton loader when loading', () => {
createComponent();
expect(findSkeletonLoader().exists()).toBe(true);
expect(findTable().exists()).toBe(false);
expect(findLoadingSpinner().exists()).toBe(false);
});
it('when switching tabs only the skeleton loader should show', () => {
createComponent();
findTabs().vm.$emit('fetchJobsByStatus', null);
expect(findSkeletonLoader().exists()).toBe(true);
expect(findLoadingSpinner().exists()).toBe(false);
});
});
describe('loaded state', () => {
beforeEach(async () => {
createComponent();

View File

@ -1,5 +1,6 @@
import { GlModal, GlFormCheckbox } from '@gitlab/ui';
import { nextTick } from 'vue';
import { createWrapper } from '@vue/test-utils';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { useFakeDate } from 'helpers/fake_date';
import { initEmojiMock, clearEmojiMock } from 'helpers/emoji';
@ -10,13 +11,17 @@ import stubChildren from 'helpers/stub_children';
import SetStatusModalWrapper from '~/set_status_modal/set_status_modal_wrapper.vue';
import { AVAILABILITY_STATUS } from '~/set_status_modal/constants';
import SetStatusForm from '~/set_status_modal/set_status_form.vue';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import { BV_HIDE_MODAL } from '~/lib/utils/constants';
jest.mock('~/alert');
describe('SetStatusModalWrapper', () => {
let wrapper;
const mockToastShow = jest.fn();
const $toast = {
show: jest.fn(),
show: mockToastShow,
};
const defaultEmoji = 'speech_balloon';
@ -58,18 +63,7 @@ describe('SetStatusModalWrapper', () => {
const findClearStatusButton = () => wrapper.find('.js-clear-user-status-button');
const findAvailabilityCheckbox = () => wrapper.findComponent(GlFormCheckbox);
const getEmojiPicker = () => wrapper.findComponent(EmojiPickerStub);
const initModal = async ({ mockOnUpdateSuccess = true, mockOnUpdateFailure = true } = {}) => {
const modal = findModal();
// mock internal emoji methods
wrapper.vm.showEmojiMenu = jest.fn();
wrapper.vm.hideEmojiMenu = jest.fn();
if (mockOnUpdateSuccess) wrapper.vm.onUpdateSuccess = jest.fn();
if (mockOnUpdateFailure) wrapper.vm.onUpdateFail = jest.fn();
modal.vm.$emit('shown');
await nextTick();
};
const initModal = () => findModal().vm.$emit('shown');
afterEach(() => {
clearEmojiMock();
@ -148,6 +142,8 @@ describe('SetStatusModalWrapper', () => {
describe('update status', () => {
describe('succeeds', () => {
useMockLocationHelper();
beforeEach(async () => {
await initEmojiMock();
wrapper = createComponent();
@ -194,11 +190,21 @@ describe('SetStatusModalWrapper', () => {
});
});
it('calls the "onUpdateSuccess" handler', async () => {
it('displays a toast message and reloads window', async () => {
findModal().vm.$emit('primary');
await nextTick();
expect(wrapper.vm.onUpdateSuccess).toHaveBeenCalled();
expect(mockToastShow).toHaveBeenCalledWith('Status updated');
expect(window.location.reload).toHaveBeenCalled();
});
it('closes modal', async () => {
const rootWrapper = createWrapper(wrapper.vm.$root);
findModal().vm.$emit('primary');
await nextTick();
expect(rootWrapper.emitted(BV_HIDE_MODAL)).toEqual([['set-user-status-modal']]);
});
});
@ -227,11 +233,22 @@ describe('SetStatusModalWrapper', () => {
jest.spyOn(UserApi, 'updateUserStatus').mockRejectedValue();
});
it('calls the "onUpdateFail" handler', async () => {
it('displays an error alert', async () => {
findModal().vm.$emit('primary');
await nextTick();
expect(wrapper.vm.onUpdateFail).toHaveBeenCalled();
expect(createAlert).toHaveBeenCalledWith({
message: "Sorry, we weren't able to set your status. Please try again later.",
});
});
it('closes modal', async () => {
const rootWrapper = createWrapper(wrapper.vm.$root);
findModal().vm.$emit('primary');
await nextTick();
expect(rootWrapper.emitted(BV_HIDE_MODAL)).toEqual([['set-user-status-modal']]);
});
});

View File

@ -1,4 +1,3 @@
import { GlIcon, GlButton } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { s__ } from '~/locale';
import FrequentItemsList from '~/super_sidebar/components//frequent_items_list.vue';
@ -17,7 +16,6 @@ describe('FrequentItemsList component', () => {
let wrapper;
const findListTitle = () => wrapper.findByTestId('list-title');
const findListEditButton = () => findListTitle().findComponent(GlButton);
const findItemsList = () => wrapper.findComponent(ItemsList);
const findEmptyText = () => wrapper.findByTestId('empty-text');
@ -68,30 +66,8 @@ describe('FrequentItemsList component', () => {
});
describe('items editing', () => {
it('renders edit button within header', () => {
const itemsEditButton = findListEditButton();
expect(itemsEditButton.exists()).toBe(true);
expect(itemsEditButton.attributes('title')).toBe('Toggle edit mode');
expect(itemsEditButton.findComponent(GlIcon).props('name')).toBe('pencil');
});
it('clicking edit button makes items list editable', async () => {
// Off by default
expect(findItemsList().props('editable')).toBe(false);
// On when clicked
await findListEditButton().vm.$emit('click');
expect(findItemsList().props('editable')).toBe(true);
// Off when clicked again
await findListEditButton().vm.$emit('click');
expect(findItemsList().props('editable')).toBe(false);
});
it('remove-item event emission from items-list causes list item to be removed', async () => {
const localStorageProjects = findItemsList().props('items');
await findListEditButton().vm.$emit('click');
await findItemsList().vm.$emit('remove-item', localStorageProjects[0]);

View File

@ -42,45 +42,47 @@ describe('HelpCenter component', () => {
};
};
const DEFAULT_HELP_ITEMS = [
{ text: HelpCenter.i18n.help, href: helpPagePath(), extraAttrs: trackingAttrs('help') },
{
text: HelpCenter.i18n.support,
href: sidebarData.support_path,
extraAttrs: trackingAttrs('support'),
},
{
text: HelpCenter.i18n.docs,
href: 'https://docs.gitlab.com',
extraAttrs: trackingAttrs('gitlab_documentation'),
},
{
text: HelpCenter.i18n.plans,
href: `${PROMO_URL}/pricing`,
extraAttrs: trackingAttrs('compare_gitlab_plans'),
},
{
text: HelpCenter.i18n.forum,
href: 'https://forum.gitlab.com/',
extraAttrs: trackingAttrs('community_forum'),
},
{
text: HelpCenter.i18n.contribute,
href: helpPagePath('', { anchor: 'contributing-to-gitlab' }),
extraAttrs: trackingAttrs('contribute_to_gitlab'),
},
{
text: HelpCenter.i18n.feedback,
href: 'https://about.gitlab.com/submit-feedback',
extraAttrs: trackingAttrs('submit_feedback'),
},
];
describe('default', () => {
beforeEach(() => {
createWrapper(sidebarData);
});
it('renders menu items', () => {
expect(findDropdownGroup(0).props('group').items).toEqual([
{ text: HelpCenter.i18n.help, href: helpPagePath(), extraAttrs: trackingAttrs('help') },
{
text: HelpCenter.i18n.support,
href: sidebarData.support_path,
extraAttrs: trackingAttrs('support'),
},
{
text: HelpCenter.i18n.docs,
href: 'https://docs.gitlab.com',
extraAttrs: trackingAttrs('gitlab_documentation'),
},
{
text: HelpCenter.i18n.plans,
href: `${PROMO_URL}/pricing`,
extraAttrs: trackingAttrs('compare_gitlab_plans'),
},
{
text: HelpCenter.i18n.forum,
href: 'https://forum.gitlab.com/',
extraAttrs: trackingAttrs('community_forum'),
},
{
text: HelpCenter.i18n.contribute,
href: helpPagePath('', { anchor: 'contributing-to-gitlab' }),
extraAttrs: trackingAttrs('contribute_to_gitlab'),
},
{
text: HelpCenter.i18n.feedback,
href: 'https://about.gitlab.com/submit-feedback',
extraAttrs: trackingAttrs('submit_feedback'),
},
]);
expect(findDropdownGroup(0).props('group').items).toEqual(DEFAULT_HELP_ITEMS);
expect(findDropdownGroup(1).props('group').items).toEqual([
expect.objectContaining({ text: HelpCenter.i18n.shortcuts }),
@ -94,6 +96,23 @@ describe('HelpCenter component', () => {
});
});
describe('with show_tanuki_bot true', () => {
beforeEach(() => {
createWrapper({ ...sidebarData, show_tanuki_bot: true });
});
it('shows Ask the Tanuki Bot with the help items', () => {
expect(findDropdownGroup(0).props('group').items).toEqual([
expect.objectContaining({
icon: 'tanuki',
text: HelpCenter.i18n.tanuki,
extraAttrs: trackingAttrs('tanuki_bot_help_dropdown'),
}),
...DEFAULT_HELP_ITEMS,
]);
});
});
describe('with Gitlab version check feature enabled', () => {
beforeEach(() => {
createWrapper({ ...sidebarData, show_version_check: true });

View File

@ -73,7 +73,6 @@ describe('ItemsList component', () => {
createWrapper({
props: {
items: [mockProject],
editable: true,
},
mountFn: mountExtended,
});
@ -84,7 +83,7 @@ describe('ItemsList component', () => {
expect(itemRemoveButton.exists()).toBe(true);
expect(itemRemoveButton.attributes('title')).toBe('Remove');
expect(itemRemoveButton.findComponent(GlIcon).props('name')).toBe('close');
expect(itemRemoveButton.findComponent(GlIcon).props('name')).toBe('dash');
});
it('emits `remove-item` event with item param when remove button is clicked', () => {

View File

@ -78,7 +78,7 @@ RSpec.describe Banzai::Pipeline::IncidentManagement::TimelineEventPipeline do
it 'replaces existing label to a link' do
# rubocop:disable Layout/LineLength
is_expected.to match(
%r(<p>.+<a href="[\w/]+-/issues\?label_name=#{label.name}".+style="background-color: #\d{6}".*>#{label.name}</span></a></span> ~unknown</p>)
%r{<p>.+<a href="[\w\-/]+-/issues\?label_name=#{label.name}".+style="background-color: #\d{6}".*>#{label.name}</span></a></span> ~unknown</p>}
)
# rubocop:enable Layout/LineLength
end
@ -95,7 +95,7 @@ RSpec.describe Banzai::Pipeline::IncidentManagement::TimelineEventPipeline do
let(:markdown) { "issue ##{issue.iid}" }
it 'contains a link to the issue' do
is_expected.to match(%r(<p>issue <a href="[\w/]+-/issues/#{issue.iid}".*>##{issue.iid}</a></p>))
is_expected.to match(%r{<p>issue <a href="[\w\-/]+-/issues/#{issue.iid}".*>##{issue.iid}</a></p>})
end
end
@ -104,7 +104,7 @@ RSpec.describe Banzai::Pipeline::IncidentManagement::TimelineEventPipeline do
let(:markdown) { "MR !#{mr.iid}" }
it 'contains a link to the merge request' do
is_expected.to match(%r(<p>MR <a href="[\w/]+-/merge_requests/#{mr.iid}".*>!#{mr.iid}</a></p>))
is_expected.to match(%r{<p>MR <a href="[\w\-/]+-/merge_requests/#{mr.iid}".*>!#{mr.iid}</a></p>})
end
end
end

View File

@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.describe Banzai::Pipeline::WikiPipeline, feature_category: :wiki do
let_it_be(:namespace) { create(:namespace, name: "wiki_link_ns") }
let_it_be(:project) { create(:project, :public, name: "wiki_link_project", namespace: namespace) }
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, :public, namespace: namespace) }
let_it_be(:wiki) { ProjectWiki.new(project, nil) }
let_it_be(:page) { build(:wiki_page, wiki: wiki, title: 'nested/twice/start-page') }
@ -85,14 +85,14 @@ RSpec.describe Banzai::Pipeline::WikiPipeline, feature_category: :wiki do
markdown = "[Page](./page)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/page\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/nested/twice/page\"")
end
it "rewrites file links to be at the scope of the current directory" do
markdown = "[Link to Page](./page.md)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/page.md\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/nested/twice/page.md\"")
end
end
@ -101,14 +101,14 @@ RSpec.describe Banzai::Pipeline::WikiPipeline, feature_category: :wiki do
markdown = "[Link to Page](../page)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/page\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/nested/page\"")
end
it "rewrites file links to be at the scope of the parent directory" do
markdown = "[Link to Page](../page.md)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/page.md\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/nested/page.md\"")
end
end
@ -117,14 +117,14 @@ RSpec.describe Banzai::Pipeline::WikiPipeline, feature_category: :wiki do
markdown = "[Link to Page](./subdirectory/page)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/subdirectory/page\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/nested/twice/subdirectory/page\"")
end
it "rewrites file links to be at the scope of the sub-directory" do
markdown = "[Link to Page](./subdirectory/page.md)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/subdirectory/page.md\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/nested/twice/subdirectory/page.md\"")
end
end
@ -133,35 +133,35 @@ RSpec.describe Banzai::Pipeline::WikiPipeline, feature_category: :wiki do
markdown = "[Link to Page](page)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/page\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/page\"")
end
it 'rewrites non-file links (with spaces) to be at the scope of the wiki root' do
markdown = "[Link to Page](page slug)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/page%20slug\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/page%20slug\"")
end
it "rewrites file links to be at the scope of the current directory" do
markdown = "[Link to Page](page.md)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/page.md\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/nested/twice/page.md\"")
end
it 'rewrites links with anchor' do
markdown = '[Link to Header](start-page#title)'
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/start-page#title\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/start-page#title\"")
end
it 'rewrites links (with spaces) with anchor' do
markdown = '[Link to Header](start page#title)'
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/start%20page#title\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/start%20page#title\"")
end
end
@ -170,14 +170,14 @@ RSpec.describe Banzai::Pipeline::WikiPipeline, feature_category: :wiki do
markdown = "[Link to Page](/page)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/page\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/page\"")
end
it 'rewrites file links to be at the scope of the wiki root' do
markdown = "[Link to Page](/page.md)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/page.md\"")
expect(output).to include("href=\"#{relative_url_root}/#{project.full_path}/-/wikis/page.md\"")
end
end
end
@ -278,28 +278,28 @@ RSpec.describe Banzai::Pipeline::WikiPipeline, feature_category: :wiki do
markdown = "![video_file](video_file_name.mp4)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/video_file_name.mp4"')
expect(output).to include(%(<video src="/#{project.full_path}/-/wikis/nested/twice/video_file_name.mp4"))
end
it 'rewrites and replaces video links names with white spaces to %20' do
markdown = "![video file](video file name.mp4)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/video%20file%20name.mp4"')
expect(output).to include(%(<video src="/#{project.full_path}/-/wikis/nested/twice/video%20file%20name.mp4"))
end
it 'generates audio html structure' do
markdown = "![audio_file](audio_file_name.wav)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include('<audio src="/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/audio_file_name.wav"')
expect(output).to include(%(<audio src="/#{project.full_path}/-/wikis/nested/twice/audio_file_name.wav"))
end
it 'rewrites and replaces audio links names with white spaces to %20' do
markdown = "![audio file](audio file name.wav)"
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
expect(output).to include('<audio src="/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/audio%20file%20name.wav"')
expect(output).to include(%(<audio src="/#{project.full_path}/-/wikis/nested/twice/audio%20file%20name.wav"))
end
end
@ -320,7 +320,7 @@ RSpec.describe Banzai::Pipeline::WikiPipeline, feature_category: :wiki do
output = described_class.to_html(markdown, project: project, wiki: wiki, page_slug: page.slug)
doc = Nokogiri::HTML::DocumentFragment.parse(output)
full_path = "/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/#{wiki_file.path}"
full_path = "/#{project.full_path}/-/wikis/nested/twice/#{wiki_file.path}"
expect(doc.css('a')[0].attr('href')).to eq(full_path)
expect(doc.css('img')[0].attr('class')).to eq('gfm lazy')
expect(doc.css('img')[0].attr('data-src')).to eq(full_path)

View File

@ -552,7 +552,7 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
end
let_it_be(:group) { create(:group, path: 'build') }
let_it_be(:project) { create(:project, name: 'cng', namespace: group) }
let_it_be(:project) { create(:project, path: 'cng', namespace: group) }
let_it_be(:container_repository) { create(:container_repository, project: project, name: "docker-alpine") }
shared_examples 'fetching the project from container repository and path' do

View File

@ -260,16 +260,16 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project, feature_category: :p
}
context 'when project name and ref include masked variables' do
let(:project_name) { 'my_project_name' }
let_it_be(:project) { create(:project, :repository, path: 'my_project_path') }
let(:branch_name) { 'merge-commit-analyze-after' }
let(:project) { create(:project, :repository, name: project_name) }
let(:namespace_path) { project.namespace.full_path }
let(:included_project_sha) { project.commit(branch_name).sha }
let(:variables) do
Gitlab::Ci::Variables::Collection.new(
[
{ key: 'VAR1', value: project_name, masked: true },
{ key: 'VAR1', value: 'my_project_path', masked: true },
{ key: 'VAR2', value: branch_name, masked: true }
])
end

View File

@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe Gitlab::Metrics::Dashboard::Stages::GrafanaFormatter do
include GrafanaApiHelpers
let_it_be(:namespace) { create(:namespace, name: 'foo') }
let_it_be(:project) { create(:project, namespace: namespace, name: 'bar') }
let_it_be(:namespace) { create(:namespace, path: 'foo') }
let_it_be(:project) { create(:project, namespace: namespace, path: 'bar') }
describe '#transform!' do
let(:grafana_dashboard) { Gitlab::Json.parse(fixture_file('grafana/simplified_dashboard_response.json'), symbolize_names: true) }

View File

@ -27,7 +27,7 @@ RSpec.describe Gitlab::Tracking::Destinations::DatabaseEventsSnowplow, :do_not_s
before do
allow(SnowplowTracker::AsyncEmitter)
.to receive(:new)
.with(endpoint: 'localhost:9091',
.with(endpoint: endpoint,
options:
{
protocol: 'https',
@ -47,15 +47,38 @@ RSpec.describe Gitlab::Tracking::Destinations::DatabaseEventsSnowplow, :do_not_s
end
describe '#event' do
let(:endpoint) { 'localhost:9091' }
let(:event_params) do
{
category: 'category',
action: 'action',
label: 'label',
property: 'property',
value: 1.5,
context: nil,
tstamp: (Time.now.to_f * 1000).to_i
}
end
context 'when on gitlab.com environment' do
let(:endpoint) { 'db-snowplow.trx.gitlab.net' }
it 'sends event to tracker' do
allow(Gitlab).to receive(:com?).and_return(true)
allow(tracker).to receive(:track_struct_event).and_call_original
subject.event('category', 'action', label: 'label', property: 'property', value: 1.5)
expect(tracker).to have_received(:track_struct_event).with(event_params)
end
end
it 'sends event to tracker' do
allow(tracker).to receive(:track_struct_event).and_call_original
subject.event('category', 'action', label: 'label', property: 'property', value: 1.5)
expect(tracker)
.to have_received(:track_struct_event)
.with(category: 'category', action: 'action', label: 'label', property: 'property', value: 1.5, context: nil,
tstamp: (Time.now.to_f * 1000).to_i)
expect(tracker).to have_received(:track_struct_event).with(event_params)
end
it 'increase total snowplow events counter' do

View File

@ -6,7 +6,7 @@ RSpec.describe ContainerRegistry::Event do
using RSpec::Parameterized::TableSyntax
let_it_be(:group) { create(:group, name: 'group') }
let_it_be(:project) { create(:project, name: 'test', namespace: group) }
let_it_be(:project) { create(:project, path: 'test', namespace: group) }
describe '#supported?' do
let(:raw_event) { { 'action' => action } }

View File

@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Releases::Source do
let_it_be(:project) { create(:project, :repository, name: 'finance-cal') }
let_it_be(:project) { create(:project, :repository) }
let(:tag_name) { 'v1.0' }
@ -27,7 +27,7 @@ RSpec.describe Releases::Source do
it 'returns zip archived source url' do
is_expected
.to eq("#{project.web_url}/-/archive/v1.0/finance-cal-v1.0.zip")
.to eq("#{project.web_url}/-/archive/v1.0/#{project.path}-v1.0.zip")
end
context 'when ref is directory structure' do
@ -35,7 +35,7 @@ RSpec.describe Releases::Source do
it 'converts slash to dash' do
is_expected
.to eq("#{project.web_url}/-/archive/beta/v1.0/finance-cal-beta-v1.0.zip")
.to eq("#{project.web_url}/-/archive/beta/v1.0/#{project.path}-beta-v1.0.zip")
end
end
end

View File

@ -29,7 +29,7 @@ RSpec.describe IssuePresenter do
describe '#web_url' do
it 'returns correct path' do
expect(presenter.web_url).to eq("http://localhost/#{group.name}/#{project.name}/-/issues/#{presented_issue.iid}")
expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/-/issues/#{presented_issue.iid}")
end
context 'when issue type is task' do
@ -59,7 +59,7 @@ RSpec.describe IssuePresenter do
describe '#issue_path' do
it 'returns correct path' do
expect(presenter.issue_path).to eq("/#{group.name}/#{project.name}/-/issues/#{presented_issue.iid}")
expect(presenter.issue_path).to eq("/#{project.full_path}/-/issues/#{presented_issue.iid}")
end
context 'when issue type is task' do

View File

@ -15,7 +15,7 @@ RSpec.describe Releases::LinkPresenter do
context 'when filepath is provided' do
let(:filepath) { '/bin/bigfile.exe' }
let(:expected_url) do
"http://localhost/#{release.project.namespace.path}/#{release.project.name}" \
"http://localhost/#{release.project.full_path}" \
"/-/releases/#{release.tag}/downloads/bin/bigfile.exe"
end

View File

@ -951,7 +951,7 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :projects d
expect(json_response.length).to eq(8)
project_names = json_response.map { |proj| proj['name'] }
expect(project_names).to contain_exactly(project.name, project2.name, 'second_project', 'public_project', 'Project', 'Test Project', 'Test Public Project', 'Test')
expect(project_names).to match_array([project, project2, project3, public_project, project_1, project_2, project_4, project_3].map(&:name))
end
end

View File

@ -72,7 +72,7 @@ RSpec.describe AnalyticsIssueEntity do
end
context 'without subgroup' do
let_it_be(:project) { create(:project, name: 'my project') }
let_it_be(:project) { create(:project) }
subject { entity.as_json }
@ -80,14 +80,14 @@ RSpec.describe AnalyticsIssueEntity do
end
context 'with subgroup' do
let_it_be(:project) { create(:project, :in_subgroup, name: 'my project') }
let_it_be(:project) { create(:project, :in_subgroup) }
subject { entity.as_json }
it_behaves_like 'generic entity'
it 'has URL containing subgroup' do
expect(subject[:url]).to include("#{project.group.parent.name}/#{project.group.name}/my_project/")
expect(subject[:url]).to include("#{project.group.parent.name}/#{project.group.name}/#{project.path}/")
end
end
end

View File

@ -55,14 +55,16 @@ RSpec.describe Clusters::Cleanup::ServiceAccountService, feature_category: :depl
context 'when there is a Kubeclient::HttpError' do
['Unauthorized', 'forbidden', 'Certificate verify Failed'].each do |message|
before do
allow(kubeclient_instance_double)
.to receive(:delete_service_account)
.and_raise(Kubeclient::HttpError.new(401, message, nil))
end
context "with error:#{message}" do
before do
allow(kubeclient_instance_double)
.to receive(:delete_service_account)
.and_raise(Kubeclient::HttpError.new(401, message, nil))
end
it 'destroys cluster' do
expect { subject }.to change { Clusters::Cluster.where(id: cluster.id).exists? }.from(true).to(false)
it 'destroys cluster' do
expect { subject }.to change { Clusters::Cluster.where(id: cluster.id).exists? }.from(true).to(false)
end
end
end
end

View File

@ -9,7 +9,7 @@ RSpec.shared_examples 'packages list' do |check_project_name: false|
expect(package_row).to have_content(pkg.name)
expect(package_row).to have_content(pkg.version)
expect(package_row).to have_content(pkg.project.name) if check_project_name
expect(package_row).to have_content(pkg.project.path) if check_project_name
end
end

View File

@ -105,7 +105,7 @@ RSpec.describe Integrations::IrkerWorker, '#perform', feature_category: :integra
end
def wrap_message(text)
message = "[#{project.path}] #{push_data['user_name']} #{text}"
message = "[#{project.name}] #{push_data['user_name']} #{text}"
to_send = { to: channels, privmsg: message }
Gitlab::Json.dump(to_send)