Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-06-25 21:10:09 +00:00
parent d07e9f8c10
commit 3c3db9066b
31 changed files with 477 additions and 266 deletions

View File

@ -66,7 +66,7 @@ export default ({ editorAiActions = [] } = {}) => {
reportAbusePath: notesDataset.reportAbusePath,
newCommentTemplatePaths: JSON.parse(notesDataset.newCommentTemplatePaths),
resourceGlobalId: convertToGraphQLId(noteableData.noteableType, noteableData.id),
editorAiActions: editorAiActions.map((factory) => factory(noteableData)),
legacyEditorAiActions: editorAiActions.map((factory) => factory(noteableData)),
newCustomEmojiPath: notesDataset.newCustomEmojiPath,
},
data() {

View File

@ -96,6 +96,11 @@ export default {
required: false,
default: null,
},
defaultBranch: {
type: String,
required: false,
default: null,
},
},
data() {
return {
@ -131,6 +136,13 @@ export default {
selectedRefFallback;
}
const defaultBranchData = branches.find((branch) => branch.name === this.defaultBranch);
// since the API for getting list of branches is paginated, we might not have a
// default branch available, so in that case we add it to the list
if (this.defaultBranch && !defaultBranchData) {
branches.push({ name: this.defaultBranch, value: this.defaultBranch, default: true });
}
return formatListBoxItems({ branches, tags, commits, selectedRef });
},
branches() {

View File

@ -223,6 +223,7 @@ export default {
data-testid="ref-dropdown-container"
:project-id="projectId"
:value="refSelectorValue"
:default-branch="rootRef"
use-symbolic-ref-names
:query-params="refSelectorQueryParams"
@input="onInput"

View File

@ -139,6 +139,11 @@ export default {
required: false,
default: false,
},
editorAiActions: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
@ -362,6 +367,7 @@ export default {
:data-uploads-path="uploadsPath"
>
<markdown-header
:editor-ai-actions="editorAiActions"
:preview-markdown="previewMarkdown"
:line-content="lineContent"
:can-suggest="canSuggest"

View File

@ -55,12 +55,17 @@ export default {
newCommentTemplatePaths: {
default: () => [],
},
editorAiActions: { default: () => [] },
mrGeneratedContent: { default: null },
canSummarizeChanges: { default: false },
canUseComposer: { default: false },
legacyEditorAiActions: { default: () => [] },
},
props: {
editorAiActions: {
type: Array,
required: false,
default: () => [],
},
previewMarkdown: {
type: Boolean,
required: true,
@ -123,6 +128,7 @@ export default {
},
data() {
const modifierKey = getModifierKey();
return {
tag: '> ',
suggestPopoverVisible: false,
@ -138,6 +144,12 @@ export default {
};
},
computed: {
aiActions() {
if (this.editorAiActions.length > 0) {
return this.editorAiActions;
}
return this.legacyEditorAiActions;
},
commentTemplatePaths() {
return this.newCommentTemplatePaths.length > 0
? this.newCommentTemplatePaths
@ -181,6 +193,17 @@ export default {
totalHighlights: this.findAndReplace.totalMatchCount,
});
},
previewToggleTooltip() {
return sprintf(
this.previewMarkdown
? s__('MarkdownEditor|Continue editing (%{shiftKey}%{modifierKey}P)')
: s__('MarkdownEditor|Preview (%{shiftKey}%{modifierKey}P)'),
{
shiftKey: this.shiftKey,
modifierKey: this.modifierKey,
},
);
},
},
watch: {
showSuggestPopover() {
@ -518,8 +541,10 @@ export default {
>
<gl-button
v-if="enablePreview"
v-gl-tooltip
data-testid="preview-toggle"
:value="previewMarkdown ? 'preview' : 'edit'"
:title="previewToggleTooltip"
:label="$options.i18n.previewTabTitle"
class="js-md-preview-button gl-flex-row-reverse gl-items-center !gl-font-normal"
size="small"
@ -572,10 +597,10 @@ export default {
</div>
</template>
<div class="gl-flex gl-gap-y-2">
<div v-if="!previewMarkdown && editorAiActions.length" class="gl-flex gl-gap-y-2">
<div v-if="!previewMarkdown && aiActions.length" class="gl-flex gl-gap-y-2">
<header-divider v-if="!previewMarkdown" />
<ai-actions-dropdown
:actions="editorAiActions"
:actions="aiActions"
@input="insertAIAction"
@replace="replaceTextarea"
/>

View File

@ -143,6 +143,11 @@ export default {
required: false,
default: () => [],
},
editorAiActions: {
type: Array,
required: false,
default: () => [],
},
},
data() {
let editingMode;
@ -399,6 +404,7 @@ export default {
:new-comment-template-paths="newCommentTemplatePaths"
:can-attach-file="!disableAttachments"
:can-suggest="codeSuggestionsConfig.canSuggest"
:editor-ai-actions="editorAiActions"
:line="codeSuggestionsConfig.line"
:lines="codeSuggestionsConfig.lines"
:show-suggest-popover="codeSuggestionsConfig.showPopover"

View File

@ -17,6 +17,7 @@ import {
autocompleteDataSources,
markdownPreviewPath,
} from '~/work_items/utils';
import projectPermissionsQuery from '../graphql/ai_permissions_for_project.query.graphql';
import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql';
import workItemDescriptionTemplateQuery from '../graphql/work_item_description_template.query.graphql';
import { i18n, NEW_WORK_ITEM_IID, TRACKING_CATEGORY_SHOW, ROUTES } from '../constants';
@ -39,11 +40,6 @@ export default {
WorkItemDescriptionTemplateListbox,
},
mixins: [Tracking.mixin()],
provide: {
editorAiActions: window.gon?.licensed_features?.generateDescription
? [generateDescriptionAction()]
: [],
},
inject: ['isGroup'],
props: {
description: {
@ -137,12 +133,19 @@ export default {
descriptionTemplate: null,
appliedTemplate: '',
showTemplateApplyWarning: false,
workspacePermissions: {},
};
},
computed: {
createFlow() {
return this.workItemId === newWorkItemId(this.newWorkItemType);
},
editorAiActions() {
const { id, userPermissions } = this.workspacePermissions;
return userPermissions?.generateDescription
? [generateDescriptionAction({ resourceId: id })]
: [];
},
workItemFullPath() {
return this.createFlow
? newWorkItemFullPath(this.fullPath, this.newWorkItemType)
@ -343,6 +346,25 @@ export default {
this.$emit('error', s__('WorkItem|Unable to find selected template.'));
},
},
workspacePermissions: {
query() {
return projectPermissionsQuery;
},
variables() {
return {
fullPath: this.fullPath,
};
},
update(data) {
return data.workspace || {};
},
skip() {
return this.isGroup;
},
error(error) {
Sentry.captureException(error);
},
},
},
methods: {
checkForConflicts() {
@ -545,6 +567,7 @@ export default {
:autocomplete-data-sources="autocompleteDataSources"
:restricted-tool-bar-items="restrictedToolBarItems"
:uploads-path="uploadsPath"
:editor-ai-actions="editorAiActions"
enable-autocomplete
supports-quick-actions
:autofocus="autofocus"

View File

@ -0,0 +1,8 @@
query projectGenerateDescriptionPermissions($fullPath: ID!) {
workspace: project(fullPath: $fullPath) {
id
userPermissions {
generateDescription
}
}
}

View File

@ -1097,7 +1097,7 @@ module Ci
def debug_mode?
# perform the check on both sides in case the runner version is old
metadata&.debug_trace_enabled? ||
debug_trace_enabled? ||
Gitlab::Utils.to_boolean(variables['CI_DEBUG_SERVICES']&.value, default: false) ||
Gitlab::Utils.to_boolean(variables['CI_DEBUG_TRACE']&.value, default: false)
end

View File

@ -30,14 +30,22 @@ module Ci
@predicate_type = "https://slsa.dev/provenance/v1"
end
def as_json(options = nil)
json = super
exceptions = ["_type"]
json.deep_transform_keys do |k|
next k if exceptions.include?(k)
def deep_change_case(json)
exceptions = %w[_type variables]
k.camelize(:lower)
new_json = {}
json.each do |key, value|
key = key.camelize(:lower) if exceptions.exclude?(key)
value = deep_change_case(value) if value.is_a?(Hash) && exceptions.exclude?(key)
new_json[key] = value
end
new_json
end
def as_json(options = nil)
deep_change_case(super)
end
def attributes
@ -52,7 +60,7 @@ module Ci
def self.from_build(build)
# TODO: update buildType as part of https://gitlab.com/gitlab-org/gitlab/-/issues/426764
build_type = "https://gitlab.com/gitlab-org/gitlab/-/issues/546150"
external_parameters = { variables: build.variables.map(&:key) }
external_parameters = ExternalParameters.from_build(build)
internal_parameters = {
architecture: build.runner_manager.architecture,
executor: build.runner_manager.executor_type,
@ -94,6 +102,28 @@ module Ci
end
end
class ExternalParameters
include ActiveModel::Model
attr_accessor :source, :entry_point, :variables
def self.from_build(build)
source = Gitlab::Routing.url_helpers.project_url(build.project)
entry_point = build.name
variables = {}
build.variables.each do |variable|
variables[variable.key] = if variable.masked?
'[MASKED]'
else
variable.value
end
end
ExternalParameters.new(source: source, entry_point: entry_point, variables: variables)
end
end
class Builder
include ActiveModel::Model

View File

@ -107,6 +107,14 @@ module Ci
end
end
# TODO: Update this logic when column `p_ci_builds.debug_trace_enabled` is added.
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/194954#note_2574776849.
def debug_trace_enabled?
return true if degenerated?
metadata&.debug_trace_enabled?
end
private
def read_metadata_attribute(legacy_key, metadata_key, default_value = nil)

View File

@ -123,7 +123,11 @@ namespace :admin do
resource :system_info, controller: 'system_info', only: [:show]
resources :projects, only: [:index]
resources :projects, only: [:index] do
collection do
get :active, :inactive, to: 'projects#index'
end
end
resources :usage_trends, only: :index
resource :dev_ops_reports, controller: 'dev_ops_report', only: :show

View File

@ -10,4 +10,5 @@ description: Routing table for CI runner managers
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/168131
milestone: '17.6'
gitlab_schema: gitlab_ci_cell_local
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/460084
table_size: small

View File

@ -10,4 +10,5 @@ description: Routing table for CI runners
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166308
milestone: '17.6'
gitlab_schema: gitlab_ci_cell_local
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442395
table_size: small

View File

@ -50,6 +50,6 @@ Automatically sync Jira issues to GitLab to unlock VSA metrics tracking. Real-ti
[Agentic Workflow: Apply Coding Style Guide](duo_workflow/duo_workflow_codestyle.md)
## Compliance and Best Practice
## Compliance and Best Practices
[Guide on Separation of Duties](guide_on_sod.md)

View File

@ -37,7 +37,7 @@ The GitLab approach to implementing SoD through Role-Based Access Control (RBAC)
### Role-Based Access Control (RBAC)
TRBAC forms the framework for implementing and enforcing SoD. It governs permissions and responsibilities across the platform, ensuring compliance with the principles of least privilege. Through RBAC, organizations can:
RBAC forms the framework for implementing and enforcing SoD. It governs permissions and responsibilities across the platform, ensuring compliance with the principles of least privilege. Through RBAC, organizations can:
- Implement holistic user management with granular role-based controls
- Assign roles with the least privileged access principles

View File

@ -20,20 +20,88 @@ title: Static reachability analysis
{{< /history >}}
Static reachability analysis (SRA) helps you prioritize remediation of vulnerabilities in dependencies.
Static reachability analysis (SRA) helps you prioritize remediation of vulnerabilities in
dependencies. SRA identifies which dependencies your application actually uses. While dependency
scanning finds all vulnerable dependencies, SRA focuses on those that are reachable and pose higher
security risks, helping you prioritize remediation based on actual threat exposure.
An application is generally deployed with many dependencies. Dependency scanning identifies which of
those dependencies have vulnerabilities. However, not all dependencies are used by an application.
Static reachability analysis identifies those dependencies that are used, in other words reachable,
and so are a higher security risk than others. Use this information to help prioritize remediation
of vulnerabilities according to risk.
## Getting started
If you are new to static reachability analysis, the following steps show how to enable it for your
project.
Prerequisites:
- Only Python projects are supported.
- [Dependency Scanning analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/dependency-scanning)
version 0.23.0 and later.
- Enable [Dependency Scanning by using SBOM](dependency_scanning_sbom/_index.md#configuration).
[Gemnasium](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium) analyzers are not
supported.
Follow the [pip](dependency_scanning_sbom/_index.md#pip) or
[pipenv](dependency_scanning_sbom/_index.md#pipenv)
related instructions for dependency scanning using SBOM. You can also use any other Python package
manager that is
[supported](https://gitlab.com/gitlab-org/security-products/analyzers/dependency-scanning#supported-files)
by the DS analyzer.
Exclusions:
- SRA cannot be used together with either a scan execution policy or pipeline execution policy.
To enable SRA:
- On the left sidebar, select **Search or go to** and find your project.
- Edit the `.gitlab-ci.yml` file, and add one of the following.
If you're using the CI/CD template, add the following (ensure there is only one `variables:`
line):
```yaml
variables:
DS_STATIC_REACHABILITY_ENABLED: true
```
If you're using the [Dependency Scanning component](https://gitlab.com/components/dependency-scanning),
add the following (ensuring there is only one `include:` line.):
```yaml
include:
- component: ${CI_SERVER_FQDN}/components/dependency-scanning/main@0
inputs:
enable_static_reachability: true
rules:
- if: $CI_SERVER_HOST == "gitlab.com"
```
At this point, SRA is enabled in your pipeline. When dependency scanning runs and outputs an SBOM,
the results are supplemented by static reachability analysis.
## Understanding the results
To identify vulnerable dependencies that are reachable, either:
- Hover over the **Severity** value of a vulnerability in the vulnerability report.
- Check the `Reachable` value in the vulnerability page.
- In the vulnerability report, hover over the **Severity** value of a vulnerability.
- In a vulnerability's details page, check the **Reachable** value.
- Use a GraphQL query to list those vulnerabilities that are reachable.
A dependency can have one of the following reachability values:
Yes
: The package linked to this vulnerability is confirmed reachable in code.
Not Found
: SRA ran successfully but did not detect usage of the vulnerable package. If a vulnerable
dependency's reachability value is shown as **Not Found** exercise caution rather than completely
dismissing it, because the beta version of SRA may produce false negatives.
Not Available
: SRA was not executed, so no reachability data exists.
When a direct dependency is marked as **in use**, all its transitive dependencies are also marked as
**in use**.
## Supported languages and package managers
Static reachability analysis is available only for Python projects. SRA uses the new dependency
@ -43,140 +111,20 @@ scanning analyzer to generate SBOMs and so supports the same package managers as
|----------|----------------------------|
| Python | `pip`, `pipenv`, `poetry`, `uv` |
## Enable static reachability analysis
## Running SRA in an offline environment
Enable static reachability analysis to identify high-risk dependencies.
Prerequisites:
- Enable [Dependency Scanning by using SBOM](dependency_scanning_sbom/_index.md#getting-started).
Make sure you follow the [pip](dependency_scanning_sbom/_index.md#pip) or [pipenv](dependency_scanning_sbom/_index.md#pipenv)
related instructions for dependency scanning using SBOM. You can also use any other Python package manager that is [supported](https://gitlab.com/gitlab-org/security-products/analyzers/dependency-scanning#supported-files) by the DS analyzer.
To enable static reachability analysis from GitLab 18.0 and later:
- Set the CI/CD variable `DS_STATIC_REACHABILITY_ENABLED` to `true`
Static reachability is integrated into the `dependency-scanning` job of the latest Dependency-Scanning template.
Alternatively you can enable Static Reachability by including the [Dependency Scanning component](https://gitlab.com/components/dependency-scanning) rather than using the standard Dependency-Scanning template.
```yaml
include:
- component: ${CI_SERVER_FQDN}/components/dependency-scanning/main@0
inputs:
enable_static_reachability: true
rules:
- if: $CI_SERVER_HOST == "gitlab.com"
```
Please notice that to use GitLab.com components on a GitLab Self-Managed instance, you [must mirror](../../../ci/components/_index.md#use-a-gitlabcom-component-on-gitlab-self-managed) the component project.
Static reachability analysis functionality is supported in [Dependency Scanning analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/dependency-scanning) version `0.23.0` and all subsequent versions.
<details><summary>If you are using GitLab 17.11 follow these instructions to enable Static Reachability Analysis</summary>
- Make sure you extend `dependency-scanning-with-reachability` needs section to depend on the build job that creates the artifact required by the DS analyzer.
```yaml
stages:
- build
- test
include:
- template: Jobs/Dependency-Scanning.latest.gitlab-ci.yml
variables:
DS_STATIC_REACHABILITY_ENABLED: true
DS_ENFORCE_NEW_ANALYZER: true
# create job required by the DS analyzer to create pipdeptree.json
# https://docs.gitlab.com/user/application_security/dependency_scanning/dependency_scanning_sbom/#pip
create:
stage: build
image: "python:latest"
script:
- "pip install -r requirements.txt"
- "pip install pipdeptree"
- "pipdeptree --json > pipdeptree.json"
artifacts:
when: on_success
access: developer
paths: ["**/pipdeptree.json"]
dependency-scanning-with-reachability:
needs:
- job: gitlab-static-reachability
optional: true
artifacts: true
- job: create
optional: true
artifacts: true
```
Static reachability in 17.11 introduces two key jobs:
- `gitlab-static-reachability`: Performs Static Reachability Analysis (SRA) on your Python files.
- `dependency-scanning-with-reachability`: Executes dependency scanning and generates an SBOM report enriched with reachability data. This job requires the artifact output from the `gitlab-static-reachability` job.
{{< alert type="note" >}}
When you enable static reachability feature for non-Python projects, the
`gitlab-static-reachability` job will fail but won't break your pipeline, because it's configured to
allow failures. In such cases, the `dependency-scanning-with-reachability` job will perform standard
dependency scanning without adding reachability data to the SBOM.
{{< /alert >}}
</details>
To use the dependency scanning component in an offline environment, you must first
[mirror the component project](../../../ci/components/_index.md#use-a-gitlabcom-component-on-gitlab-self-managed).
## How static reachability analysis works
SRA (Static reachability analysis) identifies dependencies used in a project's code and marks them and their dependencies as reachable.
Dependency scanning generates an SBOM report that identifies all components and their transitive
dependencies. Static reachability analysis checks each dependency in the SBOM report and adds a
reachability value to the SBOM report. The enriched SBOM is then ingested by the GitLab instance.
The following are marked as not found:
- Dependencies that are found in the project's lock files but are not imported in the code.
- Tools that are included in the project's lock files for local usage but are not imported in the code.
For example, tools such as coverage testing or linting packages are marked as not found even if used locally.
SRA requires two key components:
- Dependency scanning (DS): Generates an SBOM report that identifies all components and their transitive dependencies.
- GitLab Advanced SAST (GLAS): Performs static reachability analysis to provide a report showing direct dependencies usage in the codebase.
SRA adds reachability data to the SBOM output by dependency scanning. The enriched SBOM is then ingested by the GitLab instance.
Reachability data in the UI can have one of the following values:
| Reachability values | Description |
|---------------------|---------------------------------------------------------------------------|
| Yes | The package linked to this vulnerability is confirmed reachable in code |
| Not Found | SRA ran successfully but did not detect usage of the vulnerable package |
| Not Available | SRA was not executed, therefore no reachability data exists |
## Where to find the reachability data
The reachability data is available in the vulnerability report
![Reachability on the vulnerability report](img/sr_vulnerability_report_v17_11.png)
and the vulnerability page
![Reachability on the vulnerability page](img/sr_vulnerability_page_v17_11.png)
Finally reachability data can be reached using GraphQL.
{{< alert type="warning" >}}
When a vulnerability reachability value shows as "Not Found," exercise caution rather than completely dismissing it, because the beta version of SRA may produce false negatives.
{{< /alert >}}
## Restrictions
Static reachability analysis has the following limitations:
- When a direct dependency is marked as `in use`, all its transitive dependencies are also marked as `in use`.
- Requires the new [dependency scanning analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/dependency-scanning). [Gemnasium](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium) analyzers are not supported.
- SRA on beta is not supported in combination with Scan and Pipeline execution policies
- Tools that are included in the project's lock files for local usage but are not imported in the
code. For example, tools such as coverage testing or linting packages are marked as not found even
if used locally.

View File

@ -317,7 +317,7 @@ To install the Helm chart for the GitLab workspaces proxy:
helm upgrade --install gitlab-workspaces-proxy \
gitlab-workspaces-proxy/gitlab-workspaces-proxy \
--version=0.1.19 \
--version=0.1.20 \
--namespace="gitlab-workspaces" \
--set="ingress.enabled=true" \
--set="ingress.hosts[0].host=${GITLAB_WORKSPACES_PROXY_DOMAIN}" \

View File

@ -4471,6 +4471,9 @@ msgstr ""
msgid "AdminSelfHostedModels|There was an error saving the self-hosted model. Please try again."
msgstr ""
msgid "AdminSelfHostedModels|This model cannot be applied to all %{mainFeature} sub-features"
msgstr ""
msgid "AdminSelfHostedModels|This self-hosted model cannot be deleted"
msgstr ""
@ -8187,6 +8190,9 @@ msgstr ""
msgid "Apply this approval rule to all branches or a specific protected branch."
msgstr ""
msgid "Apply to all button"
msgstr ""
msgid "Applying"
msgstr ""
@ -23358,6 +23364,9 @@ msgstr ""
msgid "DuoAgenticChat|GitLab Duo Agentic Chat"
msgstr ""
msgid "DuoAgentsPlatform|Agent Flow"
msgstr ""
msgid "DuoAgentsPlatform|Agents"
msgstr ""
@ -23397,9 +23406,6 @@ msgstr ""
msgid "DuoAgentsPlatform|Prompt"
msgstr ""
msgid "DuoAgentsPlatform|Prompt is unavailable"
msgstr ""
msgid "DuoAgentsPlatform|Run an Agent Flow"
msgstr ""
@ -37381,6 +37387,9 @@ msgstr ""
msgid "MarkdownEditor|Close find and replace bar"
msgstr ""
msgid "MarkdownEditor|Continue editing (%{shiftKey}%{modifierKey}P)"
msgstr ""
msgid "MarkdownEditor|Find and replace"
msgstr ""
@ -37405,6 +37414,9 @@ msgstr ""
msgid "MarkdownEditor|Outdent line (%{modifier_key}[)"
msgstr ""
msgid "MarkdownEditor|Preview (%{shiftKey}%{modifierKey}P)"
msgstr ""
msgid "MarkdownEditor|header"
msgstr ""
@ -40011,6 +40023,9 @@ msgstr ""
msgid "ModelSelection|Successfully updated %{mainFeature} / %{title}"
msgstr ""
msgid "ModelSelection|Successfully updated all %{mainFeature} features"
msgstr ""
msgid "Modified"
msgstr ""

View File

@ -114,7 +114,6 @@ spec/frontend/sidebar/components/confidential/confidentiality_dropdown_spec.js
spec/frontend/sidebar/components/confidential/sidebar_confidentiality_widget_spec.js
spec/frontend/sidebar/components/milestone/milestone_dropdown_spec.js
spec/frontend/sidebar/components/subscriptions/subscriptions_dropdown_spec.js
spec/frontend/snippets/components/snippet_description_edit_spec.js
spec/frontend/super_sidebar/components/sidebar_portal_spec.js
spec/frontend/super_sidebar/components/user_menu_spec.js
spec/frontend/tooltips/index_spec.js

View File

@ -60,7 +60,7 @@ fi
if [ -z "${GITLAB_WORKSPACES_PROXY_HELM_CHART_VERSION}" ]; then
echo "GITLAB_WORKSPACES_PROXY_HELM_CHART_VERSION is not explicitly set. Using default."
GITLAB_WORKSPACES_PROXY_HELM_CHART_VERSION="0.1.19"
GITLAB_WORKSPACES_PROXY_HELM_CHART_VERSION="0.1.20"
fi
if [ -z "${GITLAB_WORKSPACES_PROXY_HELM_RELEASE_NAMESPACE}" ]; then

View File

@ -36,7 +36,8 @@ FactoryBot.define do
external_parameters do
{
repository: "https://gitlab.com/tanuki/hello-world",
ref: "refs/heads/main"
ref: "refs/heads/main",
variables: { CI_PIPELINE: "test", ANOTHER_UPPERCASED_VAR: "test" }
}
end

View File

@ -28,6 +28,7 @@ describe('projects/settings/components/default_branch_selector', () => {
it('displays a RefSelector component', () => {
expect(findRefSelector().props()).toEqual({
disabled,
defaultBranch: null,
value: persistedDefaultBranch,
enabledRefTypes: [REF_TYPE_BRANCHES],
projectId,

View File

@ -886,4 +886,23 @@ describe('Ref selector component', () => {
});
});
});
describe('default branch handling', () => {
const defaultBranchName = 'my-branch';
beforeEach(() => {
branchesApiCallSpy = jest.fn().mockReturnValue([HTTP_STATUS_OK, []]); // Mock branches API without the default branch
});
it('adds default branch to dropdown when not in initial API response', async () => {
createComponent({ propsData: { defaultBranch: defaultBranchName } });
await waitForRequests();
const defaultBranchItem = findBranchDropdownItems().filter(
(item) => item.text().includes(defaultBranchName) && item.text().includes('default'),
);
expect(defaultBranchItem.length).toBe(1);
});
});
});

View File

@ -27,6 +27,8 @@ const defaultMockRoute = {
},
};
const mockRootRef = 'root-ref';
describe('HeaderArea', () => {
let wrapper;
@ -50,7 +52,9 @@ describe('HeaderArea', () => {
const createComponent = ({
props = {},
route = { name: 'blobPathDecoded' },
provided = {},
provided = {
rootRef: mockRootRef,
},
} = {}) => {
return shallowMountExtended(HeaderArea, {
provide: {
@ -92,7 +96,7 @@ describe('HeaderArea', () => {
describe('Ref selector', () => {
it('renders correctly', () => {
expect(findRefSelector().exists()).toBe(true);
expect(findRefSelector().props('defaultBranch')).toBe(mockRootRef);
});
it('renders correctly when branch names ending with .json', () => {

View File

@ -1,87 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Snippet Description Edit component rendering matches the snapshot 1`] = `
<div
class="form-group"
>
<label
for="reference-0"
>
Description (optional)
</label>
<div
class="gfm-form js-vue-markdown-field md-area position-relative"
data-uploads-path=""
>
<markdown-header-stub
data-testid="markdownHeader"
enablepreview="true"
linecontent=""
markdownpreviewpath="foo/"
newcommenttemplatepathsprop=""
restrictedtoolbaritems=""
suggestionstartindex="0"
uploadspath=""
/>
<div
class="md-write-holder"
>
<div
class="div-dropzone-wrapper zen-backdrop"
>
<div
class="div-dropzone js-invalid-dropzone"
>
<textarea
aria-label="Description"
class="!gl-min-h-0 js-autosize js-gfm-input js-gfm-input-initialized markdown-area note-textarea"
data-supports-quick-actions="false"
data-testid="snippet-description-field"
dir="auto"
id="reference-0"
placeholder="Describe what your snippet does or how to use it…"
rows="3"
style="overflow-x: hidden; word-wrap: break-word;"
/>
<div
class="div-dropzone-hover"
>
<svg
class="div-dropzone-icon s24"
>
<use
xlink:href="undefined#paperclip"
/>
</svg>
</div>
</div>
<a
aria-label="Exit full screen"
class="btn-default-tertiary btn-icon btn-sm gl-button js-zen-leave zen-control zen-control-leave"
href="#"
title="Exit full screen"
>
<gl-icon-stub
name="minimize"
size="24"
variant="subtle"
/>
</a>
<markdown-toolbar-stub
canattachfile="true"
markdowndocspath="help/"
showcommenttoolbar="true"
/>
</div>
</div>
<div
class="gl-px-5 js-vue-md-preview md-preview-holder"
style="display: none;"
>
<div
class="md"
/>
</div>
</div>
</div>
`;

View File

@ -27,10 +27,6 @@ describe('Snippet Description Edit component', () => {
});
describe('rendering', () => {
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('renders the description field', () => {
createComponent('');

View File

@ -15,6 +15,7 @@ import WorkItemDescriptionTemplatesListbox from '~/work_items/components/work_it
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
import workItemDescriptionTemplateQuery from '~/work_items/graphql/work_item_description_template.query.graphql';
import projectPermissionsQuery from '~/work_items/graphql/ai_permissions_for_project.query.graphql';
import { autocompleteDataSources, markdownPreviewPath, newWorkItemId } from '~/work_items/utils';
import { ROUTES, NEW_WORK_ITEM_IID, NEW_WORK_ITEM_GID } from '~/work_items/constants';
import {
@ -60,6 +61,22 @@ describe('WorkItemDescription', () => {
},
});
const mockWorkspacePermissionsResponse = {
data: {
workspace: {
id: 'gid://gitlab/Project/1',
userPermissions: {
generateDescription: true,
},
__typename: 'Project',
},
},
};
const mockWorkspacePermissionsHandler = jest
.fn()
.mockResolvedValue(mockWorkspacePermissionsResponse);
const mockFullPath = 'test-project-path';
const createComponent = async ({
@ -81,6 +98,7 @@ describe('WorkItemDescription', () => {
routeQuery = {},
fullPath = mockFullPath,
hideFullscreenMarkdownButton = false,
workspacePermissionsHandler = mockWorkspacePermissionsHandler,
} = {}) => {
router = {
replace: jest.fn(),
@ -91,6 +109,7 @@ describe('WorkItemDescription', () => {
[workItemByIidQuery, workItemResponseHandler],
[updateWorkItemMutation, mutationHandler],
[workItemDescriptionTemplateQuery, descriptionTemplateHandler],
[projectPermissionsQuery, workspacePermissionsHandler],
]),
propsData: {
fullPath,
@ -798,4 +817,103 @@ describe('WorkItemDescription', () => {
expect(findMarkdownEditor().props('restrictedToolBarItems')).toEqual(['full-screen']);
});
});
describe('workspacePermissions query', () => {
it('is not called for groups', async () => {
const workspacePermissionsHandler = jest
.fn()
.mockResolvedValue(mockWorkspacePermissionsResponse);
await createComponent({
isGroup: true,
isEditing: true,
workspacePermissionsHandler,
});
expect(workspacePermissionsHandler).not.toHaveBeenCalled();
});
it('is called for projects', async () => {
const workspacePermissionsHandler = jest
.fn()
.mockResolvedValue(mockWorkspacePermissionsResponse);
await createComponent({
isGroup: false,
isEditing: true,
workspacePermissionsHandler,
});
expect(workspacePermissionsHandler).toHaveBeenCalledWith({
fullPath: mockFullPath,
});
});
describe('user has generateDescription permission', () => {
beforeEach(async () => {
const workspacePermissionsHandler = jest
.fn()
.mockResolvedValue(mockWorkspacePermissionsResponse);
await createComponent({
isGroup: false,
isEditing: true,
workspacePermissionsHandler,
});
});
it('passes editorAiActions prop to MarkdownEditor', () => {
const editorAiActions = findMarkdownEditor().props('editorAiActions');
expect(editorAiActions).toHaveLength(1);
});
});
describe('user does not have generateDescription permission', () => {
beforeEach(async () => {
const workspacePermissionsHandlerNoPermission = jest.fn().mockResolvedValue({
data: {
workspace: {
id: 'gid://gitlab/Project/1',
userPermissions: {
generateDescription: false,
},
__typename: 'Project',
},
},
});
await createComponent({
isGroup: false,
isEditing: true,
workspacePermissionsHandler: workspacePermissionsHandlerNoPermission,
});
});
it('passes empty editorAiActions prop to MarkdownEditor', () => {
const editorAiActions = findMarkdownEditor().props('editorAiActions');
expect(editorAiActions).toHaveLength(0);
});
});
describe('when workspace is null', () => {
beforeEach(async () => {
const workspacePermissionsHandlerNullWorkspace = jest.fn().mockResolvedValue({
data: {
workspace: null,
},
});
await createComponent({
isGroup: false,
isEditing: true,
workspacePermissionsHandler: workspacePermissionsHandlerNullWorkspace,
});
});
it('passes empty editorAiActions prop to MarkdownEditor', () => {
const editorAiActions = findMarkdownEditor().props('editorAiActions');
expect(editorAiActions).toHaveLength(0);
});
});
});
});

View File

@ -5342,13 +5342,24 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
it { is_expected.to eq false }
end
context 'when metadata does not exist' do
context 'when metadata does not exist but job is not degenerated' do
before do
build.metadata.destroy!
# Very old jobs populated this column instead of metadata
build.update_column(:options, { my_config: 'value' })
build.metadata.delete
build.reload
end
it { is_expected.to eq false }
end
context 'when job is degenerated' do
before do
build.degenerate!
end
it { is_expected.to eq true }
end
end
describe '#drop_with_exit_code!' do

View File

@ -102,15 +102,45 @@ RSpec.describe Ci::Slsa::ProvenanceStatement, type: :model, feature_category: :c
expect(subject[0]['digest']['sha256']).to eq('3d4a07bcbf2eaec380ad707451832924bee1197fbdf43d20d6d4bc96c8284268')
end
it 'has the correct predicate build definition' do
build_definition = parsed['predicate']['buildDefinition']
context 'when a build definition is generated' do
let(:build_definition) { parsed['predicate']['buildDefinition'] }
# TODO: update buildType as part of https://gitlab.com/gitlab-org/gitlab/-/issues/426764
expect(build_definition['buildType']).to eq('https://gitlab.com/gitlab-org/gitlab/-/issues/546150')
expect(build_definition['externalParameters']['variables']).to include("GITLAB_CI")
expect(build_definition['internalParameters']['name']).to start_with("My runner")
it 'has the correct predicate build definition' do
# TODO: update buildType as part of https://gitlab.com/gitlab-org/gitlab/-/issues/426764
expect(build_definition['buildType']).to eq('https://gitlab.com/gitlab-org/gitlab/-/issues/546150')
expect(build_definition['internalParameters']['name']).to start_with("My runner")
expect(build_definition['resolvedDependencies'].length).to eq(1)
end
expect(build_definition['resolvedDependencies'].length).to eq(1)
it 'has the correct external parameters' do
statement_variables = build_definition['externalParameters']['variables']
expect(statement_variables).to be_an_instance_of(Hash)
expect(statement_variables.length).to eq(build.variables.to_a.length)
non_masked = build.variables.filter { |variable| !variable.masked? }.map(&:key)
masked = build.variables.filter(&:masked?).map(&:key)
expect(non_masked.length).to be > 1
expect(masked.length).to be > 1
non_masked.each do |variable|
expect(statement_variables[variable]).to eq(build.variables[variable].value)
end
masked.each do |variable|
expect(statement_variables[variable]).to eq("[MASKED]")
end
end
it 'has the right entry point' do
entry_point = build_definition['externalParameters']['entryPoint']
expect(entry_point).to eq('test')
end
it 'has the right source' do
source = build_definition['externalParameters']['source']
expect(source).to eq(Gitlab::Routing.url_helpers.project_url(build.project))
end
end
it 'has the correct run details' do
@ -159,4 +189,27 @@ RSpec.describe Ci::Slsa::ProvenanceStatement, type: :model, feature_category: :c
end
end
end
describe '#deep_change_case' do
subject(:provenance_statement) { create(:provenance_statement) }
it 'camelizes fields appropriately' do
expect(parsed).to include('predicateType')
expect(parsed).to include('predicate')
end
it 'does not camelize exceptions' do
expect(parsed).to include('_type')
end
it 'camelizes recursively' do
expect(parsed['predicate']).to include('buildDefinition')
expect(parsed['predicate']['buildDefinition']).to include('buildType')
expect(parsed['predicate']['buildDefinition']).to include('externalParameters')
end
it 'does not recurse through exception keys' do
expect(parsed['predicate']['buildDefinition']['externalParameters']['variables']).to include('CI_PIPELINE')
end
end
end

View File

@ -63,6 +63,14 @@ RSpec.describe Admin::ProjectsController, "routing" do
expect(get("/admin/projects")).to route_to('admin/projects#index')
end
it "to #active" do
expect(get("/admin/projects/active")).to route_to('admin/projects#index')
end
it "to #inactive" do
expect(get("/admin/projects/inactive")).to route_to('admin/projects#index')
end
it "to #show" do
expect(get("/admin/projects/gitlab/gitlab-ce")).to route_to('admin/projects#show', namespace_id: 'gitlab', id: 'gitlab-ce')
expect(get("/admin/projects/gitlab/subgroup/gitlab-ce")).to route_to('admin/projects#show', namespace_id: 'gitlab/subgroup', id: 'gitlab-ce')