Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5210daf07e
commit
d8a3221aa3
|
|
@ -3,7 +3,7 @@ extends:
|
|||
- plugin:@gitlab/i18n
|
||||
- plugin:no-jquery/slim
|
||||
- plugin:no-jquery/deprecated-3.4
|
||||
- plugin:no-unsanitized/DOM
|
||||
- plugin:no-unsanitized/recommended-legacy
|
||||
- ./tooling/eslint-config/conditionally_ignore.js
|
||||
globals:
|
||||
__webpack_public_path__: true
|
||||
|
|
@ -164,11 +164,11 @@ rules:
|
|||
no-unsanitized/method:
|
||||
- error
|
||||
- escape:
|
||||
methods: 'sanitize'
|
||||
methods: ['sanitize']
|
||||
no-unsanitized/property:
|
||||
- error
|
||||
- escape:
|
||||
methods: 'sanitize'
|
||||
methods: ['sanitize']
|
||||
# This rule will be enabled later.
|
||||
unicorn/no-array-callback-reference: off
|
||||
vue/no-undef-components:
|
||||
|
|
@ -234,17 +234,17 @@ overrides:
|
|||
no-restricted-imports:
|
||||
- error
|
||||
- paths:
|
||||
- name: mousetrap
|
||||
message: 'Import { Mousetrap } from ~/lib/mousetrap instead.'
|
||||
- name: vuex
|
||||
message: 'See our documentation on "Migrating from VueX" for tips on how to avoid adding new VueX stores.'
|
||||
- name: '@sentry/browser'
|
||||
message: Use "import * as Sentry from '~/sentry/sentry_browser_wrapper';" instead
|
||||
- name: ~/locale
|
||||
importNames:
|
||||
- __
|
||||
- s__
|
||||
message: 'Do not externalize strings in specs: https://docs.gitlab.com/ee/development/i18n/externalization.html#test-files-jest'
|
||||
- name: mousetrap
|
||||
message: 'Import { Mousetrap } from ~/lib/mousetrap instead.'
|
||||
- name: vuex
|
||||
message: 'See our documentation on "Migrating from VueX" for tips on how to avoid adding new VueX stores.'
|
||||
- name: '@sentry/browser'
|
||||
message: Use "import * as Sentry from '~/sentry/sentry_browser_wrapper';" instead
|
||||
- name: ~/locale
|
||||
importNames:
|
||||
- __
|
||||
- s__
|
||||
message: 'Do not externalize strings in specs: https://docs.gitlab.com/ee/development/i18n/externalization.html#test-files-jest'
|
||||
- files:
|
||||
- 'config/**/*'
|
||||
- 'scripts/**/*'
|
||||
|
|
|
|||
|
|
@ -1,106 +0,0 @@
|
|||
.dast_conf:
|
||||
tags:
|
||||
- prm
|
||||
# For scheduling dast job
|
||||
extends:
|
||||
- .reports:rules:schedule-dast
|
||||
image:
|
||||
name: "${CI_TEMPLATE_REGISTRY_HOST}/security-products/dast:$DAST_VERSION"
|
||||
resource_group: dast_scan
|
||||
variables:
|
||||
DAST_USERNAME_FIELD: "name:user[login]"
|
||||
DAST_PASSWORD_FIELD: "name:user[password]"
|
||||
DAST_SUBMIT_FIELD: "css:.js-sign-in-button"
|
||||
DAST_FULL_SCAN_ENABLED: "true"
|
||||
DAST_VERSION: 3
|
||||
GIT_STRATEGY: none
|
||||
# -Xmx is used to set the JVM memory to 6GB to prevent DAST OutOfMemoryError.
|
||||
DAST_ZAP_CLI_OPTIONS: "-Xmx6144m"
|
||||
before_script:
|
||||
- 'export DAST_WEBSITE="${DAST_WEBSITE:-$(cat environment_url.txt)}"'
|
||||
- 'export DAST_AUTH_URL="${DAST_WEBSITE}/users/sign_in"'
|
||||
- 'export DAST_PASSWORD="${REVIEW_APPS_ROOT_PASSWORD}"'
|
||||
# Help pages are excluded from scan as they are static pages.
|
||||
# profile/two_factor_auth is excluded from scan to prevent 2FA from being turned on from user profile, which will reduce coverage.
|
||||
- 'DAST_EXCLUDE_URLS="${DAST_WEBSITE}/help/.*,${DAST_WEBSITE}/-/profile/two_factor_auth,${DAST_WEBSITE}/users/sign_out"'
|
||||
# Exclude the automatically generated monitoring project from being tested due to https://gitlab.com/gitlab-org/gitlab/-/issues/260362
|
||||
- 'export DAST_EXCLUDE_URLS="${DAST_EXCLUDE_URLS},${DAST_WEBSITE}/gitlab-instance-.*"'
|
||||
needs: ["review-deploy"]
|
||||
stage: dast
|
||||
# Default job timeout set to 90m and dast rules needs 2h to so that it won't timeout.
|
||||
timeout: 3h
|
||||
# Add retry because of intermittent connection problems. See https://gitlab.com/gitlab-org/gitlab/-/issues/244313
|
||||
retry: 1
|
||||
artifacts:
|
||||
paths:
|
||||
- gl-dast-report.json # GitLab-specific
|
||||
reports:
|
||||
dast: gl-dast-report.json
|
||||
expire_in: 1 week # GitLab-specific
|
||||
allow_failure: true
|
||||
|
||||
# DAST scan with a subset of Release scan rules.
|
||||
# ZAP rule details can be found at https://www.zaproxy.org/docs/alerts/
|
||||
|
||||
dast:anti-clickjacking-header:
|
||||
extends:
|
||||
- .dast_conf
|
||||
variables:
|
||||
DAST_USERNAME: "user1"
|
||||
DAST_ONLY_INCLUDE_RULES: "10020"
|
||||
script:
|
||||
- /analyze
|
||||
|
||||
dast:xss-persistant:
|
||||
extends:
|
||||
- .dast_conf
|
||||
variables:
|
||||
DAST_USERNAME: "user2"
|
||||
DAST_ONLY_INCLUDE_RULES: "40014"
|
||||
script:
|
||||
- /analyze
|
||||
|
||||
dast:insecure-http-method:
|
||||
extends:
|
||||
- .dast_conf
|
||||
variables:
|
||||
DAST_USERNAME: "user3"
|
||||
DAST_ONLY_INCLUDE_RULES: "90028"
|
||||
script:
|
||||
- /analyze
|
||||
|
||||
dast:server-side-template-inj:
|
||||
extends:
|
||||
- .dast_conf
|
||||
variables:
|
||||
DAST_USERNAME: "user4"
|
||||
DAST_ONLY_INCLUDE_RULES: "90035"
|
||||
script:
|
||||
- /analyze
|
||||
|
||||
dast:server-side-template-inj-blind:
|
||||
extends:
|
||||
- .dast_conf
|
||||
variables:
|
||||
DAST_USERNAME: "user5"
|
||||
DAST_ONLY_INCLUDE_RULES: "90035"
|
||||
script:
|
||||
- /analyze
|
||||
|
||||
dast:session-fixation:
|
||||
extends:
|
||||
- .dast_conf
|
||||
variables:
|
||||
DAST_USERNAME: "user6"
|
||||
DAST_ONLY_INCLUDE_RULES: "40013"
|
||||
script:
|
||||
- /analyze
|
||||
|
||||
dast:xss-dombased:
|
||||
extends:
|
||||
- .dast_conf
|
||||
variables:
|
||||
DAST_USERNAME: "user10"
|
||||
DAST_ONLY_INCLUDE_RULES: "40026"
|
||||
script:
|
||||
- /analyze
|
||||
|
|
@ -14,7 +14,6 @@ include:
|
|||
- local: .gitlab/ci/global.gitlab-ci.yml
|
||||
- local: .gitlab/ci/review-apps/rules.gitlab-ci.yml
|
||||
- local: .gitlab/ci/review-apps/qa.gitlab-ci.yml
|
||||
- local: .gitlab/ci/review-apps/dast.gitlab-ci.yml
|
||||
- local: .gitlab/ci/review-apps/dast-api.gitlab-ci.yml
|
||||
|
||||
.base-before_script: &base-before_script
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ workflow:
|
|||
QA_CAN_TEST_PRAEFECT: "false"
|
||||
QA_ALLOW_LOCAL_REQUESTS: "true"
|
||||
QA_SUITE_STATUS_ENV_FILE: $CI_PROJECT_DIR/suite_status.env
|
||||
# Force color output for cng orchestrator
|
||||
CLICOLOR_FORCE: 1
|
||||
# disable selective test execution until pipeline setup is implemented to support it correctly
|
||||
KNAPSACK_TEST_FILE_PATTERN: ""
|
||||
QA_TESTS: ""
|
||||
|
|
|
|||
2
Gemfile
2
Gemfile
|
|
@ -155,7 +155,7 @@ gem 'grape-path-helpers', '~> 2.0.1', feature_category: :api
|
|||
gem 'rack-cors', '~> 2.0.1', require: 'rack/cors' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
# GraphQL API
|
||||
gem 'graphql', '~> 2.3.5', feature_category: :api
|
||||
gem 'graphql', '~> 2.3.14', feature_category: :api
|
||||
gem 'graphql-docs', '~> 5.0.0', group: [:development, :test], feature_category: :api
|
||||
gem 'graphiql-rails', '~> 1.10', feature_category: :api
|
||||
gem 'apollo_upload_server', '~> 2.1.6', feature_category: :api
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@
|
|||
{"name":"graphiql-rails","version":"1.10.0","platform":"ruby","checksum":"b557f989a737c8b9e985142609bec52fb1e9393a701eb50e02a7c14422891040"},
|
||||
{"name":"graphlient","version":"0.8.0","platform":"ruby","checksum":"98c408da1d083454e9f5e274f3b0b6261e2a0c2b5f2ed7b3ef9441d46f8e7cb1"},
|
||||
{"name":"graphlyte","version":"1.0.0","platform":"ruby","checksum":"b5af4ab67dde6e961f00ea1c18f159f73b52ed11395bb4ece297fe628fa1804d"},
|
||||
{"name":"graphql","version":"2.3.5","platform":"ruby","checksum":"9c367835f86541660d24c3d81632267ecee553d304577aaee070f8ac05860af1"},
|
||||
{"name":"graphql","version":"2.3.14","platform":"ruby","checksum":"1781f33ab52f250f7bd6082f40ef15363d6acf98009b7acba70d54bee142f295"},
|
||||
{"name":"graphql-client","version":"0.23.0","platform":"ruby","checksum":"f238b8e451676baad06bd15f95396e018192243dcf12c4e6d13fb41d9a2babc1"},
|
||||
{"name":"graphql-docs","version":"5.0.0","platform":"ruby","checksum":"76baca6e5a803a4b6a9fbbbfdbf16742b7c4c546c8592b6e1a7aa4e79e562d04"},
|
||||
{"name":"grpc","version":"1.63.0","platform":"aarch64-linux","checksum":"dc75c5fd570b819470781d9512105dddfdd11d984f38b8e60bb946f92d1f79ee"},
|
||||
|
|
|
|||
|
|
@ -906,8 +906,9 @@ GEM
|
|||
faraday (~> 2.0)
|
||||
graphql-client
|
||||
graphlyte (1.0.0)
|
||||
graphql (2.3.5)
|
||||
graphql (2.3.14)
|
||||
base64
|
||||
fiber-storage
|
||||
graphql-client (0.23.0)
|
||||
activesupport (>= 3.0)
|
||||
graphql (>= 1.13.0)
|
||||
|
|
@ -2103,7 +2104,7 @@ DEPENDENCIES
|
|||
graphiql-rails (~> 1.10)
|
||||
graphlient (~> 0.8.0)
|
||||
graphlyte (~> 1.0.0)
|
||||
graphql (~> 2.3.5)
|
||||
graphql (~> 2.3.14)
|
||||
graphql-docs (~> 5.0.0)
|
||||
grpc (= 1.63.0)
|
||||
gssapi (~> 1.3.1)
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@
|
|||
import FILTERED_SVG_URL from '@gitlab/svgs/dist/illustrations/empty-state/empty-search-md.svg?url';
|
||||
|
||||
import { GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import EMPTY_SVG_URL from '@gitlab/svgs/dist/illustrations/empty-state/empty-catalog-md.svg';
|
||||
import { s__ } from '~/locale';
|
||||
import { COMPONENTS_DOCS_URL } from '~/ci/catalog/constants';
|
||||
|
||||
export default {
|
||||
name: 'CiCatalogEmptyState',
|
||||
COMPONENTS_DOCS_URL,
|
||||
EMPTY_SVG_URL,
|
||||
components: {
|
||||
GlEmptyState,
|
||||
GlLink,
|
||||
|
|
@ -77,6 +79,7 @@ export default {
|
|||
v-else
|
||||
:title="$options.i18n.default.title"
|
||||
:description="$options.i18n.default.description"
|
||||
:svg-path="$options.EMPTY_SVG_URL"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ export const getTimeago = (formatName) =>
|
|||
export const localTimeAgo = (elements, updateTooltip = true) => {
|
||||
const { format } = getTimeago();
|
||||
elements.forEach((el) => {
|
||||
el.innerText = format(el.dateTime, timeagoLanguageCode);
|
||||
el.innerText = format(newDate(el.dateTime), timeagoLanguageCode);
|
||||
});
|
||||
|
||||
if (!updateTooltip) {
|
||||
|
|
|
|||
|
|
@ -11,3 +11,15 @@ export const semverRegex =
|
|||
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
||||
|
||||
export const noSpacesRegex = /^\S+$/;
|
||||
|
||||
/**
|
||||
* Checks if a string contains potential regular expression elements.
|
||||
*
|
||||
* @param {string} str - The string to check for potential regex elements.
|
||||
* @returns {boolean} - Returns true if the string contains regex elements, otherwise false.
|
||||
*/
|
||||
|
||||
export const containsPotentialRegex = (str) => {
|
||||
const regexPattern = /[*+?^${}()|[\]\\]/;
|
||||
return regexPattern.test(str);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
GlSprintf,
|
||||
} from '@gitlab/ui';
|
||||
import { debounce, intersectionWith, groupBy, differenceBy, intersectionBy } from 'lodash';
|
||||
import glAbilitiesMixin from '~/vue_shared/mixins/gl_abilities_mixin';
|
||||
import { createAlert } from '~/alert';
|
||||
import { __, s__, n__ } from '~/locale';
|
||||
import { getUsers, getGroups, getDeployKeys } from '../api/access_dropdown_api';
|
||||
|
|
@ -35,6 +36,7 @@ export default {
|
|||
GlAvatar,
|
||||
GlSprintf,
|
||||
},
|
||||
mixins: [glAbilitiesMixin()],
|
||||
props: {
|
||||
accessLevelsData: {
|
||||
type: Array,
|
||||
|
|
@ -187,6 +189,9 @@ export default {
|
|||
...this.getDataForSave(LEVEL_TYPES.DEPLOY_KEY, 'deploy_key_id'),
|
||||
];
|
||||
},
|
||||
canAdminContainer() {
|
||||
return this.glAbilities.adminProject || this.glAbilities.adminGroup;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
query: debounce(function debouncedSearch() {
|
||||
|
|
@ -226,29 +231,45 @@ export default {
|
|||
focusInput() {
|
||||
this.$refs.search?.focusInput();
|
||||
},
|
||||
getGroups() {
|
||||
return this.groups.length
|
||||
? Promise.resolve({ data: this.groups })
|
||||
: getGroups({ withProjectAccess: this.groupsWithProjectAccess });
|
||||
},
|
||||
getData({ initial = false } = {}) {
|
||||
this.initialLoading = initial;
|
||||
this.loading = true;
|
||||
|
||||
if (this.hasLicense) {
|
||||
Promise.all([
|
||||
getDeployKeys(this.query),
|
||||
getUsers(this.query),
|
||||
this.groups.length
|
||||
? Promise.resolve({ data: this.groups })
|
||||
: getGroups({ withProjectAccess: this.groupsWithProjectAccess }),
|
||||
])
|
||||
.then(([deployKeysResponse, usersResponse, groupsResponse]) => {
|
||||
this.consolidateData(deployKeysResponse.data, usersResponse.data, groupsResponse.data);
|
||||
this.setSelected({ initial });
|
||||
})
|
||||
.catch(() =>
|
||||
createAlert({ message: __('Failed to load groups, users and deploy keys.') }),
|
||||
)
|
||||
.finally(() => {
|
||||
this.initialLoading = false;
|
||||
this.loading = false;
|
||||
});
|
||||
if (this.canAdminContainer) {
|
||||
Promise.all([getDeployKeys(this.query), getUsers(this.query), this.getGroups()])
|
||||
.then(([deployKeysResponse, usersResponse, groupsResponse]) => {
|
||||
this.consolidateData(
|
||||
deployKeysResponse.data,
|
||||
usersResponse.data,
|
||||
groupsResponse.data,
|
||||
);
|
||||
this.setSelected({ initial });
|
||||
})
|
||||
.catch(() =>
|
||||
createAlert({ message: __('Failed to load groups, users and deploy keys.') }),
|
||||
)
|
||||
.finally(() => {
|
||||
this.initialLoading = false;
|
||||
this.loading = false;
|
||||
});
|
||||
} else if (this.glAbilities.adminProtectedBranch) {
|
||||
Promise.all([getUsers(this.query), this.getGroups()])
|
||||
.then(([usersResponse, groupsResponse]) => {
|
||||
this.consolidateData(null, usersResponse.data, groupsResponse.data);
|
||||
this.setSelected({ initial });
|
||||
})
|
||||
.catch(() => createAlert({ message: __('Failed to load groups and users.') }))
|
||||
.finally(() => {
|
||||
this.initialLoading = false;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
getDeployKeys(this.query)
|
||||
.then((deployKeysResponse) => {
|
||||
|
|
@ -284,27 +305,31 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
this.deployKeys = deployKeysResponse.map((response) => {
|
||||
const {
|
||||
id,
|
||||
fingerprint,
|
||||
fingerprint_sha256: fingerprintSha256,
|
||||
title,
|
||||
owner: { avatar_url, name, username },
|
||||
} = response;
|
||||
if (this.canAdminContainer) {
|
||||
this.deployKeys = deployKeysResponse.map((response) => {
|
||||
const {
|
||||
id,
|
||||
fingerprint,
|
||||
fingerprint_sha256: fingerprintSha256,
|
||||
title,
|
||||
owner: { avatar_url, name, username },
|
||||
} = response;
|
||||
|
||||
const availableFingerprint = fingerprintSha256 || fingerprint;
|
||||
const shortFingerprint = `(${availableFingerprint.substring(0, 14)}...)`;
|
||||
const availableFingerprint = fingerprintSha256 || fingerprint;
|
||||
const shortFingerprint = `(${availableFingerprint.substring(0, 14)}...)`;
|
||||
|
||||
return {
|
||||
id,
|
||||
title: title.concat(' ', shortFingerprint),
|
||||
avatar_url,
|
||||
fullname: name,
|
||||
username,
|
||||
type: LEVEL_TYPES.DEPLOY_KEY,
|
||||
};
|
||||
});
|
||||
return {
|
||||
id,
|
||||
title: title.concat(' ', shortFingerprint),
|
||||
avatar_url,
|
||||
fullname: name,
|
||||
username,
|
||||
type: LEVEL_TYPES.DEPLOY_KEY,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
this.deployKeys = [];
|
||||
}
|
||||
},
|
||||
setSelected({ initial } = {}) {
|
||||
if (initial) {
|
||||
|
|
|
|||
|
|
@ -143,13 +143,26 @@ export default class ProtectedBranchCreate {
|
|||
});
|
||||
}
|
||||
|
||||
createLimitedSuccessAlert() {
|
||||
this.alert = createAlert({
|
||||
variant: VARIANT_SUCCESS,
|
||||
containerSelector: '.js-alert-protected-branch-created-container',
|
||||
message: s__('ProtectedBranch|Protected branch was sucessfully created'),
|
||||
});
|
||||
}
|
||||
|
||||
showSuccessAlertIfNeeded() {
|
||||
if (!this.hasProtectedBranchSuccessAlert()) {
|
||||
return;
|
||||
}
|
||||
this.expandAndScroll(PROTECTED_BRANCHES_ANCHOR);
|
||||
|
||||
this.createSuccessAlert();
|
||||
if (gon.abilities.adminProject || gon.abilities.adminGroup) {
|
||||
this.createSuccessAlert();
|
||||
} else {
|
||||
this.createLimitedSuccessAlert();
|
||||
}
|
||||
|
||||
localStorage.removeItem(IS_PROTECTED_BRANCH_CREATED);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
position: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -40,6 +44,7 @@ export default {
|
|||
if (this.showMore) {
|
||||
return file.chunks;
|
||||
}
|
||||
|
||||
return file.chunks.slice(0, DEFAULT_SHOW_CHUNKS);
|
||||
},
|
||||
},
|
||||
|
|
@ -53,7 +58,12 @@ export default {
|
|||
:key="`chunk${index}`"
|
||||
class="chunks-block gl-border-b gl-border-subtle last:gl-border-0"
|
||||
>
|
||||
<blob-chunks :chunk="chunk" :blame-link="file.blameUrl" :file-url="file.fileUrl" />
|
||||
<blob-chunks
|
||||
:chunk="chunk"
|
||||
:blame-link="file.blameUrl"
|
||||
:file-url="file.fileUrl"
|
||||
:position="position"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,13 @@
|
|||
import { GlTooltipDirective, GlIcon, GlLink } from '@gitlab/ui';
|
||||
import GlSafeHtmlDirective from '~/vue_shared/directives/safe_html';
|
||||
import { s__ } from '~/locale';
|
||||
import { InternalEvents } from '~/tracking';
|
||||
import {
|
||||
EVENT_CLICK_BLOB_RESULT_LINE,
|
||||
EVENT_CLICK_BLOB_RESULT_BLAME_LINE,
|
||||
} from '~/search/results/tracking';
|
||||
|
||||
const trackingMixin = InternalEvents.mixin();
|
||||
|
||||
export default {
|
||||
name: 'BlobChunks',
|
||||
|
|
@ -13,6 +20,7 @@ export default {
|
|||
GlTooltip: GlTooltipDirective,
|
||||
SafeHtml: GlSafeHtmlDirective,
|
||||
},
|
||||
mixins: [trackingMixin],
|
||||
i18n: {
|
||||
viewBlame: s__('GlobalSearch|View blame'),
|
||||
viewLine: s__('GlobalSearch|View line in repository'),
|
||||
|
|
@ -32,6 +40,10 @@ export default {
|
|||
required: false,
|
||||
default: '',
|
||||
},
|
||||
position: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
codeTheme() {
|
||||
|
|
@ -42,6 +54,18 @@ export default {
|
|||
highlightedRichText(richText) {
|
||||
return richText.replace('<b>', '<b class="hll">');
|
||||
},
|
||||
trackLineClick(lineNumber) {
|
||||
this.trackEvent(EVENT_CLICK_BLOB_RESULT_LINE, {
|
||||
property: lineNumber,
|
||||
value: this.position,
|
||||
});
|
||||
},
|
||||
trackBlameClick(lineNumber) {
|
||||
this.trackEvent(EVENT_CLICK_BLOB_RESULT_BLAME_LINE, {
|
||||
property: lineNumber,
|
||||
value: this.position,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -63,6 +87,8 @@ export default {
|
|||
:href="`${blameLink}#L${line.lineNumber}`"
|
||||
:title="$options.i18n.viewBlame"
|
||||
class="js-navigation-open"
|
||||
data-testid="search-blob-line-blame-link"
|
||||
@click="trackBlameClick(line.lineNumber)"
|
||||
><gl-icon name="git"
|
||||
/></gl-link>
|
||||
</span>
|
||||
|
|
@ -72,6 +98,8 @@ export default {
|
|||
:href="`${fileUrl}#L${line.lineNumber}`"
|
||||
:title="$options.i18n.viewLine"
|
||||
class="!gl-flex gl-items-center gl-justify-end"
|
||||
data-testid="search-blob-line-link"
|
||||
@click="trackLineClick(line.lineNumber)"
|
||||
>{{ line.lineNumber }}</gl-link
|
||||
>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@
|
|||
import { GlSprintf, GlButton, GlLink } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import { DEFAULT_FETCH_CHUNKS, DEFAULT_SHOW_CHUNKS } from '~/search/results/constants';
|
||||
import { EVENT_CLICK_BLOB_RESULTS_SHOW_MORE_LESS } from '~/search/results/tracking';
|
||||
import { InternalEvents } from '~/tracking';
|
||||
import eventHub from '../event_hub';
|
||||
|
||||
const trackingMixin = InternalEvents.mixin();
|
||||
|
||||
export default {
|
||||
name: 'BlobFooter',
|
||||
components: {
|
||||
|
|
@ -11,6 +15,7 @@ export default {
|
|||
GlButton,
|
||||
GlLink,
|
||||
},
|
||||
mixins: [trackingMixin],
|
||||
i18n: {
|
||||
showMore: s__('GlobalSearch|Show %{matches} more matches'),
|
||||
showLess: s__('GlobalSearch|Show less'),
|
||||
|
|
@ -23,6 +28,10 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
position: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -68,6 +77,10 @@ export default {
|
|||
id: `${this.projectPath}:${this.filePath}`,
|
||||
state: (this.showMore = !this.showMore),
|
||||
});
|
||||
this.trackEvent(EVENT_CLICK_BLOB_RESULTS_SHOW_MORE_LESS, {
|
||||
label: `${this.position}`,
|
||||
property: this.showMore ? 'open' : 'close',
|
||||
});
|
||||
},
|
||||
},
|
||||
DEFAULT_FETCH_CHUNKS,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,17 @@
|
|||
<script>
|
||||
import { GlLink } from '@gitlab/ui';
|
||||
import { GlLink, GlLabel } from '@gitlab/ui';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { mapState } from 'vuex';
|
||||
import GlSafeHtmlDirective from '~/vue_shared/directives/safe_html';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
import FileIcon from '~/vue_shared/components/file_icon.vue';
|
||||
import { s__ } from '~/locale';
|
||||
import { InternalEvents } from '~/tracking';
|
||||
import { containsPotentialRegex } from '~/lib/utils/regexp';
|
||||
import { EVENT_CLICK_CLIPBOARD_BUTTON, EVENT_CLICK_HEADER_LINK } from '~/search/results/tracking';
|
||||
import { DEFAULT_THEME_COLOR, DEFAULT_HEADER_LABEL_COLOR } from '../constants';
|
||||
|
||||
const trackingMixin = InternalEvents.mixin();
|
||||
|
||||
export default {
|
||||
name: 'BlobHeader',
|
||||
|
|
@ -10,7 +19,12 @@ export default {
|
|||
FileIcon,
|
||||
ClipboardButton,
|
||||
GlLink,
|
||||
GlLabel,
|
||||
},
|
||||
directives: {
|
||||
SafeHtml: GlSafeHtmlDirective,
|
||||
},
|
||||
mixins: [trackingMixin],
|
||||
props: {
|
||||
filePath: {
|
||||
type: String,
|
||||
|
|
@ -26,29 +40,72 @@ export default {
|
|||
required: false,
|
||||
default: '',
|
||||
},
|
||||
isHeaderOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
fileLink: s__('GlobalSearch|Open file in repository'),
|
||||
isHeaderOnly: s__('GlobalSearch|File name match only'),
|
||||
},
|
||||
computed: {
|
||||
...mapState(['query']),
|
||||
gfmCopyText() {
|
||||
return `\`${this.filePath}\``;
|
||||
},
|
||||
highlightedFilePath() {
|
||||
if (!this?.query?.search) {
|
||||
return this.filePath;
|
||||
}
|
||||
|
||||
if (containsPotentialRegex(this.query.search)) {
|
||||
return this.filePath;
|
||||
}
|
||||
|
||||
const regex = new RegExp(`(${this.query.search})`, 'g');
|
||||
return this.filePath.replace(
|
||||
regex,
|
||||
(match, p1) => `<span class="highlight_word">${p1}</span>`,
|
||||
);
|
||||
},
|
||||
codeTheme() {
|
||||
return gon.user_color_scheme || DEFAULT_THEME_COLOR;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
trackClipboardClick() {
|
||||
this.trackEvent(EVENT_CLICK_CLIPBOARD_BUTTON);
|
||||
},
|
||||
trackHeaderClick() {
|
||||
this.trackEvent(EVENT_CLICK_HEADER_LINK);
|
||||
},
|
||||
},
|
||||
DEFAULT_HEADER_LABEL_COLOR,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="file-header-content gl-flex gl-items-center gl-leading-1">
|
||||
<file-icon :file-name="filePath" :size="16" aria-hidden="true" css-classes="gl-mr-3" />
|
||||
|
||||
<gl-link :href="fileUrl" :title="$options.i18n.fileLink">
|
||||
<gl-link
|
||||
:href="fileUrl"
|
||||
:title="$options.i18n.fileLink"
|
||||
:class="`code ${codeTheme}`"
|
||||
@click="trackHeaderClick"
|
||||
>
|
||||
<template v-if="projectPath">
|
||||
<strong class="project-path-content" data-testid="project-path-content"
|
||||
>{{ projectPath }}:
|
||||
</strong>
|
||||
</template>
|
||||
|
||||
<strong class="file-name-content" data-testid="file-name-content">{{ filePath }}</strong>
|
||||
<strong
|
||||
v-safe-html="highlightedFilePath"
|
||||
class="file-name-content"
|
||||
data-testid="file-name-content"
|
||||
></strong>
|
||||
</gl-link>
|
||||
<clipboard-button
|
||||
:text="filePath"
|
||||
|
|
@ -56,6 +113,13 @@ export default {
|
|||
:title="__('Copy file path')"
|
||||
category="tertiary"
|
||||
css-class="gl-mr-2"
|
||||
@click="trackClipboardClick"
|
||||
/>
|
||||
<gl-label
|
||||
v-if="isHeaderOnly"
|
||||
:background-color="$options.DEFAULT_HEADER_LABEL_COLOR"
|
||||
:title="$options.i18n.isHeaderOnly"
|
||||
class="gl-self-center"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ export default {
|
|||
projectPathAndFilePath({ projectPath = '', path = '' }) {
|
||||
return `${projectPath}:${path}`;
|
||||
},
|
||||
position(index) {
|
||||
return index + 1;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -73,7 +76,7 @@ export default {
|
|||
<gl-loading-icon v-if="isLoading" :label="__('Loading')" size="md" variant="spinner" />
|
||||
<div v-if="hasResults && !isLoading" class="gl-relative">
|
||||
<gl-card
|
||||
v-for="file in blobSearch.files"
|
||||
v-for="(file, index) in blobSearch.files"
|
||||
:key="projectPathAndFilePath(file)"
|
||||
class="file-result-holder file-holder gl-my-5"
|
||||
:header-class="{
|
||||
|
|
@ -88,13 +91,14 @@ export default {
|
|||
:file-path="file.path"
|
||||
:project-path="file.projectPath"
|
||||
:file-url="file.fileUrl"
|
||||
:is-header-only="!hasCode(file)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<blob-body v-if="hasCode(file)" :file="file" />
|
||||
<blob-body v-if="hasCode(file)" :file="file" :position="position(index)" />
|
||||
|
||||
<template v-if="hasMore(file)" #footer>
|
||||
<blob-footer :file="file" />
|
||||
<blob-footer :file="file" :position="position(index)" />
|
||||
</template>
|
||||
</gl-card>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,3 +5,6 @@ export const SEARCH_RESULTS_DEBOUNCE = 500;
|
|||
export const DEFAULT_SHOW_CHUNKS = 3;
|
||||
|
||||
export const REF_FIELD_NAME = 'repository_ref';
|
||||
|
||||
export const DEFAULT_THEME_COLOR = 'white';
|
||||
export const DEFAULT_HEADER_LABEL_COLOR = '#D9C2EE';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
export const EVENT_CLICK_BLOB_RESULTS_SHOW_MORE_LESS = 'click_blob_results_show_more_less';
|
||||
export const EVENT_CLICK_BLOB_RESULT_BLAME_LINE = 'click_search_blob_result_blame_line';
|
||||
export const EVENT_CLICK_BLOB_RESULT_LINE = 'click_search_blob_result_line';
|
||||
export const EVENT_CLICK_CLIPBOARD_BUTTON = 'click_clipboard_button_in_multimatch_file_header';
|
||||
export const EVENT_CLICK_HEADER_LINK = 'click_header_link_of_blob_result';
|
||||
|
|
@ -13,7 +13,7 @@ export default {
|
|||
timeFormatted(time, format) {
|
||||
const timeago = getTimeago(format);
|
||||
|
||||
return timeago.format(time, timeagoLanguageCode);
|
||||
return timeago.format(newDate(time), timeagoLanguageCode);
|
||||
},
|
||||
|
||||
tooltipTitle(time) {
|
||||
|
|
|
|||
|
|
@ -2,22 +2,17 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25);
|
|||
|
||||
.flash-container {
|
||||
margin: 0;
|
||||
margin-bottom: $gl-padding;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@apply gl-flex;
|
||||
@apply gl-flex-col;
|
||||
@apply gl-gap-3;
|
||||
|
||||
&.sticky {
|
||||
position: sticky;
|
||||
top: $calc-application-header-height;
|
||||
z-index: 251;
|
||||
|
||||
.flash-alert,
|
||||
.flash-notice,
|
||||
.flash-success,
|
||||
.flash-warning {
|
||||
margin-bottom: $gl-spacing-scale-4;
|
||||
}
|
||||
}
|
||||
|
||||
&.flash-container-page {
|
||||
|
|
@ -63,11 +58,6 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25);
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
.gl-alert {
|
||||
margin-top: $gl-spacing-scale-4;
|
||||
margin-bottom: $gl-spacing-scale-4;
|
||||
}
|
||||
|
||||
&.flash-container-no-margin {
|
||||
.gl-alert {
|
||||
margin-top:0;
|
||||
|
|
|
|||
|
|
@ -50,17 +50,8 @@ html {
|
|||
}
|
||||
|
||||
.alert-wrapper {
|
||||
@include gl-media-breakpoint-up(xl) {
|
||||
--gl-alert-padding-x: #{$gl-spacing-scale-3};
|
||||
--gl-broadcast-message-padding-x: #{$gl-spacing-scale-3};
|
||||
}
|
||||
|
||||
.alert {
|
||||
margin-bottom: 0;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: $gl-padding;
|
||||
}
|
||||
.gl-alert:first-child {
|
||||
@apply gl-mt-3;
|
||||
}
|
||||
|
||||
.alert-link-group {
|
||||
|
|
@ -143,11 +134,6 @@ html {
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.alert-wrapper .flash-container .flash-alert:last-child,
|
||||
.alert-wrapper .flash-container .flash-notice:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
padding-bottom: 0;
|
||||
flex: 1;
|
||||
|
|
|
|||
|
|
@ -61,27 +61,6 @@ $gl-spacing-scale-48: 48 * $grid-size;
|
|||
$gl-spacing-scale-75: 75 * $grid-size;
|
||||
/* End gitlab-ui#1709 */
|
||||
|
||||
/*
|
||||
* Why another sizing scale???
|
||||
* Great question, friend!
|
||||
* This size scale is a "backport" of the equivalent set of "named" sizes
|
||||
* (e.g. `xl` versus `70`) that came from the following design document as of 2019-10-23:
|
||||
*
|
||||
* https://gitlab-org.gitlab.io/gitlab-design/hosted/design-gitlab-specs/forms-spec-previews/
|
||||
*
|
||||
* (See `input-` items at the bottom)
|
||||
*
|
||||
* The presumption here is that these sizes will be standardized in GitLab UI and thus will be
|
||||
* broadly useful here in the GitLab product when not using the GitLab UI components.
|
||||
*/
|
||||
$size-scale: (
|
||||
'xs': #{10 * $grid-size},
|
||||
's': #{20 * $grid-size},
|
||||
'm': #{30 * $grid-size},
|
||||
'l': #{40 * $grid-size},
|
||||
'xl': #{70 * $grid-size}
|
||||
);
|
||||
|
||||
// Color schema
|
||||
$purple: #6d49cb !default;
|
||||
$purple-light: #ede8fb !default;
|
||||
|
|
@ -115,15 +94,6 @@ $gl-font-size-16: 16px;
|
|||
$gl-font-size-28: 28px;
|
||||
$gl-font-size-42: 42px;
|
||||
|
||||
$type-scale: (
|
||||
1: 12px,
|
||||
2: 14px,
|
||||
3: 16px,
|
||||
4: 20px,
|
||||
5: 28px,
|
||||
6: 42px
|
||||
);
|
||||
|
||||
/*
|
||||
* Lists
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
.learn-gitlab-info-card-content {
|
||||
height: 200px;
|
||||
}
|
||||
|
|
@ -4,24 +4,42 @@
|
|||
to see the available utility classes. If you cannot find the class you need,
|
||||
consider adding it to Gitlab UI before adding it here.
|
||||
**/
|
||||
|
||||
$type-scale: (
|
||||
1: 12px,
|
||||
2: 14px,
|
||||
4: 20px,
|
||||
);
|
||||
|
||||
@each $index, $size in $type-scale {
|
||||
#{'.text-#{$index}'} {
|
||||
font-size: $size;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Why another sizing scale???
|
||||
* Great question, friend!
|
||||
* This size scale is a "backport" of the equivalent set of "named" sizes
|
||||
* (e.g. `xl` versus `70`) that came from the following design document as of 2019-10-23:
|
||||
*
|
||||
* https://gitlab-org.gitlab.io/gitlab-design/hosted/design-gitlab-specs/forms-spec-previews/
|
||||
*
|
||||
* (See `input-` items at the bottom)
|
||||
*
|
||||
* The presumption here is that these sizes will be standardized in GitLab UI and thus will be
|
||||
* broadly useful here in the GitLab product when not using the GitLab UI components.
|
||||
*/
|
||||
$size-scale: (
|
||||
's': #{20 * $grid-size},
|
||||
);
|
||||
|
||||
@each $index, $size in $size-scale {
|
||||
#{'.mw-#{$index}'} {
|
||||
max-width: $size;
|
||||
}
|
||||
}
|
||||
|
||||
@each $index, $size in $type-scale {
|
||||
#{'.lh-#{$index}'} {
|
||||
line-height: $size;
|
||||
}
|
||||
}
|
||||
|
||||
@for $i from 1 through 12 {
|
||||
#{'.tab-width-#{$i}'} {
|
||||
/* stylelint-disable-next-line property-no-vendor-prefix */
|
||||
|
|
@ -30,13 +48,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.border-width-1px { border-width: 1px; }
|
||||
.border-style-dashed { border-style: dashed; }
|
||||
.border-style-solid { border-style: solid; }
|
||||
.border-color-blue-300 { border-color: $blue-300; }
|
||||
.border-color-default { border-color: $border-color; }
|
||||
.border-radius-default { border-radius: $gl-border-radius-base; }
|
||||
.border-radius-small { border-radius: $border-radius-small; }
|
||||
.box-shadow-default { box-shadow: 0 2px 4px 0 $t-gray-a-24; }
|
||||
|
||||
// Override Bootstrap class with offset for system-header and
|
||||
|
|
@ -50,34 +62,6 @@
|
|||
top: $calc-application-header-height;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-children-ml-sm-3 > * {
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin-left: $gl-spacing-scale-3;
|
||||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-first-child-ml-sm-0 > a:first-child,
|
||||
.gl-first-child-ml-sm-0 > button:first-child {
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mh-50vh { max-height: 50vh; }
|
||||
|
||||
.min-width-0 {
|
||||
// By default flex items don't shrink below their minimum content size. To change this, set the item's min-width
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-w-16 { width: px-to-rem($grid-size * 2); }
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-w-64 { width: px-to-rem($grid-size * 8); }
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-h-32 { height: px-to-rem($grid-size * 4); }
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-h-64 { height: px-to-rem($grid-size * 8); }
|
||||
|
||||
|
|
@ -100,25 +84,6 @@
|
|||
display: flex;
|
||||
}
|
||||
|
||||
/**
|
||||
Note: ::-webkit-scrollbar is a non-standard rule only
|
||||
supported by webkit browsers.
|
||||
|
||||
It is added here to migrate components that use
|
||||
scrolling-links() mixin from `app/assets/stylesheets/framework/mixins.scss`.
|
||||
|
||||
It should not be used elsewhere: it may impact accessibility as well as
|
||||
add browser compatibility issues.
|
||||
|
||||
See: https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-scrollbar
|
||||
**/
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-webkit-scrollbar-display-none {
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Will be moved to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1465
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-focus-ring-border-1-gray-900\! {
|
||||
|
|
@ -147,13 +112,6 @@
|
|||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-md-h-9 {
|
||||
@include gl-media-breakpoint-up(md) {
|
||||
height: $gl-spacing-scale-9;
|
||||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line gitlab/no-gl-class
|
||||
.gl-pl-12 {
|
||||
padding-left: $gl-spacing-scale-12;
|
||||
|
|
|
|||
|
|
@ -443,27 +443,18 @@ class ApplicationController < BaseActionController
|
|||
end
|
||||
|
||||
def set_current_context(&block)
|
||||
static_context =
|
||||
if Feature.enabled?(:controller_static_context, Feature.current_request)
|
||||
{} # middleware should've included caller_id and feature_category
|
||||
else
|
||||
{ caller_id: self.class.endpoint_id_for_action(action_name) }
|
||||
end
|
||||
|
||||
# even though feature_category is pre-populated by
|
||||
# Gitlab::Middleware::ActionControllerStaticContext
|
||||
# using the static annotation on controllers, the
|
||||
# controllers can override feature_category conditionally
|
||||
static_context[:feature_category] = feature_category if feature_category.present?
|
||||
Gitlab::ApplicationContext.push(feature_category: feature_category) if feature_category.present?
|
||||
|
||||
Gitlab::ApplicationContext.push(
|
||||
static_context.merge({
|
||||
user: -> { context_user },
|
||||
project: -> { @project if @project&.persisted? },
|
||||
namespace: -> { @group if @group&.persisted? },
|
||||
remote_ip: request.ip,
|
||||
**http_router_rule_context
|
||||
})
|
||||
user: -> { context_user },
|
||||
project: -> { @project if @project&.persisted? },
|
||||
namespace: -> { @group if @group&.persisted? },
|
||||
remote_ip: request.ip,
|
||||
**http_router_rule_context
|
||||
)
|
||||
yield
|
||||
ensure
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ module Groups
|
|||
before_action :authorize_access!, only: :show
|
||||
before_action :define_deploy_token_variables, if: -> { can?(current_user, :create_deploy_token, @group) }
|
||||
|
||||
before_action do
|
||||
push_frontend_ability(ability: :admin_group, resource: @group, user: current_user)
|
||||
end
|
||||
|
||||
feature_category :continuous_delivery
|
||||
urgency :low
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class ProfilesController < Profiles::ApplicationController
|
|||
end
|
||||
|
||||
feature_category :user_profile, [:reset_incoming_email_token, :reset_feed_token,
|
||||
:reset_static_object_token, :update_username]
|
||||
:reset_static_object_token, :update_username, :join_early_access_program]
|
||||
|
||||
def reset_incoming_email_token
|
||||
Users::UpdateService.new(current_user, user: @user).execute! do |user|
|
||||
|
|
@ -116,4 +116,4 @@ class ProfilesController < Profiles::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
ProfilesController.prepend_mod
|
||||
ProfilesController.prepend_mod_with('ProfilesController')
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ module Projects
|
|||
push_frontend_feature_flag(:ci_variables_pages, current_user)
|
||||
push_frontend_feature_flag(:allow_push_repository_for_job_token, @project)
|
||||
push_frontend_feature_flag(:ci_hidden_variables, @project.root_ancestor)
|
||||
|
||||
push_frontend_ability(ability: :admin_project, resource: @project, user: current_user)
|
||||
end
|
||||
|
||||
helper_method :highlight_badge
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ module Projects
|
|||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:edit_branch_rules, @project)
|
||||
push_frontend_ability(ability: :admin_project, resource: @project, user: current_user)
|
||||
push_frontend_ability(ability: :admin_protected_branch, resource: @project, user: current_user)
|
||||
end
|
||||
|
||||
feature_category :source_code_management, [:show, :cleanup, :update]
|
||||
|
|
|
|||
|
|
@ -16,6 +16,12 @@ module AntiAbuse
|
|||
include ResolvableNote
|
||||
include Sortable
|
||||
|
||||
extend ::Gitlab::Utils::Override
|
||||
|
||||
cache_markdown_field :note, pipeline: :note, issuable_reference_expansion_enabled: true
|
||||
|
||||
redact_field :note
|
||||
|
||||
self.table_name = 'abuse_report_notes'
|
||||
|
||||
belongs_to :abuse_report
|
||||
|
|
@ -30,6 +36,11 @@ module AntiAbuse
|
|||
def discussion_class(_noteable = nil)
|
||||
AntiAbuse::Reports::IndividualNoteDiscussion
|
||||
end
|
||||
|
||||
override :skip_project_check?
|
||||
def skip_project_check?
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,9 +8,6 @@ class ApplicationSetting < ApplicationRecord
|
|||
include IgnorableColumns
|
||||
include Sanitizable
|
||||
|
||||
ignore_columns %i[instance_administration_project_id instance_administrators_group_id], remove_with: '16.2', remove_after: '2023-06-22'
|
||||
ignore_columns %i[repository_storages], remove_with: '16.8', remove_after: '2023-12-21'
|
||||
ignore_column :required_instance_ci_template, remove_with: '17.1', remove_after: '2024-05-10'
|
||||
ignore_column :sign_in_text_html, remove_with: '17.5', remove_after: '2024-10-17'
|
||||
ignore_columns %i[
|
||||
encrypted_openai_api_key
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
module Ci
|
||||
class JobAnnotation < Ci::ApplicationRecord
|
||||
include Ci::Partitionable
|
||||
|
||||
before_validation :set_project_id, on: :create
|
||||
|
||||
include BulkInsertSafe
|
||||
|
||||
self.table_name = :p_ci_job_annotations
|
||||
|
|
@ -18,7 +21,13 @@ module Ci
|
|||
partitionable scope: :job, partitioned: true
|
||||
|
||||
validates :data, json_schema: { filename: 'ci_job_annotation_data' }
|
||||
validates :name, presence: true,
|
||||
length: { maximum: 255 }
|
||||
validates :name, presence: true, length: { maximum: 255 }
|
||||
validates :project_id, presence: true, on: :create
|
||||
|
||||
private
|
||||
|
||||
def set_project_id
|
||||
self.project_id ||= job&.project_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Packages::Conan::Metadatum < ApplicationRecord
|
||||
include IgnorableColumns
|
||||
|
||||
ignore_columns %i[os architecture build_type compiler compiler_version compiler_libcxx compiler_cppstd],
|
||||
remove_with: '17.6', remove_after: '2024-10-22'
|
||||
NONE_VALUE = '_'
|
||||
|
||||
belongs_to :package, class_name: 'Packages::Conan::Package', inverse_of: :conan_metadatum
|
||||
|
|
@ -14,9 +18,6 @@ class Packages::Conan::Metadatum < ApplicationRecord
|
|||
|
||||
validate :username_channel_none_values
|
||||
|
||||
validates :os, :architecture, :build_type, :compiler, :compiler_libcxx, :compiler_cppstd, length: { maximum: 32 }
|
||||
validates :compiler_version, length: { maximum: 16 }
|
||||
|
||||
def recipe
|
||||
"#{package.name}/#{package.version}@#{package_username}/#{package_channel}"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
module Projects
|
||||
class BranchRulePolicy < ::ProtectedBranchPolicy
|
||||
rule { can?(:read_protected_branch) }.enable :read_branch_rule
|
||||
rule { can?(:create_protected_branch) }.enable :create_branch_rule
|
||||
rule { can?(:update_protected_branch) }.enable :update_branch_rule
|
||||
rule { can?(:destroy_protected_branch) }.enable :destroy_branch_rule
|
||||
rule { can?(:admin_project) }.policy do
|
||||
enable :read_branch_rule
|
||||
enable :create_branch_rule
|
||||
enable :update_branch_rule
|
||||
enable :destroy_branch_rule
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ module Ci
|
|||
raise ParserError, 'Annotations files must be a JSON object' unless blob_json.is_a?(Hash)
|
||||
|
||||
blob_json.each do |key, value|
|
||||
annotations.push(Ci::JobAnnotation.new(job: artifact.job, name: key, data: value))
|
||||
annotations.push(Ci::JobAnnotation.new(job: artifact.job, name: key, data: value,
|
||||
project_id: project.id))
|
||||
|
||||
if annotations.size > annotations_num_limit
|
||||
raise SizeLimitError,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@
|
|||
"admin_merge_request": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"admin_protected_branch": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"admin_push_rules": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
-# Render the parent group sidebar while creating a new subgroup/project, see GroupsController#new.
|
||||
- group = @parent_group || @group
|
||||
- context = group || @project
|
||||
- alert_class = "container-limited" unless fluid_layout
|
||||
|
||||
- sidebar_panel = super_sidebar_nav_panel(nav: nav, user: current_user, group: group, project: @project, current_ref: current_ref, ref_type: @ref_type, viewed_user: @user, organization: @organization)
|
||||
- sidebar_data = super_sidebar_context(current_user, group: group, project: @project, panel: sidebar_panel, panel_type: nav).to_json
|
||||
|
|
@ -11,7 +12,15 @@
|
|||
|
||||
.content-wrapper{ class: "#{@content_wrapper_class}" }
|
||||
= dispensable_render_if_exists 'layouts/header/verification_reminder'
|
||||
.alert-wrapper.gl-force-block-formatting-context
|
||||
|
||||
-# Broadcast messages
|
||||
.broadcast-wrapper
|
||||
= dispensable_render_if_exists "shared/token_expiration_banner"
|
||||
= dispensable_render "layouts/broadcast"
|
||||
= yield :group_invite_members_banner
|
||||
|
||||
-# Alerts
|
||||
.alert-wrapper.gl-flex.gl-flex-col.gl-gap-3.container-fluid{ class: alert_class }
|
||||
= dispensable_render 'shared/outdated_browser'
|
||||
= dispensable_render_if_exists "layouts/header/licensed_user_count_threshold"
|
||||
= dispensable_render_if_exists "layouts/header/token_expiry_notification"
|
||||
|
|
@ -20,8 +29,6 @@
|
|||
= dispensable_render_if_exists "shared/groups_projects/self_or_ancestor_marked_for_deletion_notice", context: context
|
||||
= dispensable_render "shared/projects/inactive_project_deletion_alert"
|
||||
= dispensable_render "shared/projects/archived_alert"
|
||||
= dispensable_render_if_exists "shared/token_expiration_banner"
|
||||
= dispensable_render "layouts/broadcast"
|
||||
= dispensable_render "layouts/header/read_only_banner"
|
||||
= dispensable_render "layouts/header/registration_enabled_callout"
|
||||
= dispensable_render "layouts/nav/classification_level_banner"
|
||||
|
|
@ -35,13 +42,15 @@
|
|||
= dispensable_render_if_exists "shared/new_user_signups_cap_reached_alert"
|
||||
= dispensable_render_if_exists "shared/silent_mode_banner"
|
||||
= yield :page_level_alert
|
||||
= yield :group_invite_members_banner
|
||||
- unless @hide_top_bar
|
||||
= render "layouts/nav/top_bar"
|
||||
= render "layouts/flash"
|
||||
= yield :after_flash_content
|
||||
|
||||
-# Top bar
|
||||
- unless @hide_top_bar
|
||||
= render "layouts/nav/top_bar"
|
||||
|
||||
%div{ class: "#{container_class unless @no_container} #{@content_class}" }
|
||||
%main.content{ id: "content-body", **page_itemtype }
|
||||
= render "layouts/flash", extra_flash_class: 'limit-container-width'
|
||||
= yield :after_flash_content
|
||||
= yield :before_content
|
||||
= yield
|
||||
= yield :after_content
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@
|
|||
- deletion_date = inactive_project_deletion_date(@project)
|
||||
- title = _('Due to inactivity, this project is scheduled to be deleted on %{deletion_date}. %{link_start}Why is this scheduled?%{link_end}').html_safe % { deletion_date: deletion_date, link_start: link_start, link_end: link_end }
|
||||
|
||||
= render Pajamas::AlertComponent.new(title: title, variant: :warning, alert_options: { class: 'gl-pb-3' }, dismissible: false)
|
||||
= render Pajamas::AlertComponent.new(title: title, variant: :warning, dismissible: false)
|
||||
|
|
|
|||
|
|
@ -328,7 +328,6 @@ module Gitlab
|
|||
config.assets.precompile << "page_bundles/issues_list.css"
|
||||
config.assets.precompile << "page_bundles/issues_show.css"
|
||||
config.assets.precompile << "page_bundles/jira_connect.css"
|
||||
config.assets.precompile << "page_bundles/learn_gitlab.css"
|
||||
config.assets.precompile << "page_bundles/log_viewer.css"
|
||||
config.assets.precompile << "page_bundles/login.css"
|
||||
config.assets.precompile << "page_bundles/members.css"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
description: User clicks show more or show less button on the multimatch results page
|
||||
internal_events: true
|
||||
action: click_blob_results_show_more_less
|
||||
identifiers:
|
||||
- project
|
||||
- namespace
|
||||
- user
|
||||
additional_properties:
|
||||
value:
|
||||
description: Position of the result.
|
||||
property:
|
||||
description: Set to 'open' if show more is activated and 'close' if it is closed again.
|
||||
product_group: global_search
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
description: Click on copy to clipboard button
|
||||
internal_events: true
|
||||
action: click_clipboard_button_in_multimatch_file_header
|
||||
identifiers:
|
||||
- project
|
||||
- namespace
|
||||
- user
|
||||
product_group: global_search
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
description: Click on the header link of a file result on multimatch result page
|
||||
internal_events: true
|
||||
action: click_header_link_of_blob_result
|
||||
identifiers:
|
||||
- project
|
||||
- namespace
|
||||
- user
|
||||
product_group: global_search
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
description: This event tracks click on a blame link inside the multimatch result
|
||||
internal_events: true
|
||||
action: click_search_blob_result_blame_line
|
||||
identifiers:
|
||||
- project
|
||||
- namespace
|
||||
- user
|
||||
additional_properties:
|
||||
value:
|
||||
description: Position of the result.
|
||||
property:
|
||||
description: Blame line.
|
||||
product_group: global_search
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
description: Click on line on the multimatch results page
|
||||
internal_events: true
|
||||
action: click_search_blob_result_line
|
||||
identifiers:
|
||||
- project
|
||||
- namespace
|
||||
- user
|
||||
additional_properties:
|
||||
value:
|
||||
description: Position of the result.
|
||||
property:
|
||||
description: Line number.
|
||||
product_group: global_search
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: controller_static_context
|
||||
feature_issue_url: https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/3593
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/157628
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/470047
|
||||
milestone: '17.4'
|
||||
group: group::scalability
|
||||
type: beta
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_blob_results_show_more_less_monthly
|
||||
description: Monthly count of unique users who click the show more or less button
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_blob_results_show_more_less
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_clipboard_button_in_multimatch_file_header_monthly
|
||||
description: Monthly count of unique users who clicked copy to clipboard button in header of multimatch result
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_clipboard_button_in_multimatch_file_header
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_header_link_of_blob_result_monthly
|
||||
description: Monthly count of unique users who clicked the blob result header link
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_header_link_of_blob_result
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_search_blob_result_blame_line_monthly
|
||||
description: Monthly count of unique users who clicked on blame line in the multimatch results page
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_search_blob_result_blame_line
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_search_blob_result_line_monthly
|
||||
description: Monthly count of unique users who clicked on result line link
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_search_blob_result_line
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_blob_results_show_more_less_weekly
|
||||
description: Weekly count of unique users who click the show more or less button
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_blob_results_show_more_less
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_clipboard_button_in_multimatch_file_header_weekly
|
||||
description: Weekly count of unique users who clicked copy to clipboard button in header of multimatch result
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_clipboard_button_in_multimatch_file_header
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_header_link_of_blob_result_weekly
|
||||
description: Weekly count of unique users who clicked the blob result header link
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_header_link_of_blob_result
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_search_blob_result_blame_line_weekly
|
||||
description: Weekly count of unique users who clicked on blame line in the multimatch results page
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_search_blob_result_blame_line
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_search_blob_result_line_weekly
|
||||
description: Weekly count of unique users who clicked on result line link
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.4'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161308
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_search_blob_result_line
|
||||
unique: user.id
|
||||
|
|
@ -12,6 +12,7 @@ resource :profile, only: [] do
|
|||
put :reset_feed_token
|
||||
put :reset_static_object_token
|
||||
put :update_username
|
||||
post :join_early_access_program
|
||||
end
|
||||
|
||||
scope module: :profiles do
|
||||
|
|
|
|||
|
|
@ -837,6 +837,8 @@
|
|||
- 1
|
||||
- - upload_checksum
|
||||
- 1
|
||||
- - users_experimental_communication_opt_in
|
||||
- 1
|
||||
- - users_record_last_activity
|
||||
- 1
|
||||
- - users_track_namespace_visits
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddProjectIdToPCiJobAnnotations < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.4'
|
||||
|
||||
def change
|
||||
add_column(:p_ci_job_annotations, :project_id, :bigint)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class IndexPCiJobAnnotationsOnProjectId < Gitlab::Database::Migration[2.2]
|
||||
include Gitlab::Database::PartitioningMigrationHelpers
|
||||
|
||||
milestone '17.4'
|
||||
disable_ddl_transaction!
|
||||
|
||||
TABLE_NAME = :p_ci_job_annotations
|
||||
INDEX_NAME = :index_p_ci_job_annotations_on_project_id
|
||||
|
||||
def up
|
||||
add_concurrent_partitioned_index(TABLE_NAME, :project_id, name: INDEX_NAME)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_partitioned_index_by_name(TABLE_NAME, INDEX_NAME)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPCiJobAnnotationsProjectIdNullConstraint < Gitlab::Database::Migration[2.2]
|
||||
disable_ddl_transaction!
|
||||
milestone '17.4'
|
||||
|
||||
TABLE_NAME = :p_ci_job_annotations
|
||||
COLUMN_NAME = :project_id
|
||||
CONSTRAINT_NAME = :check_375bb9900a
|
||||
|
||||
def up
|
||||
Gitlab::Database::PostgresPartitionedTable.each_partition(TABLE_NAME) do |partition|
|
||||
add_not_null_constraint(partition.identifier, COLUMN_NAME, constraint_name: CONSTRAINT_NAME, validate: false)
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
Gitlab::Database::PostgresPartitionedTable.each_partition(TABLE_NAME) do |partition|
|
||||
remove_not_null_constraint(partition.identifier, COLUMN_NAME, constraint_name: CONSTRAINT_NAME)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class BackfillNullProjectCiJobAnnotationRecords < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.4'
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_ci
|
||||
disable_ddl_transaction!
|
||||
|
||||
BATCH_SIZE = 1_000
|
||||
|
||||
def up
|
||||
annotations_model = define_batchable_model('p_ci_job_annotations', primary_key: :id)
|
||||
|
||||
annotations_model.each_batch(column: :id) do |batch|
|
||||
batch
|
||||
.where('p_ci_job_annotations.job_id = p_ci_builds.id')
|
||||
.where('p_ci_job_annotations.partition_id = p_ci_builds.partition_id')
|
||||
.update_all('project_id = p_ci_builds.project_id FROM p_ci_builds')
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ValidatePCiJobAnnotationProjectIdNullConstraint < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.4'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
TABLE_NAME = :p_ci_job_annotations
|
||||
COLUMN_NAME = :project_id
|
||||
CONSTRAINT_NAME = :check_375bb9900a
|
||||
|
||||
def up
|
||||
Gitlab::Database::PostgresPartitionedTable.each_partition(TABLE_NAME) do |partition|
|
||||
add_not_null_constraint(partition.identifier, COLUMN_NAME, constraint_name: CONSTRAINT_NAME)
|
||||
end
|
||||
|
||||
add_not_null_constraint(TABLE_NAME, COLUMN_NAME, constraint_name: CONSTRAINT_NAME)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_not_null_constraint(TABLE_NAME, COLUMN_NAME, constraint_name: CONSTRAINT_NAME)
|
||||
|
||||
Gitlab::Database::PostgresPartitionedTable.each_partition(TABLE_NAME) do |partition|
|
||||
add_not_null_constraint(partition.identifier, COLUMN_NAME, constraint_name: CONSTRAINT_NAME, validate: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
e359d298d6e198adc094e9c894ecfb2c80244d699eb27d8ff664269c0a3e9b21
|
||||
|
|
@ -0,0 +1 @@
|
|||
1eb30f94ef39ce6c71efb80e5e068808958f9726f2282d09ed46ec42f6260b29
|
||||
|
|
@ -0,0 +1 @@
|
|||
fb89e621e0a9d87cd2b304ac9d95f131c881f16fb02f3863dc984d7199a0e35e
|
||||
|
|
@ -0,0 +1 @@
|
|||
b692b0618940cbfac49b5f358e38613f5fbd799e1853362705f7539bb14d9a41
|
||||
|
|
@ -0,0 +1 @@
|
|||
771cadd0020345d7b52a97823f0b672465e0ab6c8f622fdee443162a73c79c30
|
||||
|
|
@ -2519,6 +2519,8 @@ CREATE TABLE p_ci_job_annotations (
|
|||
job_id bigint NOT NULL,
|
||||
name text NOT NULL,
|
||||
data jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
project_id bigint,
|
||||
CONSTRAINT check_375bb9900a CHECK ((project_id IS NOT NULL)),
|
||||
CONSTRAINT check_bac9224e45 CHECK ((char_length(name) <= 255)),
|
||||
CONSTRAINT data_is_array CHECK ((jsonb_typeof(data) = 'array'::text))
|
||||
)
|
||||
|
|
@ -29283,6 +29285,8 @@ CREATE INDEX index_p_ci_finished_build_ch_sync_events_on_project_id ON ONLY p_ci
|
|||
|
||||
CREATE UNIQUE INDEX index_p_ci_job_annotations_on_partition_id_job_id_name ON ONLY p_ci_job_annotations USING btree (partition_id, job_id, name);
|
||||
|
||||
CREATE INDEX index_p_ci_job_annotations_on_project_id ON ONLY p_ci_job_annotations USING btree (project_id);
|
||||
|
||||
CREATE INDEX index_p_ci_runner_machine_builds_on_runner_machine_id ON ONLY p_ci_runner_machine_builds USING btree (runner_machine_id);
|
||||
|
||||
CREATE INDEX index_packages_build_infos_on_pipeline_id ON packages_build_infos USING btree (pipeline_id);
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ The following API resources are available outside of project and group contexts
|
|||
| [Topics](topics.md) | `/topics` |
|
||||
| [Users](users.md) | `/users` |
|
||||
| [Validate `.gitlab-ci.yml` file](lint.md) | `/lint` |
|
||||
| [Web commits](web_commits.md) | `/web_commits/public_key` |
|
||||
| [Version](version.md) | `/version` |
|
||||
|
||||
## Templates API resources
|
||||
|
|
|
|||
|
|
@ -34288,6 +34288,7 @@ Represents the location of a vulnerability found by a container security scan.
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="vulnerabilitylocationcontainerscanningcontainerrepositoryurl"></a>`containerRepositoryUrl` | [`String`](#string) | URL of scanned image. |
|
||||
| <a id="vulnerabilitylocationcontainerscanningdependency"></a>`dependency` | [`VulnerableDependency`](#vulnerabledependency) | Dependency containing the vulnerability. |
|
||||
| <a id="vulnerabilitylocationcontainerscanningimage"></a>`image` | [`String`](#string) | Name of the vulnerable container image. |
|
||||
| <a id="vulnerabilitylocationcontainerscanningoperatingsystem"></a>`operatingSystem` | [`String`](#string) | Operating system that runs on the vulnerable container image. |
|
||||
|
|
@ -36899,6 +36900,7 @@ Member role permission.
|
|||
| <a id="memberrolepermissionadmin_group_member"></a>`ADMIN_GROUP_MEMBER` | Add or remove users in a group, and assign roles to users. When assigning a role, users with this custom permission must select a role that has the same or fewer permissions as the default role used as the base for their custom role. |
|
||||
| <a id="memberrolepermissionadmin_integrations"></a>`ADMIN_INTEGRATIONS` | Create, read, update, and delete integrations with external applications. |
|
||||
| <a id="memberrolepermissionadmin_merge_request"></a>`ADMIN_MERGE_REQUEST` | Allows approval of merge requests. |
|
||||
| <a id="memberrolepermissionadmin_protected_branch"></a>`ADMIN_PROTECTED_BRANCH` | Create, read, update, and delete protected branches for a project. |
|
||||
| <a id="memberrolepermissionadmin_push_rules"></a>`ADMIN_PUSH_RULES` | Configure push rules for repositories at the group or project level. |
|
||||
| <a id="memberrolepermissionadmin_runners"></a>`ADMIN_RUNNERS` | Create, view, edit, and delete group or project Runners. Includes configuring Runner settings. |
|
||||
| <a id="memberrolepermissionadmin_terraform_state"></a>`ADMIN_TERRAFORM_STATE` | Execute terraform commands, lock/unlock terraform state files, and remove file versions. |
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ tags:
|
|||
description: Operations about groups
|
||||
- name: runners
|
||||
description: Operations about runners
|
||||
- name: web_commits
|
||||
description: Operations about web commits
|
||||
- name: group_avatar
|
||||
description: Operations about group_avatars
|
||||
- name: invitations
|
||||
|
|
@ -39492,6 +39494,23 @@ paths:
|
|||
tags:
|
||||
- metadata
|
||||
operationId: getApiV4Version
|
||||
"/api/v4/web_commits/public_key":
|
||||
get:
|
||||
summary: Get the GitLab public key for signing web commits.
|
||||
description: This feature was introduced in GitLab 17.4.
|
||||
tags:
|
||||
- web_commits
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
'200':
|
||||
description: Get the GitLab public key for signing web commits.
|
||||
schema:
|
||||
"$ref": "#/definitions/API_Entities_Web_Commits"
|
||||
'503':
|
||||
description: Service unavailable
|
||||
'404':
|
||||
description: Not found
|
||||
"/api/v4/topics":
|
||||
get:
|
||||
summary: Get topics
|
||||
|
|
@ -59883,6 +59902,11 @@ definitions:
|
|||
avatar_url:
|
||||
type: string
|
||||
description: API_Entities_Projects_Topic model
|
||||
API_Entities_Web_Commits:
|
||||
type: object
|
||||
properties:
|
||||
public_key:
|
||||
type: string
|
||||
postApiV4Topics:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
|||
|
|
@ -383,6 +383,86 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
|
|||
- `400: Bad Request` if not revoked successfully.
|
||||
- `401: Unauthorized` if the access token is invalid.
|
||||
|
||||
## List token associations
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/466046) in GitLab 17.4.
|
||||
|
||||
Returns an unfiltered list of all groups, subgroups, and projects the current authenticated user can access.
|
||||
|
||||
```plaintext
|
||||
GET /personal_access_tokens/self/associations
|
||||
GET /personal_access_tokens/self/associations?page=2
|
||||
GET /personal_access_tokens/self/associations?min_access_level=40
|
||||
```
|
||||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|----------|----------|--------------------------------------------------------------------------|
|
||||
| `min_access_level` | integer | No | Limit by current user minimal [role (`access_level`)](members.md#roles). |
|
||||
| `page` | integer | No | Page to retrieve. Defaults to `1`. |
|
||||
| `per_page` | integer | No | Number of records to return per page. Defaults to `20`. |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/personal_access_tokens/self/associations"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"web_url": "http://gitlab.example.com/groups/test",
|
||||
"name": "Test",
|
||||
"parent_id": null,
|
||||
"organization_id": 1,
|
||||
"access_levels": 20,
|
||||
"visibility": "public"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"web_url": "http://gitlab.example.com/groups/test/test_private",
|
||||
"name": "Test Private",
|
||||
"parent_id": 1,
|
||||
"organization_id": 1,
|
||||
"access_levels": 50,
|
||||
"visibility": "test_private"
|
||||
}
|
||||
],
|
||||
"projects": [
|
||||
{
|
||||
"id": 1337,
|
||||
"description": "Leet.",
|
||||
"name": "Test Project",
|
||||
"name_with_namespace": "Test / Test Project",
|
||||
"path": "test-project",
|
||||
"path_with_namespace": "Test/test-project",
|
||||
"created_at": "2024-07-02T13:37:00.123Z",
|
||||
"access_levels": {
|
||||
"project_access_level": null,
|
||||
"group_access_level": 20
|
||||
},
|
||||
"visibility": "private",
|
||||
"web_url": "http://gitlab.example.com/test/test_project",
|
||||
"namespace": {
|
||||
"id": 1,
|
||||
"name": "Test",
|
||||
"path": "Test",
|
||||
"kind": "group",
|
||||
"full_path": "Test",
|
||||
"parent_id": null,
|
||||
"avatar_url": null,
|
||||
"web_url": "http://gitlab.example.com/groups/test"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Create a personal access token (administrator only)
|
||||
|
||||
See the [Users API documentation](users.md#create-a-personal-access-token) for information on creating a personal access token.
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ these additional scopes are available for the [advanced search](#advanced-search
|
|||
- `blobs`
|
||||
- `notes`
|
||||
|
||||
If you want to use basic search instead, see
|
||||
[specify a search type](../user/search/index.md#specify-a-search-type).
|
||||
|
||||
## Advanced search API
|
||||
|
||||
Search for a [term](../user/search/advanced_search.md#syntax) across the entire GitLab instance.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Web Commits API
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Free
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/442533) in GitLab 17.4.
|
||||
|
||||
Use this API to retrieve information about commits created with the Web UI.
|
||||
|
||||
## Get public signing key
|
||||
|
||||
Get the GitLab public key for signing web commits.
|
||||
|
||||
```plaintext
|
||||
GET /web_commits/public_key
|
||||
```
|
||||
|
||||
If successful, returns [`200`](rest/index.md#status-codes) and the following
|
||||
response attribute:
|
||||
|
||||
| Attribute | Type | Description |
|
||||
|--------------|--------|---------------------------------------------|
|
||||
| `public_key` | string | GitLab public key for signing web commits. |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --url "https://gitlab.example.com/api/v4/web_commits/public_key"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
public_key: "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=\""
|
||||
}
|
||||
]
|
||||
```
|
||||
|
|
@ -221,6 +221,38 @@ Apply the following feature flags to any AI feature work:
|
|||
|
||||
See the [feature flag tracker epic](https://gitlab.com/groups/gitlab-org/-/epics/10524) for the list of all feature flags and how to use them.
|
||||
|
||||
### Push feature flags to AI Gateway
|
||||
|
||||
You can push [feature flags](../feature_flags/index.md) to AI Gateway. This is helpful to gradually rollout user-facing changes even if the feature resides in AI Gateway.
|
||||
See the following example:
|
||||
|
||||
```ruby
|
||||
# Push a feature flag state to AI Gateway.
|
||||
Gitlab::AiGateway.push_feature_flag(:new_prompt_template, user)
|
||||
```
|
||||
|
||||
Later, you can use the feature flag state in AI Gateway in the following way:
|
||||
|
||||
```python
|
||||
from ai_gateway.feature_flags import is_feature_enabled
|
||||
|
||||
# Check if the feature flag "new_prompt_template" is enabled.
|
||||
if is_feature_enabled('new_prompt_template'):
|
||||
# Build a prompt from the new prompt template
|
||||
else:
|
||||
# Build a prompt from the old prompt template
|
||||
```
|
||||
|
||||
**IMPORTANT:** At the [cleaning up](../feature_flags/controls.md#cleaning-up) step, remove the feature flag in AI Gateway repository **before** removing the flag in GitLab-Rails repository.
|
||||
If you clean up the flag in GitLab-Rails repository at first, the feature flag in AI Gateway will be disabled immediately as it's the default state, hence you might encounter a surprising behavior.
|
||||
|
||||
**IMPORTANT:** Cleaning up the feature flag in AI Gateway will immediately distribute the change to all GitLab instances, including GitLab.com, Self-managed GitLab, and Dedicated.
|
||||
|
||||
Technical details: When `push_feature_flag` runs on an enabled feature flag, the name of flag is cached in the current context,
|
||||
which is later attached to `x-gitlab-enabled-feature-flags` HTTP header when GitLab-Sidekiq/Rails requests to AI Gateway.
|
||||
|
||||
As a simialr concept, we also have [`push_frontend_feature_flag`](../feature_flags/index.md) to push feature flags to frontend.
|
||||
|
||||
### GraphQL API
|
||||
|
||||
To connect to the AI provider API using the Abstraction Layer, use an extendable
|
||||
|
|
|
|||
|
|
@ -92,6 +92,33 @@ To pause indexing for [exact code search](../../user/search/exact_code_search.md
|
|||
When you pause indexing for exact code search, all changes in your repository are queued.
|
||||
To resume indexing, clear the **Pause indexing for exact code search** checkbox.
|
||||
|
||||
## Control indexing concurrency
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have administrator access to the instance.
|
||||
|
||||
You can set the number of concurrent indexing tasks
|
||||
that can be run on a Zoekt node relative to its CPU capacity.
|
||||
|
||||
A higher ratio allows more tasks to run concurrently, potentially
|
||||
improving indexing throughput at the cost of increased CPU usage.
|
||||
The default value is `1.0`, meaning one task per CPU core.
|
||||
|
||||
You can adjust this value based on your Zoekt node's performance
|
||||
characteristics and workload.
|
||||
|
||||
To set the number of concurrent indexing tasks:
|
||||
|
||||
1. On the left sidebar, at the bottom, select **Admin**.
|
||||
1. Select **Settings > Search**.
|
||||
1. Expand **Exact code search configuration**.
|
||||
1. In the **Indexing CPU to tasks multiplier** text box, enter a value.
|
||||
|
||||
For example, if a Zoekt node has `4` CPU cores and the ratio is set to `1.5`, the
|
||||
number of concurrent tasks for a node is going to be `4 * 1.5`, which is `6`.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
When working with Zoekt, you might encounter the following issues.
|
||||
|
|
|
|||
|
|
@ -2,163 +2,14 @@
|
|||
stage: Fulfillment
|
||||
group: Provision
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
remove_date: '2024-12-02'
|
||||
redirect_to: 'index.md'
|
||||
---
|
||||
|
||||
# Marketplace partners
|
||||
<!--- start_remove The following content will be removed on remove_date: '2024-12-02' -->
|
||||
|
||||
GitLab supports automation for selected distribution marketplaces to process sales of GitLab products to authorized
|
||||
channel partners. Marketplace partners can use the GitLab Marketplace APIs to integrate their systems with GitLab to
|
||||
sell GitLab subscriptions on their site.
|
||||
# Marketplace partners (removed)
|
||||
|
||||
This document's target audience is third-party developers for Marketplace partners.
|
||||
This feature was removed in GitLab 17.4.
|
||||
|
||||
## How the Marketplace APIs work
|
||||
|
||||
The Marketplace APIs are hosted in the [Customers Portal](https://customers.gitlab.com/). The Customers Portal allows
|
||||
individual customers to purchase and manage GitLab subscriptions and supports APIs for partners
|
||||
to make sales on behalf of their customers. The Customers Portal integrates with other GitLab services, including
|
||||
Zuora and Salesforce, to provide a task-oriented interface for users.
|
||||
|
||||
The following example shows a typical purchase flow of request and response between the following components:
|
||||
|
||||
- Customer
|
||||
- Marketplace partner system
|
||||
- Customers Portal
|
||||
- Zuora
|
||||
- Salesforce
|
||||
|
||||
```mermaid
|
||||
%%{init: { "fontFamily": "GitLab Sans" }}%%
|
||||
sequenceDiagram
|
||||
accTitle: Purchase flow
|
||||
accDescr: Shows the flow of a purchase from the customer, through the customer portal, Zuora, and Salesforce.
|
||||
|
||||
participant Customer
|
||||
participant Marketplace partner system
|
||||
participant Customers Portal
|
||||
participant Zuora
|
||||
participant Salesforce
|
||||
Customer ->> Marketplace partner system: Place order to purchase GitLab subscription
|
||||
Marketplace partner system ->> Customers Portal: Get OAuth token
|
||||
Customers Portal ->> Marketplace partner system: Access token
|
||||
Marketplace partner system ->> Customers Portal: Place order
|
||||
Customers Portal ->> Zuora: Create Zuora subscription
|
||||
Customers Portal ->> Salesforce: Create Salesforce objects
|
||||
Zuora ->> Customers Portal: Success response with Zuora subscription data
|
||||
Customers Portal ->> Marketplace partner system: Success response with order ID
|
||||
Zuora ->> Customers Portal: Zuora callout event
|
||||
Customers Portal ->> Customer: send license notification
|
||||
Marketplace partner system ->> Customers Portal: Poll order status
|
||||
Customers Portal ->> Marketplace partner system: Success response with order status
|
||||
```
|
||||
|
||||
## Marketplace API Specification
|
||||
|
||||
OpenAPI specs for the Marketplace APIs are available at [Marketplace interactive API documentation](https://customers.staging.gitlab.com/openapi_docs/marketplace).
|
||||
|
||||
## Access the Marketplace API
|
||||
|
||||
To access the Marketplace API you need to:
|
||||
|
||||
- Request access from GitLab.
|
||||
- Retrieve an OAuth access token.
|
||||
|
||||
Marketplace API endpoints are secured with [OAuth 2.0](https://oauth.net/2/). OAuth is an authorization framework
|
||||
that grants 3rd party or client applications, like a Marketplace partner application, limited access to resources on an
|
||||
HTTP service, like the Customers Portal.
|
||||
|
||||
OAuth 2.0 uses _grant types_ (or _flows_) that describe how a client application gets authorization in
|
||||
the form of an _access token_. An access token is a string that the client application uses to make authorized requests to
|
||||
the resource server.
|
||||
|
||||
The Marketplace API uses the `client_credentials` grant type. The client application uses the access token to access its
|
||||
own resources, instead of accessing resources on behalf of a user.
|
||||
|
||||
### Step 1: Request access
|
||||
|
||||
Before you can use the Marketplace API, you must contact your Marketplace partner Manager or email `partnerorderops@gitlab.com`
|
||||
to request access. After you request access, GitLab configures the following accounts and credentials for you:
|
||||
|
||||
1. Client credentials. Marketplace APIs are secured with OAuth 2.0. The client credentials include the client ID and client secret
|
||||
that you need to retrieve the OAuth access token.
|
||||
1. Invoice owner account in Zuora system. Required for invoice processing.
|
||||
1. Distributor account in Salesforce system.
|
||||
1. Trading partner account in Salesforce system. GitLab adds the Trading Partner ID to a permitted list to pass the validations.
|
||||
|
||||
### Step 2: Retrieve an access token
|
||||
|
||||
To retrieve an access token,
|
||||
|
||||
- Make a POST request to the [`/oauth/token`](https://customers.staging.gitlab.com/openapi_docs/marketplace#/marketplace/post_oauth_token) endpoint with the following required parameters:
|
||||
|
||||
| Parameter | Type | Required |Description |
|
||||
|-----------------|--------|----------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `client_id` | string | yes |ID of your client application record on the Customers Portal. Received from GitLab. |
|
||||
| `client_secret` | string | yes |Secret of your client application record on the Customers Portal. Received from GitLab. |
|
||||
| `grant_type` | string | yes |Specifies the type of credential flow. Use `client_credentials`. |
|
||||
| `scope` | string | yes |Specifies the level of access. Use `marketplace.order:read` for read-only access. Use `marketplace.order:create` for create access. |
|
||||
|
||||
If the request is successful, the response body includes the access token that you can use in subsequent requests. For an example of a successful
|
||||
response, see the [Marketplace interactive API documentation](https://customers.staging.gitlab.com/openapi_docs/marketplace)
|
||||
|
||||
If the request is unsuccessful, the response body includes an error and error description. The errors can be:
|
||||
|
||||
| Status | Description |
|
||||
|--------|----------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 400 | Invalid scope. Ensure the `scope` is `marketplace.order:read` or `marketplace.order:create`. |
|
||||
| 401 | Invalid client. Ensure that there are no typos or extra spaces on the client specific credentials. Incorrect `client_id` or `client_secret` |
|
||||
|
||||
### Step 3: Use the access token
|
||||
|
||||
To use the access token from a client application:
|
||||
|
||||
1. Set the `Authorization` header of the request to `Bearer <your_access_token>`.
|
||||
1. Set parameters or data needed for the endpoint and send the request.
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl \
|
||||
--url "https://customers.staging.gitlab.com/api/v1/marketplace/subscriptions/:external_subscription_id" \
|
||||
--header "Authorization: Bearer NHb_VhZhPOnBTSNfBSzmCmt28lLDWb2xtwr_c3DL148"
|
||||
```
|
||||
|
||||
## Create a new customer subscription
|
||||
|
||||
To create a new customer subscription from a Marketplace partner client application,
|
||||
|
||||
- Make an authorized POST request to the
|
||||
[`/api/v1/marketplace/subscriptions`](https://customers.staging.gitlab.com/openapi_docs/marketplace#/marketplace/post_api_v1_marketplace_subscriptions)
|
||||
endpoint in the Customers Portal with the following parameters in JSON format:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|--------------------------|--------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `externalSubscriptionId` | string | yes | ID of the subscription on the Marketplace partner system. |
|
||||
| `tradingPartnerId` | string | yes | ID of the Trading Partner account in Salesforce. Received from GitLab. |
|
||||
| `customer` | object | yes | Information about the customer. Must include company name. Contact must include `firstName`, `lastName` and `email`. Address must include `country`. |
|
||||
| `orderLines` | array | yes | Specifies the product purchased. Must include `quantity` and `productId`. |
|
||||
|
||||
If the request is successful, the response body includes the newly created subscription number. For an example of a full request body,
|
||||
see the [Marketplace interactive API documentation](https://customers.staging.gitlab.com/openapi_docs/marketplace).
|
||||
|
||||
If the subscription creation is unsuccessful, the response body includes an error message with details about the cause of the error.
|
||||
|
||||
## Check the status of a subscription
|
||||
|
||||
To get the status of a given subscription,
|
||||
|
||||
- Make an authorized GET request to the
|
||||
[`/api/v1/marketplace/subscriptions/{external_subscription_id}`](https://customers.staging.gitlab.com/openapi_docs/marketplace#/marketplace/get_api_v1_marketplace_subscriptions__external_subscription_id_)
|
||||
endpoint in the Customers Portal.
|
||||
|
||||
The request must include the Marketplace partner system ID of the subscription to fetch the status for.
|
||||
|
||||
If the request is successful, the response body contains the status of the subscription provision. The status can be:
|
||||
|
||||
- Creating
|
||||
- Created
|
||||
- Failed
|
||||
- Provisioned
|
||||
|
||||
If the subscription cannot be found using the passed `external_subscription_id`, the response has
|
||||
a 404 Not Found status.
|
||||
<!--- end_remove -->
|
||||
|
|
@ -86,6 +86,7 @@ These requirements are documented in the `Required permission` column in the fol
|
|||
| Name | Required permission | Description | Introduced in | Feature flag | Enabled in |
|
||||
|:-----|:------------|:------------------|:---------|:--------------|:---------|
|
||||
| [`admin_merge_request`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128302) | | Allows approval of merge requests. | GitLab [16.4](https://gitlab.com/gitlab-org/gitlab/-/issues/412708) | | |
|
||||
| [`admin_protected_branch`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/162208) | | Create, read, update, and delete protected branches for a project. | GitLab [17.4](https://gitlab.com/gitlab-org/gitlab/-/issues/448823) | | |
|
||||
| [`admin_push_rules`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147872) | | Configure push rules for repositories at the group or project level. | GitLab [16.11](https://gitlab.com/gitlab-org/gitlab/-/issues/421786) | `custom_ability_admin_push_rules` | |
|
||||
| [`read_code`](https://gitlab.com/gitlab-org/gitlab/-/issues/376180) | | Allows read-only access to the source code in the user interface. Does not allow users to edit or download repository archives, clone or pull repositories, view source code in an IDE, or view merge requests for private projects. You can download individual files because read-only access inherently grants the ability to make a local copy of the file. | GitLab [15.7](https://gitlab.com/gitlab-org/gitlab/-/issues/20277) | `customizable_roles` | GitLab [15.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110810) |
|
||||
|
||||
|
|
|
|||
|
|
@ -85,55 +85,72 @@ Prerequisites:
|
|||
1. On the left sidebar, select **Search or go to** and find your group.
|
||||
1. Select **Settings > Usage Quotas**.
|
||||
1. To view all members, select the **Seats** tab.
|
||||
1. To remove a member, select **Remove user**.
|
||||
|
||||
If you need more time to manage your members, or to try GitLab features
|
||||
with a team of more than five members, you can [start a trial](https://gitlab.com/-/trial_registrations/new?glm_source=docs.gitlab.com?&glm_content=free-user-limit-faq/ee/user/free_user_limit.html).
|
||||
A trial lasts for 30 days and includes an unlimited number of members.
|
||||
On this page, you can view and manage all members in your namespace. For example,
|
||||
to remove a member, select **Remove user**.
|
||||
|
||||
## Include a group in an organization's subscription
|
||||
|
||||
If there are multiple groups in your organization, they might have a
|
||||
combination of Paid and Free subscriptions. When a group
|
||||
with a Free subscription exceeds the user limit, their namespace becomes [read-only](../user/read_only_namespaces.md).
|
||||
If you have multiple groups in your organization, they might have a
|
||||
combination of paid (Premium or Ultimate tier) and Free tier subscriptions.
|
||||
When a group with a Free tier subscription exceeds the user limit, their
|
||||
namespace becomes [read-only](../user/read_only_namespaces.md).
|
||||
|
||||
To avoid user limits on groups with Free subscriptions, you can
|
||||
include them in your organization's subscription. To check if a group is included in the subscription,
|
||||
[view the group's subscription details](../subscriptions/gitlab_com/index.md#view-your-gitlabcom-subscription).
|
||||
If the group is on the Free tier, it is not included in your organization's subscription.
|
||||
To remove user limits on groups with Free tier subscriptions, include those groups
|
||||
in your organization's subscription:
|
||||
|
||||
To include the group in your Paid subscription, [transfer the group](../user/group/manage.md#transfer-a-group) to your organization's
|
||||
top-level namespace.
|
||||
1. To check if a group is included in the subscription,
|
||||
[view that group's subscription details](../subscriptions/gitlab_com/index.md#view-your-gitlabcom-subscription).
|
||||
|
||||
NOTE:
|
||||
If you previously purchased a subscription and the 5-user limit was applied to a group,
|
||||
ensure that [your subscription is linked](../subscriptions/gitlab_com/index.md#change-the-linked-group)
|
||||
to the correct top-level namespace, or that it has been
|
||||
linked to your Customers Portal account.
|
||||
If the group has a Free tier subscription, it is not included in your organization's
|
||||
subscription.
|
||||
|
||||
### Impact on seat count by transferred groups
|
||||
1. To include a group in your paid Premium or Ultimate tier subscription,
|
||||
[transfer that group](../user/group/manage.md#transfer-a-group) to your
|
||||
organization's top-level namespace.
|
||||
|
||||
When you transfer a group, there might be an increase in your seat count,
|
||||
which could incur additional costs for your subscription.
|
||||
If the five-user limit has been applied to your group even though you have
|
||||
a paid subscription in the Premium or Ultimate tier, make sure that
|
||||
[your subscription is linked](../subscriptions/gitlab_com/index.md#change-the-linked-group)
|
||||
to either of the following:
|
||||
|
||||
For example, a company has Group A and Group B:
|
||||
- The correct top-level namespace.
|
||||
- Your [Customers Portal](../subscriptions/customers_portal.md) account.
|
||||
|
||||
- Group A is on a Paid tier and has five users.
|
||||
- Group B is on the Free tier and has eight users, four of which are members of Group A.
|
||||
- Group B is placed in a read-only state when it exceeds the user limit.
|
||||
- Group B is transferred to the company's subscription to remove the read-only state.
|
||||
- The company incurs an additional cost of four seats for the
|
||||
### Impact of transferred groups on subscription costs
|
||||
|
||||
When you transfer a group to your organization's subscription, this might
|
||||
increase your seat count. This could incur additional costs for your subscription.
|
||||
|
||||
For example, your company has Group A and Group B:
|
||||
|
||||
- Group A has a paid Premium or Ultimate tier subscription and has five users.
|
||||
- Group B has a Free tier subscription and has eight users, four of which are
|
||||
members of Group A.
|
||||
- Group B is a read-only state because it exceeds the five-user limit.
|
||||
- You transfer Group B to your company's subscription to remove the read-only state.
|
||||
- Your company incurs an additional cost of four seats for the
|
||||
four members of Group B that are not members of Group A.
|
||||
|
||||
Users that are not part of the top-level namespace require additional seats to remain active. For more information, see [Add seats to your subscription](../subscriptions/gitlab_com/index.md#add-seats-to-your-subscription).
|
||||
Users that are not part of the top-level namespace require additional seats to
|
||||
remain active. For more information, see
|
||||
[add seats to your subscription](../subscriptions/gitlab_com/index.md#add-seats-to-your-subscription).
|
||||
|
||||
## Increase the five-user limit
|
||||
|
||||
On the Free tier on GitLab.com, you cannot increase the limit of five users on top-level groups with private visibility.
|
||||
On the Free subscription tier on GitLab.com, you cannot increase the limit of five users on
|
||||
top-level groups with private visibility.
|
||||
|
||||
For larger teams, you should upgrade to the Premium or Ultimate tier, which
|
||||
has no user limits and offers more features to increase team productivity. To experience the
|
||||
value of Paid features and unlimited users, you should start a [free trial](https://gitlab.com/-/trial_registrations/new?glm_source=docs.gitlab.com/ee/user/free_user_limit.html) for GitLab Ultimate.
|
||||
For larger teams, you should upgrade to the paid Premium or Ultimate tiers. These tiers
|
||||
do not limit users and have more features to increase team productivity. For more
|
||||
information, see:
|
||||
|
||||
- [Upgrade your subscription tier on GitLab self-managed](../subscriptions/self_managed/index.md#upgrade-your-subscription-tier).
|
||||
- [Upgrade your subscription tier on GitLab.com](../subscriptions/gitlab_com/index.md#upgrade-your-gitlabcom-subscription-tier).
|
||||
|
||||
To try the paid tiers before deciding to upgrade, start a
|
||||
[free trial](https://gitlab.com/-/trial_registrations/new?glm_source=docs.gitlab.com/ee/user/free_user_limit.html)
|
||||
for GitLab Ultimate.
|
||||
|
||||
## Manage members in personal projects outside a group namespace
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
---
|
||||
stage: AI-powered
|
||||
group: AI Model Validation
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# AI gateway
|
||||
|
||||
The [AI gateway](../../architecture/blueprints/ai_gateway/index.md) is a standalone service that gives access to AI-powered GitLab Duo features.
|
||||
|
||||
GitLab operates an instance of *AI Gateway* that is used by all GitLab instances, including self-managed, GitLab Dedicated, and GitLab.com via [Cloud Connector](../../development/cloud_connector/index.md).
|
||||
|
||||
This page describes where the AI gateway is deployed, and answers questions about region selection, data routing, and data sovereignty.
|
||||
|
||||
## Region support
|
||||
|
||||
For self-managed and Dedicated customers, the ability to choose the region is planned for future implementation. Currently, the process for region selection is managed internally by GitLab.
|
||||
|
||||
Runway, is currently not available to external customers. GitLab is working on expanding support to include self-managed instances in the future (Epic: [Expand Platform Engineering to more runtimes](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/1330)).
|
||||
|
||||
[View the available regions](https://gitlab-com.gitlab.io/gl-infra/platform/runway/runwayctl/manifest.schema.html#spec_regions).
|
||||
|
||||
For GitLab.com customers, it's important to note that the current routing mechanism is based on the location of the GitLab instance, not the user's location. As GitLab.com is currently single-homed in `us-east1`, requests to the AI gateway are routed to us-east4 in almost all cases. This means that the routing may not always result in the absolute nearest deployment for every user.
|
||||
|
||||
GitLab is working on an initiative to bypass the monolith when communicating with the AI Gateway (Epic: [Let the client (IDE) request Code Suggestions](https://gitlab.com/groups/gitlab-org/-/epics/13252)). This effort aims to improve routing efficiency and potentially allow for more user-location-based routing in the future.
|
||||
|
||||
### Automatic routing
|
||||
|
||||
GitLab leverages Cloudflare and *Google Cloud Platform* (GCP) load balancers to route AI
|
||||
gateway requests to the nearest available deployment automatically. This routing
|
||||
mechanism prioritizes low latency and efficient processing of user requests.
|
||||
|
||||
You cannot manually control this routing process. The system dynamically selects the optimal region based on factors like network conditions and server load.
|
||||
|
||||
### Tracing requests to specific regions
|
||||
|
||||
You cannot directly trace your AI requests to specific regions at this time.
|
||||
|
||||
If you need assistance with tracing a particular request, GitLab Support can access and
|
||||
analyze logs that contain Cloudflare headers and instance UUIDs. These logs provide
|
||||
insights into the routing path and can help identify the region where a request was processed.
|
||||
|
||||
## Data sovereignty
|
||||
|
||||
It's important to acknowledge the current limitations regarding strict data sovereignty enforcement in our multi-region AI gateway deployment. Currently, we cannot guarantee requests will go to or remain within a particular region and therefore is not a data residency solution.
|
||||
|
||||
### Factors that influence data routing
|
||||
|
||||
The following factors influence where data is routed.
|
||||
|
||||
- **Network latency:** The primary routing mechanism focuses on minimizing latency, meaning data might be processed in a region other than the nearest one if network conditions dictate.
|
||||
- **Service availability:** In case of regional outages or service disruptions, requests might be automatically rerouted to ensure uninterrupted service.
|
||||
- **Third-Party dependencies:** The GitLab AI infrastructure relies on third-party model providers, like Google Vertex AI, which have their own data handling practices.
|
||||
|
||||
### AI-gateway deployment regions
|
||||
|
||||
For the most up-to-date information on AI gateway deployment regions, please refer to the [AI-assist runway configuration file](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/blob/main/.runway/runway.yml?ref_type=heads#L12).
|
||||
|
||||
As of the last update (2023-11-21), GitLab deploys the AI gateway in the following regions:
|
||||
|
||||
- North America (`us-east4`)
|
||||
- Europe (`europe-west2`, `europe-west3`, `europe-west9`)
|
||||
- Asia Pacific (`asia-northeast1`, `asia-northeast3`)
|
||||
|
||||
Please note that deployment regions may change frequently. For the most current information, always check the configuration file linked above.
|
||||
|
||||
The exact location of the LLM models used by the AI gateway is determined by the third-party model providers. Currently, there is no guarantee that the models reside in the same geographical regions as the AI gateway deployments. This implies that data may flow back to the US or other regions where the model provider operates, even if the AI-gateway processes the initial request in a different region.
|
||||
|
||||
### Data Flow and LLM model locations
|
||||
|
||||
GitLab is working closely with LLM providers to understand their regional data handling practices fully. Currently, there might be instances where data is transmitted to regions outside the one closest to the user due to the factors mentioned above.
|
||||
|
||||
### Future enhancements
|
||||
|
||||
GitLab is actively working to let customers specify data residency requirements more granularly in the future. The proposed functionality can provide greater control over data processing locations and help meet specific compliance needs.
|
||||
|
||||
## Specific regional questions
|
||||
|
||||
### Data routing post-Brexit
|
||||
|
||||
The UK's exit from the EU does not directly impact data routing preferences or decisions for AI gateway. Data will continue to be routed to the most optimal region based on performance and availability. Data can still flow freely between the EU and UK.
|
||||
|
|
@ -119,6 +119,7 @@ For use cases and best practices, follow the [GitLab Duo examples documentation]
|
|||
> - [Introduced](https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/issues/276) in GitLab VS Code Extension 4.20.0.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/editor-extensions/gitlab-jetbrains-plugin/-/issues/462) in GitLab Duo for JetBrains 2.7.0.
|
||||
> - [Added](https://gitlab.com/gitlab-org/editor-extensions/gitlab.vim/-/merge_requests/152) to the GitLab Neovim plugin on July 16, 2024.
|
||||
> - Feature flags `advanced_context_resolver` and `code_suggestions_context` [enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161538) in GitLab 17.4.
|
||||
|
||||
FLAG:
|
||||
The availability of this feature is controlled by a feature flag.
|
||||
|
|
|
|||
|
|
@ -54,13 +54,7 @@ to check a commit's signature.
|
|||
### Verify commits made in the web UI
|
||||
|
||||
GitLab signs the commits created using the web UI using SSH.
|
||||
To verify these commits locally, [follow the steps for SSH](ssh.md#verify-commits-locally)
|
||||
and add the following public key to the `allowed_signers` file:
|
||||
`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIADOCCUoN3Q1UPQqUvp845fKy7haJH17qsSkVXzWXilW`.
|
||||
|
||||
```plaintext
|
||||
noreply@gitlab.com namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIADOCCUoN3Q1UPQqUvp845fKy7haJH17qsSkVXzWXilW
|
||||
```
|
||||
To verify these commits locally, use the [Web Commits API](../../../../api/web_commits.md#get-public-signing-key) to get the GitLab public key for signing web commits.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ For more information, see the history.
|
|||
This feature is available for testing, but not ready for production use.
|
||||
|
||||
With the Zoekt search API, you can use the [search API](../../api/search.md) for exact code search.
|
||||
When this feature is disabled, [advanced search](advanced_search.md) or [basic search](index.md) is used instead.
|
||||
If you want to use [advanced search](advanced_search.md) or basic search instead, see
|
||||
[specify a search type](index.md#specify-a-search-type).
|
||||
|
||||
By default, the Zoekt search API is disabled on GitLab.com to avoid breaking changes.
|
||||
To request access to this feature, contact GitLab.
|
||||
|
|
|
|||
|
|
@ -21,8 +21,18 @@ For code search, GitLab uses these types in this order:
|
|||
or when you search against a non-default branch.
|
||||
This type does not support group or global search.
|
||||
|
||||
When exact code search or advanced search is enabled, you can still use
|
||||
basic search by specifying the `basic_search=true` URL parameter.
|
||||
## Specify a search type
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161999) in GitLab 17.4.
|
||||
|
||||
To specify a search type, set the `search_type` URL parameter as follows:
|
||||
|
||||
- `search_type=zoekt` for [exact code search](exact_code_search.md)
|
||||
- `search_type=advanced` for [advanced search](advanced_search.md)
|
||||
- `search_type=basic` for basic search
|
||||
|
||||
`search_type` replaces the deprecated `basic_search` parameter.
|
||||
For more information, see [issue 477333](https://gitlab.com/gitlab-org/gitlab/-/issues/477333).
|
||||
|
||||
## Global search scopes
|
||||
|
||||
|
|
|
|||
|
|
@ -360,6 +360,7 @@ module API
|
|||
mount ::API::UserCounts
|
||||
mount ::API::UserRunners
|
||||
mount ::API::VirtualRegistries::Packages::Maven
|
||||
mount ::API::WebCommits
|
||||
mount ::API::Wikis
|
||||
|
||||
add_open_api_documentation!
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class GroupAssociationDetails < Entities::BasicGroupDetails
|
||||
expose :parent_id
|
||||
expose :organization_id
|
||||
|
||||
expose :access_levels do |group, options|
|
||||
group.highest_group_member(options[:current_user])&.access_level
|
||||
end
|
||||
|
||||
expose :visibility, documentation: { type: 'string', example: 'public' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class PersonalAccessTokenAssociations < Grape::Entity
|
||||
expose :groups, using: Entities::GroupAssociationDetails, documentation: { is_array: true }
|
||||
expose :projects, using: Entities::ProjectAssociationDetails, documentation: { is_array: true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class ProjectAssociationDetails < Entities::ProjectIdentity
|
||||
expose :access_levels do
|
||||
expose :project_access_level do |project, options|
|
||||
project.member(options[:current_user])&.access_level
|
||||
end
|
||||
|
||||
expose :group_access_level do |project, options|
|
||||
project.group.highest_group_member(options[:current_user])&.access_level if project.group
|
||||
end
|
||||
end
|
||||
|
||||
expose :visibility, documentation: { type: 'string', example: 'public' }
|
||||
expose :web_url, documentation: { type: 'string', example: 'https://gitlab.example.com/gitlab/gitlab' }
|
||||
expose :namespace, using: 'API::Entities::NamespaceBasic'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -6,6 +6,18 @@ module API
|
|||
extend ActiveSupport::Concern
|
||||
extend Grape::API::Helpers
|
||||
|
||||
def authorize_create_protected_branch!
|
||||
authorize!(:create_protected_branch, user_project)
|
||||
end
|
||||
|
||||
def authorize_update_protected_branch!(protected_branch)
|
||||
authorize!(:update_protected_branch, protected_branch)
|
||||
end
|
||||
|
||||
def authorize_destroy_protected_branch!(protected_branch)
|
||||
authorize!(:read_protected_branch, protected_branch)
|
||||
end
|
||||
|
||||
params :optional_params_ee do
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ module API
|
|||
class PersonalAccessTokens
|
||||
class SelfInformation < ::API::Base
|
||||
include APIGuard
|
||||
include PaginationParams
|
||||
|
||||
feature_category :system_access
|
||||
|
||||
|
|
@ -16,6 +17,20 @@ module API
|
|||
|
||||
before { authenticate! }
|
||||
|
||||
helpers do
|
||||
def load_groups
|
||||
finder_params = {}
|
||||
finder_params[:min_access_level] = params[:min_access_level] if params[:min_access_level]
|
||||
GroupsFinder.new(current_user, finder_params).execute
|
||||
end
|
||||
|
||||
def load_projects
|
||||
finder_params = {}
|
||||
finder_params[:min_access_level] = params[:min_access_level] if params[:min_access_level]
|
||||
ProjectsFinder.new(current_user: current_user, params: finder_params).execute
|
||||
end
|
||||
end
|
||||
|
||||
resource :personal_access_tokens do
|
||||
desc "Get single personal access token" do
|
||||
detail 'Get the details of a personal access token by passing it to the API in a header'
|
||||
|
|
@ -30,6 +45,28 @@ module API
|
|||
present access_token, with: Entities::PersonalAccessToken
|
||||
end
|
||||
|
||||
desc "Return personal access token associations" do
|
||||
detail 'Get groups and projects this personal access token can access by passing it to the API in a header'
|
||||
success code: 200, model: Entities::PersonalAccessToken
|
||||
failure [
|
||||
{ code: 401, message: 'Unauthorized' },
|
||||
{ code: 404, message: 'Not found' }
|
||||
]
|
||||
tags %w[personal_access_tokens]
|
||||
end
|
||||
params do
|
||||
optional :min_access_level, type: Integer, values: Gitlab::Access.all_values,
|
||||
desc: 'Limit by minimum access level of authenticated user'
|
||||
use :pagination
|
||||
end
|
||||
get 'self/associations' do
|
||||
access_token_associations = {
|
||||
groups: paginate(load_groups),
|
||||
projects: paginate(load_projects)
|
||||
}
|
||||
present access_token_associations, with: Entities::PersonalAccessTokenAssociations, current_user: current_user
|
||||
end
|
||||
|
||||
desc "Revoke a personal access token" do
|
||||
detail 'Revoke a personal access token by passing it to the API in a header'
|
||||
success code: 204
|
||||
|
|
@ -38,7 +75,6 @@ module API
|
|||
]
|
||||
tags %w[personal_access_tokens]
|
||||
end
|
||||
|
||||
delete 'self' do
|
||||
revoke_token(access_token)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ module API
|
|||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
post ':id/protected_branches' do
|
||||
authorize_admin_project
|
||||
authorize_create_protected_branch!
|
||||
|
||||
protected_branch = user_project.protected_branches.find_by(name: params[:name])
|
||||
|
||||
|
|
@ -127,10 +127,10 @@ module API
|
|||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
patch ':id/protected_branches/:name', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
|
||||
authorize_admin_project
|
||||
|
||||
protected_branch = user_project.protected_branches.find_by!(name: params[:name])
|
||||
|
||||
authorize_update_protected_branch!(protected_branch)
|
||||
|
||||
declared_params = declared_params(include_missing: false)
|
||||
api_service = ::ProtectedBranches::ApiService.new(user_project, current_user, declared_params)
|
||||
protected_branch = api_service.update(protected_branch)
|
||||
|
|
@ -156,10 +156,10 @@ module API
|
|||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
delete ':id/protected_branches/:name', requirements: BRANCH_ENDPOINT_REQUIREMENTS, urgency: :low do
|
||||
authorize_admin_project
|
||||
|
||||
protected_branch = user_project.protected_branches.find_by!(name: params[:name])
|
||||
|
||||
authorize_destroy_protected_branch!(protected_branch)
|
||||
|
||||
destroy_conditionally!(protected_branch) do
|
||||
destroy_service = ::ProtectedBranches::DestroyService.new(user_project, current_user)
|
||||
destroy_service.execute(protected_branch)
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ module API
|
|||
state: params[:state],
|
||||
confidential: params[:confidential],
|
||||
snippets: snippets?,
|
||||
basic_search: params[:basic_search],
|
||||
num_context_lines: params[:num_context_lines],
|
||||
search_type: params[:search_type],
|
||||
page: params[:page],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
class WebCommits < ::API::Base
|
||||
GITALY_PUBLIC_KEY_CACHE_KEY = 'gitaly_public_key'
|
||||
GITALY_UNAVAILABLE = 'The git server, Gitaly, is not available at this time. Please contact your administrator.'
|
||||
PUBLIC_KEY_NOT_FOUND = 'Public key not found.'
|
||||
|
||||
feature_category :source_code_management
|
||||
|
||||
before { authenticate_non_get! }
|
||||
|
||||
helpers do
|
||||
def gitaly_server
|
||||
@gitaly_server ||= Gitaly::Server.new(::Gitlab::GitalyClient.random_storage)
|
||||
end
|
||||
|
||||
def server_signature_public_key
|
||||
gitaly_server.server_signature_public_key
|
||||
end
|
||||
|
||||
def server_signature_error?
|
||||
gitaly_server.server_signature_error?
|
||||
end
|
||||
|
||||
def handle_gitaly_unavailable
|
||||
render_api_error!(GITALY_UNAVAILABLE, :service_unavailable)
|
||||
end
|
||||
|
||||
def handle_public_key_not_found
|
||||
render_api_error!(PUBLIC_KEY_NOT_FOUND, :not_found)
|
||||
end
|
||||
|
||||
def cache_public_key
|
||||
Rails.cache.fetch(GITALY_PUBLIC_KEY_CACHE_KEY, expires_in: 1.hour.to_i, skip_nil: true) do
|
||||
{ public_key: server_signature_public_key }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Get the public key for web commits' do
|
||||
detail 'This feature was introduced in GitLab 17.4.'
|
||||
success code: 200
|
||||
failure [
|
||||
{ code: 503, message: GITALY_UNAVAILABLE },
|
||||
{ code: 404, message: PUBLIC_KEY_NOT_FOUND }
|
||||
]
|
||||
end
|
||||
|
||||
get 'web_commits/public_key' do
|
||||
handle_gitaly_unavailable if server_signature_error?
|
||||
handle_public_key_not_found if server_signature_public_key.empty?
|
||||
|
||||
cache_public_key
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,6 +5,8 @@ module Gitaly
|
|||
SHA_VERSION_REGEX = /\A\d+\.\d+\.\d+-\d+-g([a-f0-9]{8})\z/
|
||||
DEFAULT_REPLICATION_FACTOR = 1
|
||||
|
||||
ServerSignature = Struct.new(:public_key, :error)
|
||||
|
||||
class << self
|
||||
def all
|
||||
Gitlab.config.repositories.storages.keys.map { |s| Gitaly::Server.new(s) }
|
||||
|
|
@ -58,6 +60,14 @@ module Gitaly
|
|||
storage_status&.fs_type
|
||||
end
|
||||
|
||||
def server_signature_public_key
|
||||
server_signature&.public_key
|
||||
end
|
||||
|
||||
def server_signature_error?
|
||||
!!server_signature.try(:error)
|
||||
end
|
||||
|
||||
def disk_used
|
||||
disk_statistics_storage_status&.used
|
||||
end
|
||||
|
|
@ -99,26 +109,30 @@ module Gitaly
|
|||
Gitlab::GitalyClient.expected_server_version.start_with?(match[1])
|
||||
end
|
||||
|
||||
def server_signature
|
||||
@server_signature ||= Gitlab::GitalyClient::ServerService.new(@storage).server_signature
|
||||
rescue GRPC::Unavailable, GRPC::DeadlineExceeded
|
||||
ServerSignature.new(public_key: nil, error: true)
|
||||
end
|
||||
|
||||
def info
|
||||
@info ||=
|
||||
begin
|
||||
Gitlab::GitalyClient::ServerService.new(@storage).info
|
||||
rescue GRPC::Unavailable, GRPC::DeadlineExceeded => ex
|
||||
Gitlab::ErrorTracking.track_exception(ex)
|
||||
# This will show the server as being out of date
|
||||
Gitaly::ServerInfoResponse.new(git_version: '', server_version: '', storage_statuses: [])
|
||||
end
|
||||
@info ||= wrapper_gitaly_rpc_errors do
|
||||
Gitlab::GitalyClient::ServerService.new(@storage).info
|
||||
end
|
||||
end
|
||||
|
||||
def disk_statistics
|
||||
@disk_statistics ||=
|
||||
begin
|
||||
Gitlab::GitalyClient::ServerService.new(@storage).disk_statistics
|
||||
rescue GRPC::Unavailable, GRPC::DeadlineExceeded => ex
|
||||
Gitlab::ErrorTracking.track_exception(ex)
|
||||
# This will show the server as being out of date
|
||||
Gitaly::ServerInfoResponse.new(git_version: '', server_version: '', storage_statuses: [])
|
||||
end
|
||||
@disk_statistics ||= wrapper_gitaly_rpc_errors do
|
||||
Gitlab::GitalyClient::ServerService.new(@storage).disk_statistics
|
||||
end
|
||||
end
|
||||
|
||||
def wrapper_gitaly_rpc_errors
|
||||
yield
|
||||
rescue GRPC::Unavailable, GRPC::DeadlineExceeded => ex
|
||||
Gitlab::ErrorTracking.track_exception(ex)
|
||||
# This will show the server as being out of date
|
||||
Gitaly::ServerInfoResponse.new(git_version: '', server_version: '', storage_statuses: [])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -876,9 +876,9 @@ module Gitlab
|
|||
end
|
||||
|
||||
# peel_tags slows down the request by a factor of 3-4
|
||||
def list_refs(patterns = [Gitlab::Git::BRANCH_REF_PREFIX], pointing_at_oids: [], peel_tags: false)
|
||||
def list_refs(...)
|
||||
wrapped_gitaly_errors do
|
||||
gitaly_ref_client.list_refs(patterns, pointing_at_oids: pointing_at_oids, peel_tags: peel_tags)
|
||||
gitaly_ref_client.list_refs(...)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ module Gitlab
|
|||
{ service: 'gitaly.ServerService', method: 'DiskStatistics' },
|
||||
{ service: 'gitaly.ServerService', method: 'ReadinessCheck' },
|
||||
{ service: 'gitaly.ServerService', method: 'ServerInfo' },
|
||||
{ service: 'gitaly.ServerService', method: 'ServerSignature' },
|
||||
{ service: 'grpc.health.v1.Health', method: 'Check' }
|
||||
],
|
||||
retryPolicy: {
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
# peel_tags slows down the request by a factor of 3-4
|
||||
def list_refs(patterns = [Gitlab::Git::BRANCH_REF_PREFIX], pointing_at_oids: [], peel_tags: false)
|
||||
def list_refs(patterns = [Gitlab::Git::BRANCH_REF_PREFIX], pointing_at_oids: [], peel_tags: false, dynamic_timeout: nil)
|
||||
request = Gitaly::ListRefsRequest.new(
|
||||
repository: @gitaly_repo,
|
||||
patterns: patterns,
|
||||
|
|
@ -245,7 +245,9 @@ module Gitlab
|
|||
peel_tags: peel_tags
|
||||
)
|
||||
|
||||
response = gitaly_client_call(@storage, :ref_service, :list_refs, request, timeout: GitalyClient.fast_timeout)
|
||||
timeout = dynamic_timeout || GitalyClient.fast_timeout
|
||||
|
||||
response = gitaly_client_call(@storage, :ref_service, :list_refs, request, timeout: timeout)
|
||||
consume_list_refs_response(response)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ module Gitlab
|
|||
GitalyClient.call(@storage, :server_service, :disk_statistics, Gitaly::DiskStatisticsRequest.new, timeout: GitalyClient.fast_timeout)
|
||||
end
|
||||
|
||||
def server_signature
|
||||
GitalyClient.call(@storage, :server_service, :server_signature, Gitaly::ServerSignatureRequest.new, timeout: GitalyClient.fast_timeout)
|
||||
end
|
||||
|
||||
def storage_info
|
||||
storage_specific(info)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ module Gitlab
|
|||
end
|
||||
|
||||
def call(env)
|
||||
return @app.call(env) unless Feature.enabled?(:controller_static_context, Feature.current_request)
|
||||
|
||||
req = ActionDispatch::Request.new(env)
|
||||
|
||||
action_name = req.path_parameters[:action]
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue