Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-09-06 21:07:30 +00:00
parent 23985334ba
commit 52982854c8
58 changed files with 1225 additions and 1046 deletions

View File

@ -41,7 +41,7 @@ review-docs-cleanup:
.docs-markdown-lint-image:
# When updating the image version here, update it in /scripts/lint-doc.sh too.
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-docs/lint-markdown:alpine-3.20-vale-3.6.1-markdownlint2-0.13.0-lychee-0.15.1
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-docs/lint-markdown:alpine-3.20-vale-3.7.1-markdownlint2-0.13.0-lychee-0.15.1
docs-lint markdown:
extends:
@ -52,7 +52,6 @@ docs-lint markdown:
stage: lint
needs: []
script:
- apk add libuuid
- source ./scripts/utils.sh
- yarn_install_script
- install_gitlab_gem

View File

@ -256,7 +256,7 @@ gem 'asciidoctor-kroki', '~> 0.10.0', require: false # rubocop:todo Gemfile/Miss
gem 'rouge', '~> 4.3.0', feature_category: :shared
gem 'truncato', '~> 0.7.12' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'nokogiri', '~> 1.16' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'gitlab-glfm-markdown', '~> 0.0.19', feature_category: :team_planning
gem 'gitlab-glfm-markdown', '~> 0.0.20', feature_category: :team_planning
# Calendar rendering
gem 'icalendar', '~> 2.10.1', feature_category: :system_access

View File

@ -213,11 +213,11 @@
{"name":"gitlab-dangerfiles","version":"4.8.0","platform":"ruby","checksum":"b327d079552ec974a63bf34d749a0308425af6ebf51d01064f1a6ff216a523db"},
{"name":"gitlab-experiment","version":"0.9.1","platform":"ruby","checksum":"f230ee742154805a755d5f2539dc44d93cdff08c5bbbb7656018d61f93d01f48"},
{"name":"gitlab-fog-azure-rm","version":"2.1.0","platform":"ruby","checksum":"f5becd9e412a8c8f18f8ac061b8bc2562a354fb7a9f63d8cc1b301e6e6aa0bdd"},
{"name":"gitlab-glfm-markdown","version":"0.0.19","platform":"aarch64-linux","checksum":"7cc4272cddb60c62745ac01e90368f1f11a5efbe8308a6df466eb3cca00b2c1a"},
{"name":"gitlab-glfm-markdown","version":"0.0.19","platform":"arm64-darwin","checksum":"1aef2fe4ec251fdd00fe19c163f6ce94111128edd61623e7123b67bac0c74aed"},
{"name":"gitlab-glfm-markdown","version":"0.0.19","platform":"ruby","checksum":"17a83a7ad1a63f2483a3c7fa75ecbc97467ec00123811793587995074f95811a"},
{"name":"gitlab-glfm-markdown","version":"0.0.19","platform":"x86_64-darwin","checksum":"370a5706058219acaad5e856f4dafd3b6f76ac45222ed53d6edd0b284dc99dde"},
{"name":"gitlab-glfm-markdown","version":"0.0.19","platform":"x86_64-linux","checksum":"d7d6e06e22669bd02c4fa5fbbca091e0f43a263bfd74fedb1fd974a5150d6fec"},
{"name":"gitlab-glfm-markdown","version":"0.0.20","platform":"aarch64-linux","checksum":"3ef33ad9374467c309e1ca318f42379ec01cf72ffffeb8f1a7eb3919416db619"},
{"name":"gitlab-glfm-markdown","version":"0.0.20","platform":"arm64-darwin","checksum":"2e97dcd7d23b9ada3db3bc5eaa3a39d710ef9df93ad68d438df92e835c64c19e"},
{"name":"gitlab-glfm-markdown","version":"0.0.20","platform":"ruby","checksum":"8c5057f980d7c6c5bf78de3673eddf825095433f923cdab21603f715da63acd2"},
{"name":"gitlab-glfm-markdown","version":"0.0.20","platform":"x86_64-darwin","checksum":"58396e74002e514ff1ad87b8e97d6e69e735ca4c6cd57f1d233c9b06ff95a045"},
{"name":"gitlab-glfm-markdown","version":"0.0.20","platform":"x86_64-linux","checksum":"20138aeaa968450897cbfdc67c351c87fa8d514c1bef667424cbcb10ed7c5cbe"},
{"name":"gitlab-kas-grpc","version":"17.4.0.pre.rc1","platform":"ruby","checksum":"37679435a3e71b830b215741e78714984c3392fbc09d660b3102e4a5e6ab92ca"},
{"name":"gitlab-labkit","version":"0.36.1","platform":"ruby","checksum":"04fb6941b7e5fc1fdcee8f9971fa2086a4dc442e39e67a74b992403dd580c300"},
{"name":"gitlab-license","version":"2.5.0","platform":"ruby","checksum":"4c166c469c2ad17876ca43188a4ccebe3feb0726c4c1770047f8dcef96573f4d"},

View File

@ -724,7 +724,7 @@ GEM
mime-types
net-http-persistent (~> 4.0)
nokogiri (~> 1, >= 1.10.8)
gitlab-glfm-markdown (0.0.19)
gitlab-glfm-markdown (0.0.20)
rb_sys (= 0.9.94)
gitlab-kas-grpc (17.4.0.pre.rc1)
grpc (~> 1.0)
@ -2054,7 +2054,7 @@ DEPENDENCIES
gitlab-duo-workflow-service-client (~> 0.1)!
gitlab-experiment (~> 0.9.1)
gitlab-fog-azure-rm (~> 2.1.0)
gitlab-glfm-markdown (~> 0.0.19)
gitlab-glfm-markdown (~> 0.0.20)
gitlab-housekeeper!
gitlab-http!
gitlab-kas-grpc (~> 17.4.0.pre.rc1)

View File

@ -75,7 +75,7 @@ export default {
@click="toggleOpen"
/>
{{ title }}
<gl-badge v-if="!loading" class="gl-ml-1" variant="neutral" size="sm">{{
<gl-badge v-if="!loading || count" class="gl-ml-1" variant="neutral" size="sm">{{
count
}}</gl-badge>
</h5>

View File

@ -18,7 +18,7 @@ export default {
return QUERIES[this.query];
},
update(d) {
return d.currentUser?.[this.query] || {};
return d.currentUser?.mergeRequests || {};
},
variables() {
return {

View File

@ -9,6 +9,16 @@ export function initMergeRequestDashboard(el) {
const { lists, switch_dashboard_path: switchDashboardPath } = JSON.parse(el.dataset.initialData);
const keyArgs = [
'state',
'reviewState',
'reviewStates',
'reviewerWildcardId',
'mergedAfter',
'assignedReviewStates',
'reviewerReviewStates',
];
return new Vue({
el,
apolloProvider: new VueApollo({
@ -18,19 +28,15 @@ export function initMergeRequestDashboard(el) {
cacheConfig: {
typePolicies: {
CurrentUser: {
merge: true,
fields: {
reviewRequestedMergeRequests: {
keyArgs: ['state', 'reviewState', 'reviewStates', 'mergedAfter'],
},
assignedMergeRequests: {
keyArgs: [
'state',
'reviewState',
'reviewStates',
'reviewerWildcardId',
'mergedAfter',
],
keyArgs,
},
reviewRequestedMergeRequests: {
keyArgs,
},
assigneeOrReviewerMergeRequests: {
keyArgs,
},
},
},
@ -39,10 +45,8 @@ export function initMergeRequestDashboard(el) {
nodes: concatPagination(),
},
},
UserMergeRequestInteraction: {
merge(a) {
return a;
},
MergeRequestReviewer: {
keyFields: false,
},
},
},

View File

@ -12,7 +12,7 @@ query requestingReview(
) {
currentUser {
id
assignedMergeRequests(
mergeRequests: assignedMergeRequests(
state: $state
reviewState: $reviewState
reviewStates: $reviewStates

View File

@ -11,7 +11,7 @@ query assigneeOrReviewer(
) {
currentUser {
id
assigneeOrReviewerMergeRequests(
mergeRequests: assigneeOrReviewerMergeRequests(
state: $state
assignedReviewStates: $assignedReviewStates
reviewerReviewStates: $reviewerReviewStates

View File

@ -11,7 +11,7 @@ query reviewRequests(
) {
currentUser {
id
reviewRequestedMergeRequests(
mergeRequests: reviewRequestedMergeRequests(
state: $state
reviewState: $reviewState
reviewStates: $reviewStates

View File

@ -416,7 +416,7 @@ export default {
},
},
showDuoSettings() {
return this.licensedAiFeaturesAvailable && this.glFeatures.aiSettingsVueProject;
return this.licensedAiFeaturesAvailable;
},
},

View File

@ -443,19 +443,6 @@ export default {
@edit="openAllowedToMergeDrawer"
/>
<rule-drawer
:is-open="isAllowedToMergeDrawerOpen || isAllowedToPushAndMergeDrawerOpen"
:roles="accessLevelsDrawerData.roles"
:users="accessLevelsDrawerData.users"
:groups="accessLevelsDrawerData.groups"
:deploy-keys="accessLevelsDrawerData.deployKeys"
:is-loading="isRuleUpdating"
:group-id="groupId"
:title="accessLevelsDrawerTitle"
@editRule="onEditAccessLevels"
@close="closeAccessLevelsDrawer"
/>
<!-- Allowed to push -->
<protection
class="gl-mt-5"
@ -474,6 +461,20 @@ export default {
@edit="openAllowedToPushAndMergeDrawer"
/>
<rule-drawer
:is-open="isAllowedToMergeDrawerOpen || isAllowedToPushAndMergeDrawerOpen"
:roles="accessLevelsDrawerData.roles"
:users="accessLevelsDrawerData.users"
:groups="accessLevelsDrawerData.groups"
:deploy-keys="accessLevelsDrawerData.deployKeys"
:is-loading="isRuleUpdating"
:group-id="groupId"
:title="accessLevelsDrawerTitle"
:is-push-access-levels="isAllowedToPushAndMergeDrawerOpen"
@editRule="onEditAccessLevels"
@close="closeAccessLevelsDrawer"
/>
<!-- Force push -->
<protection-toggle
v-if="hasPushAccessLevelSet"

View File

@ -40,6 +40,9 @@ export default {
ItemsSelector: () =>
import('ee_component/projects/settings/branch_rules/components/view/items_selector.vue'),
},
inject: {
showEnterpriseAccessLevels: { default: false },
},
props: {
isOpen: {
type: Boolean,
@ -78,6 +81,11 @@ export default {
type: Boolean,
required: true,
},
isPushAccessLevels: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
@ -193,20 +201,23 @@ export default {
}}
</gl-form-checkbox>
<template v-if="showEnterpriseAccessLevels">
<items-selector
:type="$options.USERS_TYPE"
:items="formatItemsIds(users)"
:users-options="$options.projectUsersOptions"
data-testid="users-selector"
@change="handleRuleDataUpdate('updatedUsers', $event)"
/>
<items-selector
:type="$options.GROUPS_TYPE"
:items="formatItemsIds(groups)"
data-testid="groups-selector"
@change="handleRuleDataUpdate('updatedGroups', $event)"
/>
</template>
<items-selector
:type="$options.USERS_TYPE"
:items="formatItemsIds(users)"
:users-options="$options.projectUsersOptions"
data-testid="users-selector"
@change="handleRuleDataUpdate('updatedUsers', $event)"
/>
<items-selector
:type="$options.GROUPS_TYPE"
:items="formatItemsIds(groups)"
data-testid="groups-selector"
@change="handleRuleDataUpdate('updatedGroups', $event)"
/>
<items-selector
v-if="isPushAccessLevels"
:type="$options.DEPLOY_KEYS_TYPE"
:items="formatItemsIds(deployKeys)"
data-testid="deploy-keys-selector"

View File

@ -26,6 +26,7 @@ export default function mountBranchRules(el, store) {
showStatusChecks,
showApprovers,
showCodeOwners,
showEnterpriseAccessLevels,
canAdminProtectedBranches,
} = el.dataset;
@ -44,6 +45,7 @@ export default function mountBranchRules(el, store) {
showStatusChecks: parseBoolean(showStatusChecks),
showApprovers: parseBoolean(showApprovers),
showCodeOwners: parseBoolean(showCodeOwners),
showEnterpriseAccessLevels: parseBoolean(showEnterpriseAccessLevels),
canAdminProtectedBranches: parseBoolean(canAdminProtectedBranches),
},
render(h) {

View File

@ -51,6 +51,8 @@ export default {
inject: {
projectPath: { default: '' },
branchRulesPath: { default: '' },
showStatusChecks: { default: false },
showApprovers: { default: false },
},
data() {
return {
@ -63,13 +65,15 @@ export default {
{ text: this.$options.i18n.branchName, action: () => this.openCreateRuleModal() },
];
[this.$options.i18n.allBranches, this.$options.i18n.allProtectedBranches].forEach(
(branch) => {
if (!this.hasPredefinedBranchRule(branch)) {
items.push(this.createPredefinedBrachRulesItem(branch));
}
},
);
if (this.showApprovers || this.showStatusChecks) {
[this.$options.i18n.allBranches, this.$options.i18n.allProtectedBranches].forEach(
(branch) => {
if (!this.hasPredefinedBranchRule(branch)) {
items.push(this.createPredefinedBrachRulesItem(branch));
}
},
);
}
return items;
},

View File

@ -8,16 +8,8 @@ export const SEARCH_INPUT_DESCRIPTION = 'label-search-input-description';
export const SEARCH_RESULTS_DESCRIPTION = 'label-search-results-description';
const header = __('Labels');
export const LABEL_FILTER_HEADER = __('Labels');
const scopes = {
ISSUES: 'issues',
};
export const LABEL_FILTER_PARAM = 'label_name';
const filterParam = 'labels';
export const labelFilterData = {
header,
scopes,
filterParam,
};
export const LABEL_AGREGATION_NAME = 'labels';

View File

@ -12,7 +12,7 @@ import {
} from '@gitlab/ui';
// eslint-disable-next-line no-restricted-imports
import { mapActions, mapState, mapGetters } from 'vuex';
import { uniq } from 'lodash';
import { difference, uniq } from 'lodash';
import { rgbFromHex } from '@gitlab/ui/dist/utils/utils';
import { slugify } from '~/lib/utils/text_utility';
@ -26,7 +26,8 @@ import {
SEARCH_BOX_INDEX,
SEARCH_RESULTS_DESCRIPTION,
SEARCH_INPUT_DESCRIPTION,
labelFilterData,
LABEL_FILTER_PARAM,
LABEL_FILTER_HEADER,
} from './data';
import { trackSelectCheckbox, trackOpenDropdown } from './tracking';
@ -50,6 +51,7 @@ export default {
return {
currentFocusIndex: SEARCH_BOX_INDEX,
isFocused: false,
combinedSelectedLabels: [],
};
},
i18n: I18N,
@ -83,7 +85,6 @@ export default {
combinedSelectedFilters() {
const appliedSelectedLabelKeys = this.appliedSelectedLabels.map((label) => label.key);
const { labels = [] } = this.query;
return uniq([...appliedSelectedLabelKeys, ...labels]);
},
searchLabels: {
@ -96,16 +97,30 @@ export default {
},
selectedLabels: {
get() {
return this.combinedSelectedFilters;
return this.combinedSelectedLabels;
},
set(value) {
this.setQuery({ key: this.$options.labelFilterData?.filterParam, value });
const labelName = this.getLabelNameById(value);
this.setQuery({ key: this.$options.LABEL_FILTER_PARAM, value: labelName });
trackSelectCheckbox(value);
},
},
},
watch: {
combinedSelectedFilters(newLabels, oldLabels) {
const hasDifference = difference(newLabels, oldLabels).length > 0;
if (hasDifference) {
this.combinedSelectedLabels = newLabels;
}
},
filteredAppliedSelectedLabels(newCount, oldCount) {
if (newCount.length !== oldCount.length) {
this.currentFocusIndex = FIRST_DROPDOWN_INDEX;
}
},
},
async created() {
if (this.urlQuery?.[labelFilterData.filterParam]?.length > 0) {
if (this.urlQuery?.[LABEL_FILTER_PARAM]?.length > 0) {
await this.fetchAllAggregation();
}
},
@ -136,24 +151,34 @@ export default {
return;
}
const { key } = event.target.closest('.gl-label').dataset;
this.closeLabel({ key });
const { title } = event.target.closest('.gl-label').dataset;
this.closeLabel({ title });
},
inactiveLabelColor(label) {
return `rgba(${rgbFromHex(label.color)}, 0.3)`;
},
getLabelNameById(labelIds) {
const labelNames = labelIds.map((id) => {
const label = this.filteredLabels.find((filteredLabel) => {
return filteredLabel.key === String(id);
});
return label?.title;
});
return labelNames;
},
},
FIRST_DROPDOWN_INDEX,
SEARCH_RESULTS_DESCRIPTION,
SEARCH_INPUT_DESCRIPTION,
labelFilterData,
LABEL_FILTER_PARAM,
LABEL_FILTER_HEADER,
};
</script>
<template>
<div class="label-filter gl-relative gl-pb-0 md:gl-pt-0">
<div class="gl-mb-2 gl-text-sm gl-font-bold" data-testid="label-filter-title">
{{ $options.labelFilterData.header }}
{{ $options.LABEL_FILTER_HEADER }}
</div>
<div>
<gl-label
@ -178,9 +203,9 @@ export default {
/>
<gl-label
v-for="label in appliedSelectedLabels"
:key="label.key"
:key="label.title"
class="gl-mb-2 gl-mr-2 gl-bg-gray-10"
:data-key="label.key"
:data-title="label.title"
:background-color="label.color"
:title="label.title"
:show-close-button="true"

View File

@ -4,7 +4,7 @@ import axios from '~/lib/utils/axios_utils';
import { visitUrl, setUrlParams, getNormalizedURL, updateHistory } from '~/lib/utils/url_utility';
import { logError } from '~/lib/logger';
import { __ } from '~/locale';
import { labelFilterData } from '~/search/sidebar/components/label_filter/data';
import { LABEL_FILTER_PARAM } from '~/search/sidebar/components/label_filter/data';
import { SCOPE_BLOB, SEARCH_TYPE_ZOEKT } from '~/search/sidebar/constants';
import {
GROUPS_LOCAL_STORAGE_KEY,
@ -146,10 +146,9 @@ export const resetQuery = ({ state }) => {
);
};
export const closeLabel = ({ state, commit }, { key }) => {
const labels = state?.query?.labels.filter((labelKey) => labelKey !== key);
setQuery({ state, commit }, { key: labelFilterData.filterParam, value: labels });
export const closeLabel = ({ state, commit }, { title }) => {
const labels = state?.query?.[LABEL_FILTER_PARAM].filter((labelName) => labelName !== title);
setQuery({ state, commit }, { key: LABEL_FILTER_PARAM, value: labels });
};
export const setLabelFilterSearch = ({ commit }, { value }) => {

View File

@ -1,7 +1,7 @@
import { statusFilterData } from '~/search/sidebar/components/status_filter/data';
import { confidentialFilterData } from '~/search/sidebar/components/confidentiality_filter/data';
import { languageFilterData } from '~/search/sidebar/components/language_filter/data';
import { labelFilterData } from '~/search/sidebar/components/label_filter/data';
import { LABEL_FILTER_PARAM } from '~/search/sidebar/components/label_filter/data';
import { archivedFilterData } from '~/search/sidebar/components/archived_filter/data';
import { INCLUDE_FORKED_FILTER_PARAM } from '~/search/sidebar/components/forks_filter/index.vue';
import { s__ } from '~/locale';
@ -18,7 +18,7 @@ export const SIDEBAR_PARAMS = [
statusFilterData.filterParam,
confidentialFilterData.filterParam,
languageFilterData.filterParam,
labelFilterData.filterParam,
LABEL_FILTER_PARAM,
archivedFilterData.filterParam,
INCLUDE_FORKED_FILTER_PARAM,
];

View File

@ -1,6 +1,9 @@
import { findKey, intersection } from 'lodash';
import { languageFilterData } from '~/search/sidebar/components/language_filter/data';
import { labelFilterData } from '~/search/sidebar/components/label_filter/data';
import {
LABEL_FILTER_PARAM,
LABEL_AGREGATION_NAME,
} from '~/search/sidebar/components/label_filter/data';
import {
formatSearchResultCount,
addCountOverLimit,
@ -10,8 +13,8 @@ import {
import { PROJECT_DATA, SCOPE_BLOB } from '~/search/sidebar/constants';
import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY, ICON_MAP } from './constants';
const queryLabelFilters = (state) => state?.query?.[labelFilterData.filterParam] || [];
const urlQueryLabelFilters = (state) => state?.urlQuery?.[labelFilterData.filterParam] || [];
const queryLabelFilters = (state) => state?.query?.[LABEL_FILTER_PARAM] || [];
const urlQueryLabelFilters = (state) => state?.urlQuery?.[LABEL_FILTER_PARAM] || [];
const appliedSelectedLabelsKeys = (state) =>
intersection(urlQueryLabelFilters(state), queryLabelFilters(state));
@ -19,8 +22,11 @@ const appliedSelectedLabelsKeys = (state) =>
const unselectedLabelsKeys = (state) =>
urlQueryLabelFilters(state)?.filter((label) => !queryLabelFilters(state)?.includes(label));
const unappliedNewLabelKeys = (state) =>
state?.query?.labels?.filter((label) => !urlQueryLabelFilters(state)?.includes(label));
const unappliedNewLabelKeys = (state) => {
return state?.query?.[LABEL_FILTER_PARAM]?.filter(
(label) => !urlQueryLabelFilters(state)?.includes(label),
);
};
export const queryLanguageFilters = (state) => state.query[languageFilterData.filterParam] || [];
@ -42,9 +48,8 @@ export const languageAggregationBuckets = (state) => {
export const labelAggregationBuckets = (state) => {
return (
state?.aggregations?.data?.find(
(aggregation) => aggregation.name === labelFilterData.filterParam,
)?.buckets || []
state?.aggregations?.data?.find((aggregation) => aggregation.name === LABEL_AGREGATION_NAME)
?.buckets || []
);
};
@ -58,26 +63,26 @@ export const filteredLabels = (state) => {
};
export const filteredAppliedSelectedLabels = (state) =>
filteredLabels(state)?.filter((label) => urlQueryLabelFilters(state)?.includes(label.key));
filteredLabels(state)?.filter((label) => urlQueryLabelFilters(state)?.includes(label.title));
export const appliedSelectedLabels = (state) => {
return labelAggregationBuckets(state)?.filter((label) =>
appliedSelectedLabelsKeys(state)?.includes(label.key),
appliedSelectedLabelsKeys(state)?.includes(label.title),
);
};
export const filteredUnselectedLabels = (state) =>
filteredLabels(state)?.filter((label) => !urlQueryLabelFilters(state)?.includes(label.key));
filteredLabels(state)?.filter((label) => !urlQueryLabelFilters(state)?.includes(label.title));
export const unselectedLabels = (state) =>
labelAggregationBuckets(state).filter((label) =>
unselectedLabelsKeys(state)?.includes(label.key),
unselectedLabelsKeys(state)?.includes(label.title),
);
export const unappliedNewLabels = (state) =>
labelAggregationBuckets(state).filter((label) =>
unappliedNewLabelKeys(state)?.includes(label.key),
);
labelAggregationBuckets(state).filter((label) => {
return unappliedNewLabelKeys(state)?.includes(label.title);
});
export const currentScope = (state) => findKey(state.navigation, { active: true });

View File

@ -3,6 +3,7 @@ import AccessorUtilities from '~/lib/utils/accessor';
import { formatNumber } from '~/locale';
import { joinPaths, queryToObject, objectToQuery, getBaseURL } from '~/lib/utils/url_utility';
import { languageFilterData } from '~/search/sidebar/components/language_filter/data';
import { LABEL_AGREGATION_NAME } from '~/search/sidebar/components/label_filter/data';
import {
MAX_FREQUENT_ITEMS,
MAX_FREQUENCY,
@ -151,6 +152,12 @@ const sortLanguages = (state, entries) => {
return orderBy(entries, [({ key }) => queriedLanguagesSet.has(key), 'count'], ['desc', 'desc']);
};
const getUniqueNamesOnly = (items) => {
return items.filter(
(item, index, array) => index === array.findIndex((obj) => obj.title === item.title),
);
};
export const prepareSearchAggregations = (state, aggregationData) =>
aggregationData.map((item) => {
if (item?.name === LANGUAGE_AGGREGATION_NAME) {
@ -160,6 +167,13 @@ export const prepareSearchAggregations = (state, aggregationData) =>
};
}
if (item?.name === LABEL_AGREGATION_NAME) {
return {
...item,
buckets: getUniqueNamesOnly(item.buckets),
};
}
return item;
});

View File

@ -42,6 +42,8 @@
}
}
// Vertical line gradient for specific system note icons
.system-note-icon-v2 {
&.icon-success{
--bg-color: var(--green-100, #{$green-100});
@ -68,6 +70,21 @@
}
}
// Remove beginning/end of system note gradient activity line
.system-note-v2 {
&:first-of-type {
.system-note-icon-v2::before {
@include activity-line-gradient(transparent, transparent);
}
}
&:last-of-type {
.system-note-icon-v2::after {
@include activity-line-gradient(transparent, transparent);
}
}
}
.system-note-v2:first-child :is(.system-note-icon-v2::before, .system-note-icon-v2::after) {
display: none;
}

View File

@ -653,3 +653,11 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
@apply gl-bg-subtle;
}
}
// Timeline avatars
// stylelint-disable-next-line gitlab/no-gl-class
.timeline-avatar .gl-avatar-link {
@apply gl-bg-default;
border-radius: 50%;
}

View File

@ -2,6 +2,7 @@
- show_code_owners = @project.licensed_feature_available?(:code_owner_approval_required)
- show_status_checks = @project.licensed_feature_available?(:external_status_checks)
- show_approvers = @project.licensed_feature_available?(:merge_request_approvers)
- show_enterprise_access_levels = @project.licensed_feature_available?(:protected_refs_for_users)
= render ::Layouts::SettingsBlockComponent.new(_('Branch rules'),
id: 'branch-rules',
@ -14,4 +15,4 @@
= _('Define rules for who can push, merge, and the required approvals for each branch.')
= link_to(_('Leave feedback.'), 'https://gitlab.com/gitlab-org/gitlab/-/issues/388149', target: '_blank', rel: 'noopener noreferrer')
- c.with_body do
#js-branch-rules{ data: { project_path: @project.full_path, branch_rules_path: project_settings_repository_branch_rules_path(@project), show_code_owners: show_code_owners.to_s, show_status_checks: show_status_checks.to_s, show_approvers: show_approvers.to_s } }
#js-branch-rules{ data: { project_path: @project.full_path, branch_rules_path: project_settings_repository_branch_rules_path(@project), show_code_owners: show_code_owners.to_s, show_status_checks: show_status_checks.to_s, show_approvers: show_approvers.to_s, show_enterprise_access_levels: show_enterprise_access_levels.to_s } }

View File

@ -3,4 +3,4 @@
%h4.pt-3.pb-3= _("Validate your GitLab CI configuration")
#js-ci-lint{ data: { endpoint: project_ci_lint_path(@project), pipeline_simulation_help_page_path: help_page_path('ci/lint', anchor: 'simulate-a-pipeline') , lint_help_page_path: help_page_path('ci/lint', anchor: 'check-cicd-syntax') } }
#js-ci-lint{ data: { endpoint: project_ci_lint_path(@project), pipeline_simulation_help_page_path: help_page_path('ci/yaml/lint', anchor: 'simulate-a-pipeline') , lint_help_page_path: help_page_path('ci/yaml/lint', anchor: 'check-cicd-syntax') } }

View File

@ -7,5 +7,5 @@ feature_categories:
description: Dependency list exported data
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104361
milestone: '15.7'
gitlab_schema: gitlab_main
gitlab_schema: gitlab_main_cell
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/454947

View File

@ -249,6 +249,16 @@ IP addresses that have been added to your IP allowlist can be viewed on the Conf
Specify a comma separated list of IP addresses that can access your GitLab Dedicated instance in your [support ticket](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=4414917877650). The IP addresses are then added to the IP allowlist for your instance.
#### Enable OpenID Connect for your IP allowlist
Using [GitLab as an OpenID Connect identity provider](../../integration/openid_connect_provider.md) requires internet access to the OpenID Connect verification endpoint.
To enable access to the OpenID Connect endpoint while maintaining your IP allowlist:
- In a [support ticket](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=4414917877650), request to allow access to the OpenID Connect endpoint.
The configuration is applied during the next maintenance window.
### SAML
You can [configure SAML single sign-on (SSO)](../../integration/saml.md#configure-saml-support-in-gitlab) for your GitLab Dedicated instance.

View File

@ -25,7 +25,7 @@ POST /projects/:id/ci/lint
| Attribute | Type | Required | Description |
|----------------|---------|----------|-------------|
| `content` | string | Yes | The CI/CD configuration content. |
| `dry_run` | boolean | No | Run [pipeline creation simulation](../ci/lint.md#simulate-a-pipeline), or only do static check. Default: `false`. |
| `dry_run` | boolean | No | Run [pipeline creation simulation](../ci/yaml/lint.md#simulate-a-pipeline), or only do static check. Default: `false`. |
| `include_jobs` | boolean | No | If the list of jobs that would exist in a static check or pipeline simulation should be included in the response. Default: `false`. |
| `ref` | string | No | When `dry_run` is `true`, sets the branch or tag context to use to validate the CI/CD YAML configuration. Defaults to the project's default branch when not set. |

View File

@ -73,12 +73,12 @@ latest version of the schema.
#### Verify syntax with CI Lint tool
You can use the [CI Lint tool](lint.md) to verify that the syntax of a CI/CD configuration
You can use the [CI Lint tool](yaml/lint.md) to verify that the syntax of a CI/CD configuration
snippet is correct. Paste in full `.gitlab-ci.yml` files or individual job configurations,
to verify the basic syntax.
When a `.gitlab-ci.yml` file is present in a project, you can also use the CI Lint
tool to [simulate the creation of a full pipeline](lint.md#simulate-a-pipeline).
tool to [simulate the creation of a full pipeline](yaml/lint.md#simulate-a-pipeline).
It does deeper verification of the configuration syntax.
### Use pipeline names
@ -339,7 +339,7 @@ configuration into more independent [parent-child pipelines](../ci/pipelines/pip
Pipeline configuration warnings are shown when you:
- [Validate configuration with the CI Lint tool](lint.md).
- [Validate configuration with the CI Lint tool](yaml/lint.md).
- [Manually run a pipeline](pipelines/index.md#run-a-pipeline-manually).
### `Job may allow multiple pipelines to run for a single action` warning

View File

@ -589,7 +589,7 @@ unit_test:
#### Deploy to production
The job `deploy_production` will deploy the app to the production server.
To deploy our app with Envoy, we had to set up the `$SSH_PRIVATE_KEY` variable as an [SSH private key](../../ssh_keys/index.md#ssh-keys-when-using-the-docker-executor).
To deploy our app with Envoy, we had to set up the `$SSH_PRIVATE_KEY` variable as an [SSH private key](../../jobs/ssh_keys.md#ssh-keys-when-using-the-docker-executor).
If the SSH keys have added successfully, we can run Envoy.
As mentioned before, GitLab supports [Continuous Delivery](https://about.gitlab.com/blog/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#continuous-delivery) methods as well.

View File

@ -1,139 +1,11 @@
---
stage: Verify
group: Pipeline Execution
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
redirect_to: 'runners/git_submodules.md'
remove_date: '2024-12-04'
---
# Using Git submodules with GitLab CI/CD
This document was moved to [another location](runners/git_submodules.md).
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
Use [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to keep
a Git repository as a subdirectory of another Git repository. You can clone another
repository into your project and keep your commits separate.
## Configure the `.gitmodules` file
When you use Git submodules, your project should have a file named `.gitmodules`.
You have multiple options to configure it to work in a GitLab CI/CD job.
### Using absolute URLs
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/3198) in GitLab Runner 15.11.
For example, your generated `.gitmodules` configuration might look like the following if:
- Your project is located at `https://gitlab.com/secret-group/my-project`.
- Your project depends on `https://gitlab.com/group/project`, which you want
to include as a submodule.
- You check out your sources with an SSH address like `git@gitlab.com:secret-group/my-project.git`.
```ini
[submodule "project"]
path = project
url = git@gitlab.com:group/project.git
```
In this case, use the [`GIT_SUBMODULE_FORCE_HTTPS`](runners/configure_runners.md#rewrite-submodule-urls-to-https) variable
to instruct GitLab Runner to convert the URL to HTTPS before it clones the submodules.
Alternatively, if you also use HTTPS locally, you can configure an HTTPS URL:
```ini
[submodule "project"]
path = project
url = https://gitlab.com/group/project.git
```
You do not need to configure additional variables in this case, but you need to use a
[personal access token](../user/profile/personal_access_tokens.md) to clone it locally.
### Using relative URLs
WARNING:
If you use relative URLs, submodules may resolve incorrectly in forking workflows.
Use absolute URLs instead if you expect your project to have forks.
When your submodule is on the same GitLab server, you can also use relative URLs in
your `.gitmodules` file:
```ini
[submodule "project"]
path = project
url = ../../project.git
```
The above configuration instructs Git to automatically deduce the URL to
use when cloning sources. You can clone with HTTPS in all your CI/CD jobs, and you
can continue to use SSH to clone locally.
For submodules not located on the same GitLab server, always use the full URL:
```ini
[submodule "project-x"]
path = project-x
url = https://gitserver.com/group/project-x.git
```
## Use Git submodules in CI/CD jobs
To make submodules work correctly in CI/CD jobs:
1. You can set the `GIT_SUBMODULE_STRATEGY` variable to either `normal` or `recursive`
to tell the runner to [fetch your submodules before the job](runners/configure_runners.md#git-submodule-strategy):
```yaml
variables:
GIT_SUBMODULE_STRATEGY: recursive
```
1. For submodules located on the same GitLab server and configured with a Git or SSH URL, make sure
you set the [`GIT_SUBMODULE_FORCE_HTTPS`](runners/configure_runners.md#rewrite-submodule-urls-to-https) variable.
1. Use `GIT_SUBMODULE_DEPTH` to configure the cloning depth of submodules independently of the [`GIT_DEPTH`](runners/configure_runners.md#shallow-cloning) variable:
```yaml
variables:
GIT_SUBMODULE_DEPTH: 1
```
1. You can filter or exclude specific submodules to control which submodules are synchronized using
[`GIT_SUBMODULE_PATHS`](runners/configure_runners.md#sync-or-exclude-specific-submodules-from-ci-jobs).
```yaml
variables:
GIT_SUBMODULE_PATHS: submoduleA submoduleB
```
1. You can provide additional flags to control advanced checkout behavior using
[`GIT_SUBMODULE_UPDATE_FLAGS`](runners/configure_runners.md#git-submodule-update-flags).
```yaml
variables:
GIT_SUBMODULE_STRATEGY: recursive
GIT_SUBMODULE_UPDATE_FLAGS: --jobs 4
```
If you use the [`CI_JOB_TOKEN`](jobs/ci_job_token.md) to clone a submodule in a
pipeline job, the user executing the job must be assigned to a role that has
[permission](../user/permissions.md#cicd) to trigger a pipeline
in the upstream submodule project. Additionally, [CI/CD job token access](jobs/ci_job_token.md#control-job-token-access-to-your-project) must be properly configured in the upstream submodule project.
## Troubleshooting
### Can't find the `.gitmodules` file
The `.gitmodules` file might be hard to find because it is usually a hidden file.
You can check documentation for your specific OS to learn how to find and display
hidden files.
If there is no `.gitmodules` file, it's possible the submodule settings are in a
[`git config`](https://www.atlassian.com/git/tutorials/setting-up-a-repository/git-config) file.
### `fatal: run_command returned non-zero status` error
This error can happen in a job when working with submodules and the `GIT_STRATEGY` is set to `fetch`.
Setting the `GIT_STRATEGY` to `clone` should resolve the issue.
<!-- This redirect file can be deleted after <2024-12-04>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -0,0 +1,442 @@
---
stage: none
group: unassigned
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
---
# Mobile DevOps
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
**Status:** Experiment
Use GitLab Mobile DevOps to quickly build, sign, and release native and cross-platform mobile apps
for Android and iOS using GitLab CI/CD. Mobile DevOps is an experimental feature developed by
[GitLab Incubation Engineering](https://handbook.gitlab.com/handbook/engineering/development/incubation/).
Mobile DevOps is still in development, but you can:
- [Request a feature](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=feature_request).
- [Report a bug](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=report_bug).
- [Share feedback](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=general_feedback).
## Build environments
Get started quickly by using [GitLab-hosted runners](../runners/index.md),
or set up [self-managed runners](https://docs.gitlab.com/runner/#use-self-managed-runners)
for complete control over the build environment.
### Android build environments
Set up an Android build environment by selecting an appropriate Docker image
and adding it to your `.gitlab-ci.yml` file. [Fabernovel](https://hub.docker.com/r/fabernovel/android/tags)
provides a variety of supported Android versions.
For example:
```yaml
test:
image: fabernovel/android:api-33-v1.7.0
stage: test
script:
- fastlane test
```
### iOS build environments
[GitLab hosted runners on macOS](../runners/hosted_runners/macos.md) are in beta.
[Choose an image](../runners/hosted_runners/macos.md#supported-macos-images) to run a job on a macOS GitLab-hosted runner and add it to your `.gitlab-ci.yml` file.
For example:
```yaml
test:
image: macos-14-xcode-15
stage: test
script:
- fastlane test
tags:
- saas-macos-medium-m1
```
## Code signing
All Android and iOS apps must be securely signed before being distributed through
the various app stores. Signing ensures that applications haven't been tampered with
before reaching a user's device.
With [project-level secure files](../secure_files/index.md), you can store the following
in GitLab, so that they can be used to securely sign apps in CI/CD builds:
- Keystores
- Provision profiles
- Signing certificates
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Project-level secure files demo](https://youtu.be/O7FbJu3H2YM).
### Code signing Android projects with fastlane & Gradle
To set up code signing for Android:
1. Upload your keystore and keystore properties files to project-level secure files.
1. Update the Gradle configuration to use those files in the build.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [How to build and release an Android app to Google Play with GitLab](https://youtu.be/u8yC8W2k85U).
#### Create a keystore
Run the following command to generate a keystore file if you don't already have one:
```shell
keytool -genkey -v -keystore release-keystore.jks -storepass password -alias release -keypass password -keyalg RSA -keysize 2048 -validity 10000
```
Next, put the keystore configuration in a file called `release-keystore.properties`,
which should look similar to this example:
```plaintext
storeFile=.secure_files/release-keystore.jks
keyAlias=release
keyPassword=password
storePassword=password
```
After these files are created:
- [Upload them as Secure Files](../secure_files/index.md) in the GitLab project
so they can be used in CI/CD jobs.
- Add both files to your `.gitignore` file so they aren't committed to version control.
#### Configure Gradle
The next step is to configure Gradle to use the newly created keystore. In the app's `build.gradle` file:
1. Immediately after the plugins section, add:
```gradle
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('.secure_files/release-keystore.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
```
1. Anywhere in the `android` block, add:
```gradle
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
```
1. Add the `signingConfig` to the release build type:
```gradle
signingConfig signingConfigs.release
```
With this configuration in place, you can use fastlane to build & sign the app
with the files stored in secure files.
For example:
- Sample `fastlane/Fastfile` file:
```ruby
default_platform(:android)
platform :android do
desc "Create and sign a new build"
lane :build do
gradle(tasks: ["clean", "assembleRelease", "bundleRelease"])
end
end
```
- Sample `.gitlab-ci.yml` file:
```yaml
build:
image: fabernovel/android:api-33-v1.7.0
stage: build
script:
- apt update -y && apt install -y curl
- curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash
- fastlane build
```
### Code sign iOS projects with fastlane
To set up code signing for iOS, you must:
1. Install fastlane locally so you can upload your signing certificates to GitLab.
1. Configure the build to use those files.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [How to build and release an iOS app to Test Flight with GitLab](https://youtu.be/Ar8IsBgP1as).
#### Initialize fastlane
With fastlane installed, start by running:
```shell
fastlane init
```
This command creates a `fastlane` folder in the project with an `Appfile` and a stubbed-out `fastfile`.
During this process, you are prompted for App Store Connect login credentials to generate an app identifier and an App Store app if they don't already exist.
The next step sets up fastlane match to manage code signing files for the project.
Run the following command to generate a `Matchfile` with the configuration:
```shell
fastlane match init
```
This command prompts you to:
- Choose which storage backend you want to use, you must select `gitlab_secure_files`.
- Input your project path, for example `gitlab-org/gitlab`.
#### Generate and upload certificates
Run the following command to generate certificates and profiles in the Apple Developer portal
and upload those files to GitLab:
```shell
PRIVATE_TOKEN=YOUR-TOKEN bundle exec fastlane match development
```
In this example:
- `YOUR-TOKEN` must be either a personal or project access token with Maintainer role for the GitLab project.
- Replace `development` with the type of build you want to sign, for example `appstore` or `ad-hoc`.
You can view the files in your project's CI/CD settings as soon as the command completes.
#### Upload-only
If you have already created signing certificates and provisioning profiles for your project,
you can optionally use `fastlane match import` to load your existing files into GitLab:
```shell
PRIVATE_TOKEN=YOUR-TOKEN bundle exec fastlane match import
```
You are prompted to input the path to your files. After you provide those details,
your files are uploaded and visible in your project's CI/CD settings.
If prompted for the `git_url` during the import, it is safe to leave it blank and press <kbd>enter</kbd>.
With this configuration in place, you can use fastlane to build and sign the app with
the files stored in secure files.
For example:
- Sample `fastlane/Fastfile` file:
```ruby
default_platform(:ios)
platform :ios do
desc "Build and sign the application for development"
lane :build do
setup_ci
match(type: 'development', readonly: is_ci)
build_app(
project: "ios demo.xcodeproj",
scheme: "ios demo",
configuration: "Debug",
export_method: "development"
)
end
end
```
- Sample `.gitlab-ci.yml` file:
```yaml
build_ios:
image: macos-12-xcode-14
stage: build
script:
- fastlane build
tags:
- saas-macos-medium-m1
```
## Distribution
Signed builds can be uploaded to the Google Play Store or Apple App Store by using
the Mobile DevOps Distribution integrations.
### Android distribution with Google Play integration and fastlane
To create an Android distribution with Google Play integration and fastlane, you must:
1. [Create a Google service account](https://docs.fastlane.tools/actions/supply/#setup)
in Google Cloud Platform and grant that account access to the project in Google Play.
1. [Enable the Google Play integration](#enable-google-play-integration).
1. Add the release step to your pipeline.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Google Play integration demo](https://youtu.be/Fxaj3hna4uk).
#### Enable Google Play Integration
Use the [Google Play integration](../../user/project/integrations/google_play.md),
to configure your CI/CD pipelines to connect to the [Google Play Console](https://play.google.com/console/developers)
to build and release Android apps. To enable the integration:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Integrations**.
1. Select **Google Play**.
1. Under **Enable integration**, select the **Active** checkbox.
1. In **Package name**, enter the package name of the app. For example, `com.gitlab.app_name`.
1. In **Service account key (.JSON)** drag or upload your key file.
1. Select **Save changes**.
With the integration enabled, you can use fastlane to distribute a build to Google Play.
For example:
- Sample `fastlane/Fastfile`:
```ruby
default_platform(:android)
platform :android do
desc "Submit a new Beta build to the Google Play store"
lane :beta do
upload_to_play_store(
track: 'internal',
aab: 'app/build/outputs/bundle/release/app-release.aab',
release_status: 'draft'
)
end
end
```
- Sample `.gitlab-ci.yml`:
```yaml
beta:
image: fabernovel/android:api-33-v1.7.0
stage: beta
script:
- fastlane beta
```
### iOS distribution Apple Store integration and fastlane
To create an iOS distribution with the Apple Store integration and fastlane, you must:
1. Generate an API Key for App Store Connect API. In the Apple App Store Connect portal,
[generate a new private key for your project](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api).
1. [Enable the Apple App Store Connect integration](#enable-the-apple-app-store-connect-integration).
1. Add the release step to your pipeline and fastlane configuration.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Apple App Store Connect integration demo](https://youtu.be/CwzAWVgJeK8).
<!-- Video published on 2023-03-17 -->
#### Enable the Apple App Store Connect integration
Prerequisites:
- You must have an Apple ID enrolled in the [Apple Developer Program](https://developer.apple.com/programs/enroll/).
- You must [generate a new private key](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) for your project in the Apple App Store Connect portal.
Use the Apple App Store Connect integration to configure your CI/CD pipelines to connect to [App Store Connect](https://appstoreconnect.apple.com).
With this integration, you can build and release apps for iOS, iPadOS, macOS, tvOS, and watchOS.
To enable the Apple App Store Connect integration in GitLab:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Integrations**.
1. Select **Apple App Store Connect**.
1. Under **Enable integration**, select the **Active** checkbox.
1. Provide the Apple App Store Connect configuration information:
- **Issuer ID**: The Apple App Store Connect issuer ID.
- **Key ID**: The key ID of the generated private key.
- **Private key**: The generated private key. You can download this key only once.
- **Protected branches and tags only**: Enable to set variables on protected branches and tags only.
1. Select **Save changes**.
With the integration enabled, you can use fastlane to distribute a build to TestFlight
and the Apple App Store.
For example:
- Sample `fastlane/Fastfile`:
```ruby
default_platform(:ios)
platform :ios do
desc "Build and sign the application for distribution, upload to TestFlight"
lane :beta do
setup_ci
match(type: 'appstore', readonly: is_ci)
app_store_connect_api_key
increment_build_number(
build_number: latest_testflight_build_number(initial_build_number: 1) + 1,
xcodeproj: "ios demo.xcodeproj"
)
build_app(
project: "ios demo.xcodeproj",
scheme: "ios demo",
configuration: "Release",
export_method: "app-store"
)
upload_to_testflight
end
end
```
- Sample `.gitlab-ci.yml`:
```yaml
beta_ios:
image: macos-12-xcode-14
stage: beta
script:
- fastlane beta
```
## Review apps for mobile
You can use [review apps](../review_apps/index.md) to preview changes directly from a merge request.
This feature is possible through an integration with [Appetize.io](https://appetize.io/).
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Review apps for mobile setup instructions](https://youtu.be/X15mI19TXa4).
To get started, see the [setup instructions](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/readme/-/issues/15).
## Sample Reference Projects
See the sample reference projects below for complete build, sign, and release pipeline examples for various platforms. A list of all available projects can be found in [the Mobile DevOps Demo Projects group](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/demo-projects/).
- [Android Demo](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/demo-projects/android_demo)
- [iOS Demo](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/demo-projects/ios-demo)
- [Flutter Demo](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/demo-projects/flutter-demo)
## Mobile DevOps Blog
Additional reference material can be found in the [DevOps section](https://about.gitlab.com/blog/categories/devops/) of the GitLab blog.

231
doc/ci/jobs/ssh_keys.md Normal file
View File

@ -0,0 +1,231 @@
---
stage: Verify
group: Pipeline Security
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
---
# Using SSH keys with GitLab CI/CD
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
GitLab currently doesn't have built-in support for managing SSH keys in a build
environment (where the GitLab Runner runs).
Use SSH keys when you want to:
- Check out internal submodules.
- Download private packages using your package manager. For example, Bundler.
- Deploy your application to your own server or, for example, Heroku.
- Execute SSH commands from the build environment to a remote server.
- Rsync files from the build environment to a remote server.
If anything of the above rings a bell, then you most likely need an SSH key.
The most widely supported method is to inject an SSH key into your build
environment by extending your `.gitlab-ci.yml`, and it's a solution that works
with any type of [executor](https://docs.gitlab.com/runner/executors/)
(like Docker or shell, for example).
## Create and use an SSH key
To create and use an SSH key in GitLab CI/CD:
1. [Create a new SSH key pair](../../user/ssh.md#generate-an-ssh-key-pair) locally with `ssh-keygen`.
1. Add the private key as a [file type CI/CD variable](../variables/index.md#for-a-project) to
your project. The variable value must end in a newline (`LF` character). To add a newline, press <kbd>Enter</kbd> or <kbd>Return</kbd>
at the end of the last line of the SSH key before saving it in the CI/CD settings.
1. Run the [`ssh-agent`](https://linux.die.net/man/1/ssh-agent) in the job, which loads
the private key.
1. Copy the public key to the servers you want to have access to (usually in
`~/.ssh/authorized_keys`) or add it as a [deploy key](../../user/project/deploy_keys/index.md)
if you are accessing a private GitLab repository.
In the following example, the `ssh-add -` command does not display the value of
`$SSH_PRIVATE_KEY` in the job log, though it could be exposed if you enable
[debug logging](../variables/index.md#enable-debug-logging). You might also want to
check the [visibility of your pipelines](../pipelines/settings.md#change-which-users-can-view-your-pipelines).
## SSH keys when using the Docker executor
When your CI/CD jobs run inside Docker containers (meaning the environment is
contained) and you want to deploy your code in a private server, you need a way
to access it. In this case, you can use an SSH key pair.
1. You first must create an SSH key pair. For more information, follow
the instructions to [generate an SSH key](../../user/ssh.md#generate-an-ssh-key-pair).
**Do not** add a passphrase to the SSH key, or the `before_script` will
prompt for it.
1. Create a new [file type CI/CD variable](../variables/index.md#for-a-project).
- In the **Key** field, enter `SSH_PRIVATE_KEY`.
- In the **Value** field, paste the content of your _private_ key from the key pair that you created earlier.
Make sure the file ends with a newline. To add a newline, press
<kbd>Enter</kbd> or <kbd>Return</kbd> at the end of the last line of the SSH key before saving your changes.
1. Modify your `.gitlab-ci.yml` with a `before_script` action. In the following
example, a Debian based image is assumed. Edit to your needs:
```yaml
before_script:
##
## Install ssh-agent if not already installed, it is required by Docker.
## (change apt-get to yum if you use an RPM-based image)
##
- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'
##
## Run ssh-agent (inside the build environment)
##
- eval $(ssh-agent -s)
##
## Give the right permissions, otherwise ssh-add will refuse to add files
## Add the SSH key stored in SSH_PRIVATE_KEY file type CI/CD variable to the agent store
##
- chmod 400 "$SSH_PRIVATE_KEY"
- ssh-add "$SSH_PRIVATE_KEY"
##
## Create the SSH directory and give it the right permissions
##
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
##
## Optionally, if you will be using any Git commands, set the user name and
## and email.
##
# - git config --global user.email "user@example.com"
# - git config --global user.name "User name"
```
The [`before_script`](../yaml/index.md#before_script) can be set as a default
or per-job.
1. Make sure the private server's [SSH host keys are verified](#verifying-the-ssh-host-keys).
1. As a final step, add the _public_ key from the one you created in the first
step to the services that you want to have an access to from inside the build
environment. If you are accessing a private GitLab repository you must add
it as a [deploy key](../../user/project/deploy_keys/index.md).
That's it! You can now have access to private servers or repositories in your
build environment.
## SSH keys when using the Shell executor
If you are using the Shell executor and not Docker, it is easier to set up an
SSH key.
You can generate the SSH key from the machine that GitLab Runner is installed
on, and use that key for all projects that are run on this machine.
1. First, sign in to the server that runs your jobs.
1. Then, from the terminal, sign in as the `gitlab-runner` user:
```shell
sudo su - gitlab-runner
```
1. Generate the SSH key pair as described in the instructions to
[generate an SSH key](../../user/ssh.md#generate-an-ssh-key-pair).
**Do not** add a passphrase to the SSH key, or the `before_script` will
prompt for it.
1. As a final step, add the _public_ key from the one you created earlier to the
services that you want to have an access to from inside the build environment.
If you are accessing a private GitLab repository you must add it as a
[deploy key](../../user/project/deploy_keys/index.md).
After generating the key, try to sign in to the remote server to accept the
fingerprint:
```shell
ssh example.com
```
For accessing repositories on GitLab.com, you would use `git@gitlab.com`.
## Verifying the SSH host keys
It is a good practice to check the private server's own public key to make sure
you are not being targeted by a man-in-the-middle attack. If anything
suspicious happens, you notice it because the job fails (the SSH
connection fails when the public keys don't match).
To find out the host keys of your server, run the `ssh-keyscan` command from a
trusted network (ideally, from the private server itself):
```shell
## Use the domain name
ssh-keyscan example.com
## Or use an IP
ssh-keyscan 10.0.2.2
```
Create a new [file type CI/CD variable](../variables/index.md#use-file-type-cicd-variables)
with `SSH_KNOWN_HOSTS` as "Key", and as a "Value" add the output of `ssh-keyscan`.
Make sure the file ends with a newline. To add a newline, press <kbd>Enter</kbd> or <kbd>Return</kbd>
at the end of the last line of the SSH key before saving your changes.
If you must connect to multiple servers, all the server host keys
must be collected in the **Value** of the variable, one key per line.
NOTE:
By using a file type CI/CD variable instead of `ssh-keyscan` directly inside
`.gitlab-ci.yml`, it has the benefit that you don't have to change `.gitlab-ci.yml`
if the host domain name changes for some reason. Also, the values are predefined
by you, meaning that if the host keys suddenly change, the CI/CD job doesn't fail,
so there's something wrong with the server or the network.
Now that the `SSH_KNOWN_HOSTS` variable is created, in addition to the
[content of `.gitlab-ci.yml`](#ssh-keys-when-using-the-docker-executor)
above, you must add:
```yaml
before_script:
##
## Assuming you created the SSH_KNOWN_HOSTS file type CI/CD variable, uncomment the
## following two lines.
##
- cp "$SSH_KNOWN_HOSTS" ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
##
## Alternatively, use ssh-keyscan to scan the keys of your private server.
## Replace example.com with your private server's domain name. Repeat that
## command if you have more than one server to connect to. Include the -t
## flag to specify the key type.
##
# - ssh-keyscan -t rsa,ed25519 example.com >> ~/.ssh/known_hosts
# - chmod 644 ~/.ssh/known_hosts
##
## You can optionally disable host key checking. Be aware that by adding that
## you are susceptible to man-in-the-middle attacks.
## WARNING: Use this only with the Docker executor, if you use it with shell
## you will overwrite your user's SSH config.
##
# - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" >> ~/.ssh/config'
```
## Use SSH key without a file type CI/CD variable
If you do not want to use a file type CI/CD variable, the [example SSH Project](https://gitlab.com/gitlab-examples/ssh-private-key/)
shows an alternative method. This method uses a regular CI/CD variable instead of
the file type variable recommended above.
## Troubleshooting
### `Error loading key "/builds/path/SSH_PRIVATE_KEY": error in libcrypto` message
This message can be returned if there is a formatting error with the SSH key.
When saving the SSH key as a [file type CI/CD variable](../variables/index.md#use-file-type-cicd-variables),
the value must end with a newline (`LF` character). To add a newline, press <kbd>Enter</kbd> or <kbd>Return</kbd>
at the end of the `-----END OPENSSH PRIVATE KEY-----` line of the SSH key before saving
the variable.

View File

@ -1,57 +1,11 @@
---
stage: Verify
group: Pipeline Authoring
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
redirect_to: 'yaml/lint.md'
remove_date: '2024-12-04'
---
# Validate GitLab CI/CD configuration
This document was moved to [another location](yaml/lint.md).
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
Use the CI Lint tool to check the validity of GitLab CI/CD configuration.
You can validate the syntax from a `.gitlab-ci.yml` file or any other sample CI/CD configuration.
This tool checks for syntax and logic errors, and can simulate pipeline
creation to try to find more complicated configuration problems.
If you use the [pipeline editor](pipeline_editor/index.md), it verifies configuration
syntax automatically.
If you use VS Code, you can validate your CI/CD configuration with the
[GitLab Workflow VS Code extension](../editor_extensions/visual_studio_code/index.md).
## Check CI/CD syntax
The CI lint tool checks the syntax of GitLab CI/CD configuration, including
configuration added with the [`includes` keyword](yaml/index.md#include).
To check CI/CD configuration with the CI lint tool:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Build > Pipeline editor**.
1. Select the **Validate** tab.
1. Select **Lint CI/CD sample**.
1. Paste a copy of the CI/CD configuration you want to check into the text box.
1. Select **Validate**.
## Simulate a pipeline
You can simulate the creation of a GitLab CI/CD pipeline to find more complicated issues,
including problems with [`needs`](yaml/index.md#needs) and [`rules`](yaml/index.md#rules)
configuration. A simulation runs as a Git `push` event on the default branch.
Prerequisites:
- You must have [permissions](../user/permissions.md#project-members-permissions)
to create pipelines on this branch to validate with a simulation.
To simulate a pipeline:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Build > Pipeline editor**.
1. Select the **Validate** tab.
1. Select **Lint CI/CD sample**.
1. Paste a copy of the CI/CD configuration you want to check into the text box.
1. Select **Simulate pipeline creation for the default branch**.
1. Select **Validate**.
<!-- This redirect file can be deleted after <2024-12-04>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -1,442 +1,11 @@
---
stage: none
group: unassigned
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
redirect_to: 'jobs/mobile_devops.md'
remove_date: '2024-12-04'
---
# Mobile DevOps
This document was moved to [another location](jobs/mobile_devops.md).
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
**Status:** Experiment
Use GitLab Mobile DevOps to quickly build, sign, and release native and cross-platform mobile apps
for Android and iOS using GitLab CI/CD. Mobile DevOps is an experimental feature developed by
[GitLab Incubation Engineering](https://handbook.gitlab.com/handbook/engineering/development/incubation/).
Mobile DevOps is still in development, but you can:
- [Request a feature](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=feature_request).
- [Report a bug](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=report_bug).
- [Share feedback](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=general_feedback).
## Build environments
Get started quickly by using [GitLab-hosted runners](../ci/runners/index.md),
or set up [self-managed runners](https://docs.gitlab.com/runner/#use-self-managed-runners)
for complete control over the build environment.
### Android build environments
Set up an Android build environment by selecting an appropriate Docker image
and adding it to your `.gitlab-ci.yml` file. [Fabernovel](https://hub.docker.com/r/fabernovel/android/tags)
provides a variety of supported Android versions.
For example:
```yaml
test:
image: fabernovel/android:api-33-v1.7.0
stage: test
script:
- fastlane test
```
### iOS build environments
[GitLab hosted runners on macOS](../ci/runners/hosted_runners/macos.md) are in beta.
[Choose an image](../ci/runners/hosted_runners/macos.md#supported-macos-images) to run a job on a macOS GitLab-hosted runner and add it to your `.gitlab-ci.yml` file.
For example:
```yaml
test:
image: macos-14-xcode-15
stage: test
script:
- fastlane test
tags:
- saas-macos-medium-m1
```
## Code signing
All Android and iOS apps must be securely signed before being distributed through
the various app stores. Signing ensures that applications haven't been tampered with
before reaching a user's device.
With [project-level secure files](secure_files/index.md), you can store the following
in GitLab, so that they can be used to securely sign apps in CI/CD builds:
- Keystores
- Provision profiles
- Signing certificates
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Project-level secure files demo](https://youtu.be/O7FbJu3H2YM).
### Code signing Android projects with fastlane & Gradle
To set up code signing for Android:
1. Upload your keystore and keystore properties files to project-level secure files.
1. Update the Gradle configuration to use those files in the build.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [How to build and release an Android app to Google Play with GitLab](https://youtu.be/u8yC8W2k85U).
#### Create a keystore
Run the following command to generate a keystore file if you don't already have one:
```shell
keytool -genkey -v -keystore release-keystore.jks -storepass password -alias release -keypass password -keyalg RSA -keysize 2048 -validity 10000
```
Next, put the keystore configuration in a file called `release-keystore.properties`,
which should look similar to this example:
```plaintext
storeFile=.secure_files/release-keystore.jks
keyAlias=release
keyPassword=password
storePassword=password
```
After these files are created:
- [Upload them as Secure Files](secure_files/index.md) in the GitLab project
so they can be used in CI/CD jobs.
- Add both files to your `.gitignore` file so they aren't committed to version control.
#### Configure Gradle
The next step is to configure Gradle to use the newly created keystore. In the app's `build.gradle` file:
1. Immediately after the plugins section, add:
```gradle
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('.secure_files/release-keystore.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
```
1. Anywhere in the `android` block, add:
```gradle
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
```
1. Add the `signingConfig` to the release build type:
```gradle
signingConfig signingConfigs.release
```
With this configuration in place, you can use fastlane to build & sign the app
with the files stored in secure files.
For example:
- Sample `fastlane/Fastfile` file:
```ruby
default_platform(:android)
platform :android do
desc "Create and sign a new build"
lane :build do
gradle(tasks: ["clean", "assembleRelease", "bundleRelease"])
end
end
```
- Sample `.gitlab-ci.yml` file:
```yaml
build:
image: fabernovel/android:api-33-v1.7.0
stage: build
script:
- apt update -y && apt install -y curl
- curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash
- fastlane build
```
### Code sign iOS projects with fastlane
To set up code signing for iOS, you must:
1. Install fastlane locally so you can upload your signing certificates to GitLab.
1. Configure the build to use those files.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [How to build and release an iOS app to Test Flight with GitLab](https://youtu.be/Ar8IsBgP1as).
#### Initialize fastlane
With fastlane installed, start by running:
```shell
fastlane init
```
This command creates a `fastlane` folder in the project with an `Appfile` and a stubbed-out `fastfile`.
During this process, you are prompted for App Store Connect login credentials to generate an app identifier and an App Store app if they don't already exist.
The next step sets up fastlane match to manage code signing files for the project.
Run the following command to generate a `Matchfile` with the configuration:
```shell
fastlane match init
```
This command prompts you to:
- Choose which storage backend you want to use, you must select `gitlab_secure_files`.
- Input your project path, for example `gitlab-org/gitlab`.
#### Generate and upload certificates
Run the following command to generate certificates and profiles in the Apple Developer portal
and upload those files to GitLab:
```shell
PRIVATE_TOKEN=YOUR-TOKEN bundle exec fastlane match development
```
In this example:
- `YOUR-TOKEN` must be either a personal or project access token with Maintainer role for the GitLab project.
- Replace `development` with the type of build you want to sign, for example `appstore` or `ad-hoc`.
You can view the files in your project's CI/CD settings as soon as the command completes.
#### Upload-only
If you have already created signing certificates and provisioning profiles for your project,
you can optionally use `fastlane match import` to load your existing files into GitLab:
```shell
PRIVATE_TOKEN=YOUR-TOKEN bundle exec fastlane match import
```
You are prompted to input the path to your files. After you provide those details,
your files are uploaded and visible in your project's CI/CD settings.
If prompted for the `git_url` during the import, it is safe to leave it blank and press <kbd>enter</kbd>.
With this configuration in place, you can use fastlane to build and sign the app with
the files stored in secure files.
For example:
- Sample `fastlane/Fastfile` file:
```ruby
default_platform(:ios)
platform :ios do
desc "Build and sign the application for development"
lane :build do
setup_ci
match(type: 'development', readonly: is_ci)
build_app(
project: "ios demo.xcodeproj",
scheme: "ios demo",
configuration: "Debug",
export_method: "development"
)
end
end
```
- Sample `.gitlab-ci.yml` file:
```yaml
build_ios:
image: macos-12-xcode-14
stage: build
script:
- fastlane build
tags:
- saas-macos-medium-m1
```
## Distribution
Signed builds can be uploaded to the Google Play Store or Apple App Store by using
the Mobile DevOps Distribution integrations.
### Android distribution with Google Play integration and fastlane
To create an Android distribution with Google Play integration and fastlane, you must:
1. [Create a Google service account](https://docs.fastlane.tools/actions/supply/#setup)
in Google Cloud Platform and grant that account access to the project in Google Play.
1. [Enable the Google Play integration](#enable-google-play-integration).
1. Add the release step to your pipeline.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Google Play integration demo](https://youtu.be/Fxaj3hna4uk).
#### Enable Google Play Integration
Use the [Google Play integration](../user/project/integrations/google_play.md),
to configure your CI/CD pipelines to connect to the [Google Play Console](https://play.google.com/console/developers)
to build and release Android apps. To enable the integration:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Integrations**.
1. Select **Google Play**.
1. Under **Enable integration**, select the **Active** checkbox.
1. In **Package name**, enter the package name of the app. For example, `com.gitlab.app_name`.
1. In **Service account key (.JSON)** drag or upload your key file.
1. Select **Save changes**.
With the integration enabled, you can use fastlane to distribute a build to Google Play.
For example:
- Sample `fastlane/Fastfile`:
```ruby
default_platform(:android)
platform :android do
desc "Submit a new Beta build to the Google Play store"
lane :beta do
upload_to_play_store(
track: 'internal',
aab: 'app/build/outputs/bundle/release/app-release.aab',
release_status: 'draft'
)
end
end
```
- Sample `.gitlab-ci.yml`:
```yaml
beta:
image: fabernovel/android:api-33-v1.7.0
stage: beta
script:
- fastlane beta
```
### iOS distribution Apple Store integration and fastlane
To create an iOS distribution with the Apple Store integration and fastlane, you must:
1. Generate an API Key for App Store Connect API. In the Apple App Store Connect portal,
[generate a new private key for your project](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api).
1. [Enable the Apple App Store Connect integration](#enable-the-apple-app-store-connect-integration).
1. Add the release step to your pipeline and fastlane configuration.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Apple App Store Connect integration demo](https://youtu.be/CwzAWVgJeK8).
<!-- Video published on 2023-03-17 -->
#### Enable the Apple App Store Connect integration
Prerequisites:
- You must have an Apple ID enrolled in the [Apple Developer Program](https://developer.apple.com/programs/enroll/).
- You must [generate a new private key](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) for your project in the Apple App Store Connect portal.
Use the Apple App Store Connect integration to configure your CI/CD pipelines to connect to [App Store Connect](https://appstoreconnect.apple.com).
With this integration, you can build and release apps for iOS, iPadOS, macOS, tvOS, and watchOS.
To enable the Apple App Store Connect integration in GitLab:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Integrations**.
1. Select **Apple App Store Connect**.
1. Under **Enable integration**, select the **Active** checkbox.
1. Provide the Apple App Store Connect configuration information:
- **Issuer ID**: The Apple App Store Connect issuer ID.
- **Key ID**: The key ID of the generated private key.
- **Private key**: The generated private key. You can download this key only once.
- **Protected branches and tags only**: Enable to set variables on protected branches and tags only.
1. Select **Save changes**.
With the integration enabled, you can use fastlane to distribute a build to TestFlight
and the Apple App Store.
For example:
- Sample `fastlane/Fastfile`:
```ruby
default_platform(:ios)
platform :ios do
desc "Build and sign the application for distribution, upload to TestFlight"
lane :beta do
setup_ci
match(type: 'appstore', readonly: is_ci)
app_store_connect_api_key
increment_build_number(
build_number: latest_testflight_build_number(initial_build_number: 1) + 1,
xcodeproj: "ios demo.xcodeproj"
)
build_app(
project: "ios demo.xcodeproj",
scheme: "ios demo",
configuration: "Release",
export_method: "app-store"
)
upload_to_testflight
end
end
```
- Sample `.gitlab-ci.yml`:
```yaml
beta_ios:
image: macos-12-xcode-14
stage: beta
script:
- fastlane beta
```
## Review apps for mobile
You can use [review apps](review_apps/index.md) to preview changes directly from a merge request.
This feature is possible through an integration with [Appetize.io](https://appetize.io/).
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Review apps for mobile setup instructions](https://youtu.be/X15mI19TXa4).
To get started, see the [setup instructions](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/readme/-/issues/15).
## Sample Reference Projects
See the sample reference projects below for complete build, sign, and release pipeline examples for various platforms. A list of all available projects can be found in [the Mobile DevOps Demo Projects group](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/demo-projects/).
- [Android Demo](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/demo-projects/android_demo)
- [iOS Demo](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/demo-projects/ios-demo)
- [Flutter Demo](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/demo-projects/flutter-demo)
## Mobile DevOps Blog
Additional reference material can be found in the [DevOps section](https://about.gitlab.com/blog/categories/devops/) of the GitLab blog.
<!-- This redirect file can be deleted after <2024-12-04>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -52,7 +52,7 @@ This tool checks for syntax and logical errors but goes into more detail than th
automatic [validation](#validate-ci-configuration) in the editor.
The results are updated in real-time. Any changes you make to the configuration are
reflected in the CI lint. It displays the same results as the existing [CI Lint tool](../lint.md).
reflected in the CI lint. It displays the same results as the existing [CI Lint tool](../yaml/lint.md).
![Linting errors in a CI configuration](img/pipeline_editor_lint_v13_8.png)
@ -63,7 +63,7 @@ reflected in the CI lint. It displays the same results as the existing [CI Lint
To look for pipeline syntax and logic issues, you can simulate the creation of a
GitLab CI/CD pipeline in the **Validate** tab. A pipeline simulation can help find
problems such as incorrect `rules` and `needs` job dependencies, and is similar to
simulations in the [CI Lint tool](../lint.md#simulate-a-pipeline).
simulations in the [CI Lint tool](../yaml/lint.md#simulate-a-pipeline).
## View included CI/CD configuration

View File

@ -0,0 +1,139 @@
---
stage: Verify
group: Pipeline Execution
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
---
# Using Git submodules with GitLab CI/CD
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
Use [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to keep
a Git repository as a subdirectory of another Git repository. You can clone another
repository into your project and keep your commits separate.
## Configure the `.gitmodules` file
When you use Git submodules, your project should have a file named `.gitmodules`.
You have multiple options to configure it to work in a GitLab CI/CD job.
### Using absolute URLs
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/3198) in GitLab Runner 15.11.
For example, your generated `.gitmodules` configuration might look like the following if:
- Your project is located at `https://gitlab.com/secret-group/my-project`.
- Your project depends on `https://gitlab.com/group/project`, which you want
to include as a submodule.
- You check out your sources with an SSH address like `git@gitlab.com:secret-group/my-project.git`.
```ini
[submodule "project"]
path = project
url = git@gitlab.com:group/project.git
```
In this case, use the [`GIT_SUBMODULE_FORCE_HTTPS`](configure_runners.md#rewrite-submodule-urls-to-https) variable
to instruct GitLab Runner to convert the URL to HTTPS before it clones the submodules.
Alternatively, if you also use HTTPS locally, you can configure an HTTPS URL:
```ini
[submodule "project"]
path = project
url = https://gitlab.com/group/project.git
```
You do not need to configure additional variables in this case, but you need to use a
[personal access token](../../user/profile/personal_access_tokens.md) to clone it locally.
### Using relative URLs
WARNING:
If you use relative URLs, submodules may resolve incorrectly in forking workflows.
Use absolute URLs instead if you expect your project to have forks.
When your submodule is on the same GitLab server, you can also use relative URLs in
your `.gitmodules` file:
```ini
[submodule "project"]
path = project
url = ../../project.git
```
The above configuration instructs Git to automatically deduce the URL to
use when cloning sources. You can clone with HTTPS in all your CI/CD jobs, and you
can continue to use SSH to clone locally.
For submodules not located on the same GitLab server, always use the full URL:
```ini
[submodule "project-x"]
path = project-x
url = https://gitserver.com/group/project-x.git
```
## Use Git submodules in CI/CD jobs
To make submodules work correctly in CI/CD jobs:
1. You can set the `GIT_SUBMODULE_STRATEGY` variable to either `normal` or `recursive`
to tell the runner to [fetch your submodules before the job](configure_runners.md#git-submodule-strategy):
```yaml
variables:
GIT_SUBMODULE_STRATEGY: recursive
```
1. For submodules located on the same GitLab server and configured with a Git or SSH URL, make sure
you set the [`GIT_SUBMODULE_FORCE_HTTPS`](configure_runners.md#rewrite-submodule-urls-to-https) variable.
1. Use `GIT_SUBMODULE_DEPTH` to configure the cloning depth of submodules independently of the [`GIT_DEPTH`](configure_runners.md#shallow-cloning) variable:
```yaml
variables:
GIT_SUBMODULE_DEPTH: 1
```
1. You can filter or exclude specific submodules to control which submodules are synchronized using
[`GIT_SUBMODULE_PATHS`](configure_runners.md#sync-or-exclude-specific-submodules-from-ci-jobs).
```yaml
variables:
GIT_SUBMODULE_PATHS: submoduleA submoduleB
```
1. You can provide additional flags to control advanced checkout behavior using
[`GIT_SUBMODULE_UPDATE_FLAGS`](configure_runners.md#git-submodule-update-flags).
```yaml
variables:
GIT_SUBMODULE_STRATEGY: recursive
GIT_SUBMODULE_UPDATE_FLAGS: --jobs 4
```
If you use the [`CI_JOB_TOKEN`](../jobs/ci_job_token.md) to clone a submodule in a
pipeline job, the user executing the job must be assigned to a role that has
[permission](../../user/permissions.md#cicd) to trigger a pipeline
in the upstream submodule project. Additionally, [CI/CD job token access](../jobs/ci_job_token.md#control-job-token-access-to-your-project) must be properly configured in the upstream submodule project.
## Troubleshooting
### Can't find the `.gitmodules` file
The `.gitmodules` file might be hard to find because it is usually a hidden file.
You can check documentation for your specific OS to learn how to find and display
hidden files.
If there is no `.gitmodules` file, it's possible the submodule settings are in a
[`git config`](https://www.atlassian.com/git/tutorials/setting-up-a-repository/git-config) file.
### `fatal: run_command returned non-zero status` error
This error can happen in a job when working with submodules and the `GIT_STRATEGY` is set to `fetch`.
Setting the `GIT_STRATEGY` to `clone` should resolve the issue.

View File

@ -13,7 +13,7 @@ DETAILS:
Hosted runners on macOS provide an on-demand macOS environment, fully integrated with GitLab [CI/CD](../../../ci/index.md).
You can use these runners to build, test, and deploy apps for the Apple ecosystem (macOS, iOS, watchOS, tvOS).
Our [Mobile DevOps section](../../../ci/mobile_devops.md#ios-build-environments) provides features, documentation, and guidance on building and deploying mobile applications for iOS.
Our [Mobile DevOps section](../../../ci/jobs/mobile_devops.md#ios-build-environments) provides features, documentation, and guidance on building and deploying mobile applications for iOS.
Hosted runners on macOS are in [beta](../../../policy/experiment-beta-support.md#beta) and available for open source programs and customers in Premium and Ultimate plans.
[General availability](../../../policy/experiment-beta-support.md#generally-available-ga) of Hosted runners on macOS is proposed in [epic 8267](https://gitlab.com/groups/gitlab-org/-/epics/8267).
@ -86,7 +86,7 @@ Before you can integrate GitLab with Apple services, install to a device, or dep
Included in each runner on macOS VM image is [fastlane](https://fastlane.tools/),
an open-source solution aimed at simplifying mobile app deployment.
For information about how to set up code signing for your application, see the instructions in the [Mobile DevOps documentation](../../../ci/mobile_devops.md#code-sign-ios-projects-with-fastlane).
For information about how to set up code signing for your application, see the instructions in the [Mobile DevOps documentation](../../../ci/jobs/mobile_devops.md#code-sign-ios-projects-with-fastlane).
Related topics:

View File

@ -1,231 +1,11 @@
---
stage: Verify
group: Pipeline Security
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
redirect_to: '../jobs/ssh_keys.md'
remove_date: '2024-12-04'
---
# Using SSH keys with GitLab CI/CD
This document was moved to [another location](../jobs/ssh_keys.md).
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
GitLab currently doesn't have built-in support for managing SSH keys in a build
environment (where the GitLab Runner runs).
Use SSH keys when you want to:
- Check out internal submodules.
- Download private packages using your package manager. For example, Bundler.
- Deploy your application to your own server or, for example, Heroku.
- Execute SSH commands from the build environment to a remote server.
- Rsync files from the build environment to a remote server.
If anything of the above rings a bell, then you most likely need an SSH key.
The most widely supported method is to inject an SSH key into your build
environment by extending your `.gitlab-ci.yml`, and it's a solution that works
with any type of [executor](https://docs.gitlab.com/runner/executors/)
(like Docker or shell, for example).
## Create and use an SSH key
To create and use an SSH key in GitLab CI/CD:
1. [Create a new SSH key pair](../../user/ssh.md#generate-an-ssh-key-pair) locally with `ssh-keygen`.
1. Add the private key as a [file type CI/CD variable](../variables/index.md#for-a-project) to
your project. The variable value must end in a newline (`LF` character). To add a newline, press <kbd>Enter</kbd> or <kbd>Return</kbd>
at the end of the last line of the SSH key before saving it in the CI/CD settings.
1. Run the [`ssh-agent`](https://linux.die.net/man/1/ssh-agent) in the job, which loads
the private key.
1. Copy the public key to the servers you want to have access to (usually in
`~/.ssh/authorized_keys`) or add it as a [deploy key](../../user/project/deploy_keys/index.md)
if you are accessing a private GitLab repository.
In the following example, the `ssh-add -` command does not display the value of
`$SSH_PRIVATE_KEY` in the job log, though it could be exposed if you enable
[debug logging](../variables/index.md#enable-debug-logging). You might also want to
check the [visibility of your pipelines](../pipelines/settings.md#change-which-users-can-view-your-pipelines).
## SSH keys when using the Docker executor
When your CI/CD jobs run inside Docker containers (meaning the environment is
contained) and you want to deploy your code in a private server, you need a way
to access it. In this case, you can use an SSH key pair.
1. You first must create an SSH key pair. For more information, follow
the instructions to [generate an SSH key](../../user/ssh.md#generate-an-ssh-key-pair).
**Do not** add a passphrase to the SSH key, or the `before_script` will
prompt for it.
1. Create a new [file type CI/CD variable](../variables/index.md#for-a-project).
- In the **Key** field, enter `SSH_PRIVATE_KEY`.
- In the **Value** field, paste the content of your _private_ key from the key pair that you created earlier.
Make sure the file ends with a newline. To add a newline, press
<kbd>Enter</kbd> or <kbd>Return</kbd> at the end of the last line of the SSH key before saving your changes.
1. Modify your `.gitlab-ci.yml` with a `before_script` action. In the following
example, a Debian based image is assumed. Edit to your needs:
```yaml
before_script:
##
## Install ssh-agent if not already installed, it is required by Docker.
## (change apt-get to yum if you use an RPM-based image)
##
- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'
##
## Run ssh-agent (inside the build environment)
##
- eval $(ssh-agent -s)
##
## Give the right permissions, otherwise ssh-add will refuse to add files
## Add the SSH key stored in SSH_PRIVATE_KEY file type CI/CD variable to the agent store
##
- chmod 400 "$SSH_PRIVATE_KEY"
- ssh-add "$SSH_PRIVATE_KEY"
##
## Create the SSH directory and give it the right permissions
##
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
##
## Optionally, if you will be using any Git commands, set the user name and
## and email.
##
# - git config --global user.email "user@example.com"
# - git config --global user.name "User name"
```
The [`before_script`](../yaml/index.md#before_script) can be set as a default
or per-job.
1. Make sure the private server's [SSH host keys are verified](#verifying-the-ssh-host-keys).
1. As a final step, add the _public_ key from the one you created in the first
step to the services that you want to have an access to from inside the build
environment. If you are accessing a private GitLab repository you must add
it as a [deploy key](../../user/project/deploy_keys/index.md).
That's it! You can now have access to private servers or repositories in your
build environment.
## SSH keys when using the Shell executor
If you are using the Shell executor and not Docker, it is easier to set up an
SSH key.
You can generate the SSH key from the machine that GitLab Runner is installed
on, and use that key for all projects that are run on this machine.
1. First, sign in to the server that runs your jobs.
1. Then, from the terminal, sign in as the `gitlab-runner` user:
```shell
sudo su - gitlab-runner
```
1. Generate the SSH key pair as described in the instructions to
[generate an SSH key](../../user/ssh.md#generate-an-ssh-key-pair).
**Do not** add a passphrase to the SSH key, or the `before_script` will
prompt for it.
1. As a final step, add the _public_ key from the one you created earlier to the
services that you want to have an access to from inside the build environment.
If you are accessing a private GitLab repository you must add it as a
[deploy key](../../user/project/deploy_keys/index.md).
After generating the key, try to sign in to the remote server to accept the
fingerprint:
```shell
ssh example.com
```
For accessing repositories on GitLab.com, you would use `git@gitlab.com`.
## Verifying the SSH host keys
It is a good practice to check the private server's own public key to make sure
you are not being targeted by a man-in-the-middle attack. If anything
suspicious happens, you notice it because the job fails (the SSH
connection fails when the public keys don't match).
To find out the host keys of your server, run the `ssh-keyscan` command from a
trusted network (ideally, from the private server itself):
```shell
## Use the domain name
ssh-keyscan example.com
## Or use an IP
ssh-keyscan 10.0.2.2
```
Create a new [file type CI/CD variable](../variables/index.md#use-file-type-cicd-variables)
with `SSH_KNOWN_HOSTS` as "Key", and as a "Value" add the output of `ssh-keyscan`.
Make sure the file ends with a newline. To add a newline, press <kbd>Enter</kbd> or <kbd>Return</kbd>
at the end of the last line of the SSH key before saving your changes.
If you must connect to multiple servers, all the server host keys
must be collected in the **Value** of the variable, one key per line.
NOTE:
By using a file type CI/CD variable instead of `ssh-keyscan` directly inside
`.gitlab-ci.yml`, it has the benefit that you don't have to change `.gitlab-ci.yml`
if the host domain name changes for some reason. Also, the values are predefined
by you, meaning that if the host keys suddenly change, the CI/CD job doesn't fail,
so there's something wrong with the server or the network.
Now that the `SSH_KNOWN_HOSTS` variable is created, in addition to the
[content of `.gitlab-ci.yml`](#ssh-keys-when-using-the-docker-executor)
above, you must add:
```yaml
before_script:
##
## Assuming you created the SSH_KNOWN_HOSTS file type CI/CD variable, uncomment the
## following two lines.
##
- cp "$SSH_KNOWN_HOSTS" ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
##
## Alternatively, use ssh-keyscan to scan the keys of your private server.
## Replace example.com with your private server's domain name. Repeat that
## command if you have more than one server to connect to. Include the -t
## flag to specify the key type.
##
# - ssh-keyscan -t rsa,ed25519 example.com >> ~/.ssh/known_hosts
# - chmod 644 ~/.ssh/known_hosts
##
## You can optionally disable host key checking. Be aware that by adding that
## you are susceptible to man-in-the-middle attacks.
## WARNING: Use this only with the Docker executor, if you use it with shell
## you will overwrite your user's SSH config.
##
# - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" >> ~/.ssh/config'
```
## Use SSH key without a file type CI/CD variable
If you do not want to use a file type CI/CD variable, the [example SSH Project](https://gitlab.com/gitlab-examples/ssh-private-key/)
shows an alternative method. This method uses a regular CI/CD variable instead of
the file type variable recommended above.
## Troubleshooting
### `Error loading key "/builds/path/SSH_PRIVATE_KEY": error in libcrypto` message
This message can be returned if there is a formatting error with the SSH key.
When saving the SSH key as a [file type CI/CD variable](../variables/index.md#use-file-type-cicd-variables),
the value must end with a newline (`LF` character). To add a newline, press <kbd>Enter</kbd> or <kbd>Return</kbd>
at the end of the `-----END OPENSSH PRIVATE KEY-----` line of the SSH key before saving
the variable.
<!-- This redirect file can be deleted after <2024-12-04>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

57
doc/ci/yaml/lint.md Normal file
View File

@ -0,0 +1,57 @@
---
stage: Verify
group: Pipeline Authoring
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
---
# Validate GitLab CI/CD configuration
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
Use the CI Lint tool to check the validity of GitLab CI/CD configuration.
You can validate the syntax from a `.gitlab-ci.yml` file or any other sample CI/CD configuration.
This tool checks for syntax and logic errors, and can simulate pipeline
creation to try to find more complicated configuration problems.
If you use the [pipeline editor](../pipeline_editor/index.md), it verifies configuration
syntax automatically.
If you use VS Code, you can validate your CI/CD configuration with the
[GitLab Workflow VS Code extension](../../editor_extensions/visual_studio_code/index.md).
## Check CI/CD syntax
The CI lint tool checks the syntax of GitLab CI/CD configuration, including
configuration added with the [`includes` keyword](index.md#include).
To check CI/CD configuration with the CI lint tool:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Build > Pipeline editor**.
1. Select the **Validate** tab.
1. Select **Lint CI/CD sample**.
1. Paste a copy of the CI/CD configuration you want to check into the text box.
1. Select **Validate**.
## Simulate a pipeline
You can simulate the creation of a GitLab CI/CD pipeline to find more complicated issues,
including problems with [`needs`](index.md#needs) and [`rules`](index.md#rules)
configuration. A simulation runs as a Git `push` event on the default branch.
Prerequisites:
- You must have [permissions](../../user/permissions.md#project-members-permissions)
to create pipelines on this branch to validate with a simulation.
To simulate a pipeline:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Build > Pipeline editor**.
1. Select the **Validate** tab.
1. Select **Lint CI/CD sample**.
1. Paste a copy of the CI/CD configuration you want to check into the text box.
1. Select **Simulate pipeline creation for the default branch**.
1. Select **Validate**.

View File

@ -198,6 +198,10 @@ As an alternative to using hosted runners, you can use your own runners for your
To use self-managed runners, install [GitLab Runner](https://docs.gitlab.com/runner/install/) on infrastructure that you own or manage.
#### OpenID Connect
You can use [GitLab as an OpenID Connect identity provider](../../integration/openid_connect_provider.md). If you use an IP allowlist to restrict access to your instance, you can [enable OpenID Connect requests](../../administration/dedicated/configure_instance.md#enable-openid-connect-for-your-ip-allowlist) while maintaining your IP restrictions.
#### Migration
To help you migrate your data to GitLab Dedicated, choose from the following options:

View File

@ -14,6 +14,5 @@ Use CI/CD to generate your application.
| [**Getting started**](../ci/index.md)<br>Overview of how CI/CD features fit together. | [**CI/CD YAML syntax reference**](../ci/yaml/index.md)<br>Pipeline configuration keywords, syntax, examples, inputs. | [**Runners**](https://docs.gitlab.com/runner/)<br>Installation, configuration, job execution. |
| [**Pipelines**](../ci/pipelines/index.md)<br>Configuration, automation, stages, schedules, efficiency. | [**Jobs**](../ci/jobs/index.md)<br>Configuration, rules, caching, artifacts, logs. | [**CI/CD components**](../ci/components/index.md)<br>Reusable, versioned CI/CD components for pipelines. |
| [**CI/CD variables**](../ci/variables/index.md)<br>Configuration, usage, security. | [**Pipeline security**](../ci/pipelines/pipeline_security.md)<br>Secrets management, job tokens, secure files, cloud security. | [**Debugging**](../ci/debugging.md)<br>Configuration validation, warnings, errors, troubleshooting. |
| [**Auto DevOps**](autodevops/index.md)<br>Automated DevOps, language detection, deployment, customization. | [**Testing**](../ci/testing/index.md)<br>Unit tests, integration tests, test reports, coverage, quality assurance. | [**SSH keys**](../ci/ssh_keys/index.md)<br>Authentication, secure access, deployment, remote execution, key management. |
| [**Mobile DevOps**](../ci/mobile_devops.md)<br>Mobile apps, Android, build automation, app distribution. | [**Google cloud integration**](../ci/gitlab_google_cloud_integration/index.md)<br>Cloud services, Kubernetes deployments. | [**External repository integrations**](../ci/ci_cd_for_external_repos/index.md)<br>GitHub, Bitbucket, external sources, mirroring, cross-platform. |
| [**Migrate to GitLab CI/CD**](../ci/migration/plan_a_migration.md)<br> Migrate from Jenkins, GitHub Actions, others. | | |
| [**Auto DevOps**](autodevops/index.md)<br>Automated DevOps, language detection, deployment, customization. | [**Testing**](../ci/testing/index.md)<br>Unit tests, integration tests, test reports, coverage, quality assurance. | [**Google cloud integration**](../ci/gitlab_google_cloud_integration/index.md)<br>Cloud services, Kubernetes deployments. |
| [**Migrate to GitLab CI/CD**](../ci/migration/plan_a_migration.md)<br> Migrate from Jenkins, GitHub Actions, others. | [**External repository integrations**](../ci/ci_cd_for_external_repos/index.md)<br>GitHub, Bitbucket, external sources, mirroring, cross-platform. | |

View File

@ -472,7 +472,7 @@ on the files make them readable to you but not accessible to others.
## Configure two-factor authentication (2FA)
You can set up two-factor authentication (2FA) for
[Git over SSH](../security/two_factor_authentication.md#2fa-for-git-over-ssh-operations). We recommend using
[Git over SSH](../security/two_factor_authentication.md#2fa-for-git-over-ssh-operations). You should use
[ED25519_SK](#ed25519_sk-ssh-keys) or [ECDSA_SK](#ecdsa_sk-ssh-keys) SSH keys.
## Use EGit on Eclipse

View File

@ -47,6 +47,7 @@ default:
before_script:
- cd $[[inputs.gem_path_prefix]]$[[inputs.gem_name]]
- ruby -v # Print out ruby version for debugging
- gem update --system
- bundle_version=$(grep -A 1 "BUNDLED WITH" Gemfile.lock | tail -n 1 | sed -e 's/[[:space:]]//')
- gem install bundler --version "$bundle_version" --no-document # Bundler is not installed with the image
- bundle config # Show bundler configuration

View File

@ -186,4 +186,4 @@ DEPENDENCIES
rubocop-rails (<= 2.20)
BUNDLED WITH
2.5.7
2.5.18

View File

@ -3,10 +3,13 @@
module API
class Notes < ::API::Base
include PaginationParams
include APIGuard
helpers ::API::Helpers::NotesHelpers
before { authenticate! }
allow_access_with_scope :ai_workflows
urgency :low, [
'/projects/:id/merge_requests/:noteable_id/notes',
'/projects/:id/merge_requests/:noteable_id/notes/:note_id'

View File

@ -22255,6 +22255,9 @@ msgstr ""
msgid "Excluding merge commits. Limited to 6,000 commits."
msgstr ""
msgid "Exclusions"
msgstr ""
msgid "Execution time"
msgstr ""
@ -48267,9 +48270,6 @@ msgstr ""
msgid "Secret Detection"
msgstr ""
msgid "Secret Detection Configuration"
msgstr ""
msgid "Secret push protection"
msgstr ""
@ -48282,15 +48282,24 @@ msgstr ""
msgid "Secret token."
msgstr ""
msgid "SecretDetection|Add exclusion"
msgstr ""
msgid "SecretDetection|Configure Secret Detection"
msgstr ""
msgid "SecretDetection|Feature not available"
msgstr ""
msgid "SecretDetection|No exclusions yet"
msgstr ""
msgid "SecretDetection|Only a project maintainer or owner can toggle this feature."
msgstr ""
msgid "SecretDetection|Secret detection configuration"
msgstr ""
msgid "SecretDetection|Secret push protection is disabled"
msgstr ""
@ -48305,6 +48314,9 @@ msgstr[1] ""
msgid "SecretDetection|This feature has been disabled at the instance level. Please reach out to your instance administrator to request activation."
msgstr ""
msgid "SecretDetection|Use secret detection exclusions to specify file paths, raw values, and regex that should be excluded by secret detection in this project. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
msgid "SecretDetection|Warning: Potential secret detected"
msgid_plural "SecretDetection|Warning: Potential secrets detected"
msgstr[0] ""

View File

@ -202,7 +202,7 @@ function run_locally_or_in_container() {
local cmd=$1
local args=$2
local files=$3
local registry_url="registry.gitlab.com/gitlab-org/gitlab-docs/lint-markdown:alpine-3.20-vale-3.6.1-markdownlint2-0.13.0-lychee-0.15.1"
local registry_url="registry.gitlab.com/gitlab-org/gitlab-docs/lint-markdown:alpine-3.20-vale-3.7.1-markdownlint2-0.13.0-lychee-0.15.1"
if hash "${cmd}" 2>/dev/null
then

View File

@ -49,25 +49,6 @@ RSpec.describe 'Projects > Settings > Repository > Branch rules settings', featu
end
end
context 'Branch rule details for a predefined rule', :js do
let(:role) { :maintainer }
before do
visit_branch_rules_settings
wait_for_requests
click_button 'Add branch rule'
click_button 'All branches'
wait_for_requests
end
it 'does not create a rule if a user leaves it empty' do
visit_branch_rules_settings
expect(page).not_to have_css '[data-testid="branch-content"]', text: 'All branches'
end
end
context 'Branch rule details for custom rule', :js do
let(:role) { :maintainer }

View File

@ -26,7 +26,7 @@ describe('Merge requests app component', () => {
data: {
currentUser: {
id: 1,
assignedMergeRequests: {
mergeRequests: {
count: 1,
pageInfo: {
hasNextPage: true,

View File

@ -22,7 +22,7 @@ describe('Merge requests query component', () => {
data: {
currentUser: {
id: 1,
reviewRequestedMergeRequests: {
mergeRequests: {
count: 0,
pageInfo: {
__typename: 'PageInfo',
@ -40,7 +40,7 @@ describe('Merge requests query component', () => {
data: {
currentUser: {
id: 1,
assignedMergeRequests: {
mergeRequests: {
count: 0,
pageInfo: {
hasNextPage: false,

View File

@ -835,19 +835,10 @@ describe('Settings Panel', () => {
});
});
describe('Duo', () => {
describe('when the FF is on', () => {
it('shows duo toggle', () => {
wrapper = mountComponent({ glFeatures: { aiSettingsVueProject: true } });
it('shows duo toggle', () => {
wrapper = mountComponent();
expect(findDuoSettings().exists()).toBe(true);
});
});
describe('when the FF is off', () => {
it('does not show duo toggle', () => {
wrapper = mountComponent();
expect(findDuoSettings().exists()).toBe(false);
});
expect(findDuoSettings().exists()).toBe(true);
});
});
});

View File

@ -53,6 +53,7 @@ describe('Branch rules app', () => {
glFeatures = { editBranchRules: true },
queryHandler = branchRulesQuerySuccessHandler,
mutationHandler = addRuleMutationSuccessHandler,
provided = appProvideMock,
} = {}) => {
fakeApollo = createMockApollo([
[branchRulesQuery, queryHandler],
@ -63,7 +64,7 @@ describe('Branch rules app', () => {
wrapper = mountExtended(BranchRules, {
apolloProvider: fakeApollo,
provide: {
...appProvideMock,
...provided,
glFeatures,
},
stubs: {
@ -120,28 +121,12 @@ describe('Branch rules app', () => {
expect(findAddBranchRuleDropdown().props('toggleText')).toBe('Add branch rule');
});
it('renders a dropdown containing predefined branch rules with actions', () => {
it('renders a dropdown containing custom rules with actions', () => {
expect(findAddBranchRuleDropdown().props('items')).toEqual([
{ action: expect.any(Function), text: 'Branch name or pattern' },
{ action: expect.any(Function), text: 'All branches' },
{ action: expect.any(Function), text: 'All protected branches' },
]);
});
it('does not render predefined branch rules when they are already set', async () => {
const { nodes } = predefinedBranchRulesMockResponse.data.project.branchRules;
await createComponent({
queryHandler: jest.fn().mockResolvedValue(predefinedBranchRulesMockResponse),
});
await findAddBranchRuleDropdown().vm.$emit('shown');
await nextTick();
expect(findAddBranchRuleDropdown().props('items').length).toEqual(
addBranchRulesItems.length - nodes.length,
);
});
it('renders a modal with correct props/attributes', () => {
expect(findModal().props()).toMatchObject({
title: I18N.createBranchRule,
@ -193,6 +178,48 @@ describe('Branch rules app', () => {
label: 'branch_rule_details',
});
});
describe('when status check and merge request approvals features are available', () => {
describe.each`
showStatusChecks | showApprovers
${true} | ${false}
${false} | ${true}
${true} | ${true}
`(
'when showStatusChecks is $showStatusChecks and showApprovers is $showApprovers',
({ showStatusChecks, showApprovers }) => {
it(`renders a dropdown containing predefined branch rules with actions`, () => {
createComponent({
provided: {
...appProvideMock,
showStatusChecks,
showApprovers,
},
});
expect(findAddBranchRuleDropdown().props('items')).toEqual([
{ action: expect.any(Function), text: 'Branch name or pattern' },
{ action: expect.any(Function), text: 'All branches' },
{ action: expect.any(Function), text: 'All protected branches' },
]);
});
},
);
it('does not render predefined branch rules when they are already set', async () => {
const { nodes } = predefinedBranchRulesMockResponse.data.project.branchRules;
await createComponent({
queryHandler: jest.fn().mockResolvedValue(predefinedBranchRulesMockResponse),
provided: { ...appProvideMock, showStatusChecks: true, showApprovers: true },
});
await findAddBranchRuleDropdown().vm.$emit('shown');
await nextTick();
expect(findAddBranchRuleDropdown().props('items').length).toEqual(
addBranchRulesItems.length - nodes.length,
);
});
});
});
describe('Add branch rule when editBranchRules FF disabled', () => {

View File

@ -7,7 +7,7 @@ export const MOCK_QUERY = {
confidential: null,
group_id: 1,
language: ['C', 'JavaScript'],
labels: ['60', '37'],
label_name: ['Aftersync', 'Brist'],
search: '*',
};

View File

@ -34,7 +34,7 @@ import {
TRACKING_ACTION_SHOW,
} from '~/search/sidebar/components/label_filter/tracking';
import { labelFilterData } from '~/search/sidebar/components/label_filter/data';
import { LABEL_FILTER_PARAM } from '~/search/sidebar/components/label_filter/data';
import {
RECEIVE_AGGREGATIONS_SUCCESS,
@ -62,6 +62,7 @@ describe('GlobalSearchSidebarLabelFilter', () => {
state = createState({
query: MOCK_QUERY,
aggregations: MOCK_LABEL_AGGREGATIONS,
navigation: {},
...initialState,
});
@ -343,21 +344,21 @@ describe('GlobalSearchSidebarLabelFilter', () => {
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
await findCheckboxGroup().vm.$emit('input', 6);
await findCheckboxGroup().vm.$emit('input', [6]);
await Vue.nextTick();
});
it('trigger event', () => {
expect(actionSpies.setQuery).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({ key: labelFilterData?.filterParam, value: 6 }),
expect.objectContaining({ key: LABEL_FILTER_PARAM, value: ['Cosche'] }),
);
});
it('sends tracking information when checkbox is selected', () => {
expect(trackingSpy).toHaveBeenCalledWith(TRACKING_ACTION_SELECT, TRACKING_LABEL_CHECKBOX, {
label: TRACKING_LABEL_FILTER,
property: 6,
property: [6],
});
});
});

View File

@ -207,7 +207,7 @@ describe('Global Search Store Actions', () => {
it('calls visitUrl and setParams with the state.query', async () => {
await testAction(actions.applyQuery, null, state, [], []);
expect(urlUtils.visitUrl).toHaveBeenCalledWith(
'https://test/?scope=issues&state=all&group_id=1&language%5B%5D=C&language%5B%5D=JavaScript&labels%5B%5D=60&labels%5B%5D=37&search=*',
'https://test/?scope=issues&state=all&group_id=1&language%5B%5D=C&language%5B%5D=JavaScript&label_name%5B%5D=Aftersync&label_name%5B%5D=Brist&search=*',
);
});
});
@ -408,8 +408,8 @@ describe('Global Search Store Actions', () => {
const expectedResult = [
{
payload: {
key: 'labels',
value: ['37'],
key: 'label_name',
value: ['Aftersync', 'Brist'],
},
type: 'SET_QUERY',
},

View File

@ -143,7 +143,7 @@ describe('Global Search Store Getters', () => {
describe('unselectedLabels', () => {
it('returns all labels that are not selected', () => {
state.query.labels = ['60'];
state.query.label_name = ['Brist'];
expect(getters.unselectedLabels(state)).toStrictEqual([MOCK_LABEL_SEARCH_RESULT]);
});
});
@ -151,9 +151,9 @@ describe('Global Search Store Getters', () => {
describe('unappliedNewLabels', () => {
it('returns all labels that are selected but not applied', () => {
// Applied labels
state.urlQuery.labels = ['37', '60'];
state.urlQuery.label_name = ['Aftersync', 'Brist'];
// Applied and selected labels
state.query.labels = ['37', '6', '73', '60'];
state.query.label_name = ['Aftersync', 'Cosche', 'Accent', 'Brist'];
// Selected but unapplied labels
// expect(getters.unappliedNewLabels(state)).toStrictEqual(MOCK_FILTERED_UNSELECTED_LABELS);
expect(getters.unappliedNewLabels(state).map(({ key }) => key)).toStrictEqual(['6', '73']);

View File

@ -418,5 +418,29 @@ RSpec.describe API::Notes, feature_category: :team_planning do
end
end
end
context 'when authenticated with a token that has the ai_workflows scope' do
let(:oauth_token) { create(:oauth_access_token, user: user, scopes: [:ai_workflows]) }
context 'a post request creates a merge request note' do
subject { post api(request_path, oauth_access_token: oauth_token), params: params }
it 'is successful' do
subject
expect(response).to have_gitlab_http_status(:created)
end
end
context 'a get request returns a list of merge request notes' do
subject { get api(request_path, oauth_access_token: oauth_token) }
it 'is successful' do
subject
expect(response).to have_gitlab_http_status(:ok)
end
end
end
end
end