Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-09-09 00:11:20 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-09-09 00:11:20 +0300
commit6a315e2d9c09da0efffdabed91ee872e867a981c (patch)
tree177a685b4ee5929e0e0b3b7b29430e893321c959
parent375b68fe813abba5e362aa6054eae7b325e92e03 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.checksum2
-rw-r--r--Gemfile.lock6
-rw-r--r--app/assets/javascripts/entrypoints/analytics.js1
-rw-r--r--app/assets/javascripts/vue_shared/components/confidentiality_badge.vue21
-rw-r--r--app/assets/javascripts/work_items/components/work_item_created_updated.vue1
-rw-r--r--app/assets/javascripts/work_items/components/work_item_state_badge.vue12
-rw-r--r--doc/ci/migration/examples/img/maven-freestyle-plugin.pngbin0 -> 47991 bytes
-rw-r--r--doc/ci/migration/examples/img/maven-freestyle-shell.pngbin0 -> 62207 bytes
-rw-r--r--doc/ci/migration/examples/jenkins-maven.md141
-rw-r--r--doc/development/activitypub/actor.md137
-rw-r--r--doc/development/activitypub/actors/group.md205
-rw-r--r--doc/development/activitypub/actors/index.md148
-rw-r--r--doc/development/activitypub/actors/project.md640
-rw-r--r--doc/development/activitypub/actors/releases.md85
-rw-r--r--doc/development/activitypub/actors/topic.md91
-rw-r--r--doc/development/activitypub/actors/user.md47
-rw-r--r--doc/development/activitypub/index.md11
-rw-r--r--doc/development/work_items.md167
-rw-r--r--spec/frontend/vue_shared/components/confidentiality_badge_spec.js42
-rw-r--r--spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js1
-rw-r--r--spec/frontend/work_items/components/work_item_state_badge_spec.js5
22 files changed, 1605 insertions, 160 deletions
diff --git a/Gemfile b/Gemfile
index d8f0e09913f..4ef5bb8b5e2 100644
--- a/Gemfile
+++ b/Gemfile
@@ -93,7 +93,7 @@ gem 'timfel-krb5-auth', '~> 0.8', group: :kerberos
# Spam and anti-bot protection
gem 'recaptcha', '~> 5.12', require: 'recaptcha/rails'
gem 'akismet', '~> 3.0'
-gem 'invisible_captcha', '~> 2.0.0'
+gem 'invisible_captcha', '~> 2.1.0'
# Two-factor authentication
gem 'devise-two-factor', '~> 4.0.2'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index af1f01be1a8..6a85dc3ce67 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -305,7 +305,7 @@
{"name":"ice_cube","version":"0.16.4","platform":"ruby","checksum":"da117e5de24bdc33931be629f9b55048641924442c7e9b72fedc05e5592531b7"},
{"name":"ice_nine","version":"0.11.2","platform":"ruby","checksum":"5d506a7d2723d5592dc121b9928e4931742730131f22a1a37649df1c1e2e63db"},
{"name":"imagen","version":"0.1.8","platform":"ruby","checksum":"fde7b727d4fe79c6bb5ac46c1f7184bf87a6d54df54d712ad2be039d2f93a162"},
-{"name":"invisible_captcha","version":"2.0.0","platform":"ruby","checksum":"a381edcb1d1b8744e9dc398ecad142c3e2ab077604645f85eeb02f9ea535c042"},
+{"name":"invisible_captcha","version":"2.1.0","platform":"ruby","checksum":"02b452f3eb1b691d155ba3e8e97e1be0e6b6be62e8bc94957234b9cde0852b1e"},
{"name":"ipaddr","version":"1.2.5","platform":"ruby","checksum":"4e679c71d6d8ed99f925487082f70f9a958de155591caa0e7f6cef9aa160f17a"},
{"name":"ipaddress","version":"0.8.3","platform":"ruby","checksum":"85640c4f9194c26937afc8c78e3074a8e7c97d5d1210358d1440f01034d006f5"},
{"name":"jaeger-client","version":"1.1.0","platform":"ruby","checksum":"cb5e9b9bbee6ee8d6a82d03d947a5b04543d8c0a949c22e484254f18d8a458a8"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 605ee7339e8..b85acd88706 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -882,8 +882,8 @@ GEM
ice_nine (0.11.2)
imagen (0.1.8)
parser (>= 2.5, != 2.5.1.1)
- invisible_captcha (2.0.0)
- rails (>= 5.0)
+ invisible_captcha (2.1.0)
+ rails (>= 5.2)
ipaddr (1.2.5)
ipaddress (0.8.3)
jaeger-client (1.1.0)
@@ -1873,7 +1873,7 @@ DEPENDENCIES
html2text
httparty (~> 0.21.0)
icalendar
- invisible_captcha (~> 2.0.0)
+ invisible_captcha (~> 2.1.0)
ipaddr (~> 1.2.5)
ipaddress (~> 0.8.3)
ipynbdiff!
diff --git a/app/assets/javascripts/entrypoints/analytics.js b/app/assets/javascripts/entrypoints/analytics.js
index 75f7c41a930..8eb265cb1e8 100644
--- a/app/assets/javascripts/entrypoints/analytics.js
+++ b/app/assets/javascripts/entrypoints/analytics.js
@@ -6,6 +6,7 @@ if (appId && host) {
window.glClient = glClientSDK({
appId,
host,
+ hasCookieConsent: true,
plugins: {
clientHints: false,
linkTracking: false,
diff --git a/app/assets/javascripts/vue_shared/components/confidentiality_badge.vue b/app/assets/javascripts/vue_shared/components/confidentiality_badge.vue
index 447820471b3..025e38a55ad 100644
--- a/app/assets/javascripts/vue_shared/components/confidentiality_badge.vue
+++ b/app/assets/javascripts/vue_shared/components/confidentiality_badge.vue
@@ -1,10 +1,11 @@
<script>
-import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
+import { GlBadge, GlTooltipDirective, GlIcon } from '@gitlab/ui';
import { confidentialityInfoText } from '../constants';
export default {
components: {
GlBadge,
+ GlIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -18,17 +19,31 @@ export default {
type: String,
required: true,
},
+ hideTextInSmallScreens: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
confidentialTooltip() {
return confidentialityInfoText(this.workspaceType, this.issuableType);
},
+ confidentialTextClass() {
+ return {
+ 'gl-display-none gl-sm-display-block': this.hideTextInSmallScreens,
+ 'gl-ml-2': true,
+ };
+ },
},
};
</script>
<template>
- <gl-badge v-gl-tooltip icon="eye-slash" :title="confidentialTooltip" variant="warning">
- {{ __('Confidential') }}
+ <gl-badge v-gl-tooltip :title="confidentialTooltip" variant="warning">
+ <gl-icon name="eye-slash" :size="16" />
+ <span data-testid="confidential-badge-text" :class="confidentialTextClass">{{
+ __('Confidential')
+ }}</span>
</gl-badge>
</template>
diff --git a/app/assets/javascripts/work_items/components/work_item_created_updated.vue b/app/assets/javascripts/work_items/components/work_item_created_updated.vue
index 7008955efac..60a74b9cdeb 100644
--- a/app/assets/javascripts/work_items/components/work_item_created_updated.vue
+++ b/app/assets/javascripts/work_items/components/work_item_created_updated.vue
@@ -87,6 +87,7 @@ export default {
class="gl-vertical-align-middle gl-display-inline-flex! gl-mr-2"
:issuable-type="workItemType"
:workspace-type="$options.WORKSPACE_PROJECT"
+ hide-text-in-small-screens
/>
<work-item-type-icon
class="gl-vertical-align-middle gl-mr-0!"
diff --git a/app/assets/javascripts/work_items/components/work_item_state_badge.vue b/app/assets/javascripts/work_items/components/work_item_state_badge.vue
index 1d1bc7352b1..5c5b41b38e6 100644
--- a/app/assets/javascripts/work_items/components/work_item_state_badge.vue
+++ b/app/assets/javascripts/work_items/components/work_item_state_badge.vue
@@ -1,11 +1,12 @@
<script>
-import { GlBadge } from '@gitlab/ui';
+import { GlBadge, GlIcon } from '@gitlab/ui';
import { __ } from '~/locale';
import { STATE_OPEN } from '../constants';
export default {
components: {
GlBadge,
+ GlIcon,
},
props: {
workItemState: {
@@ -31,11 +32,8 @@ export default {
</script>
<template>
- <gl-badge
- :icon="workItemStateIcon"
- :variant="workItemStateVariant"
- class="gl-mr-2 gl-vertical-align-middle"
- >
- {{ stateText }}
+ <gl-badge :variant="workItemStateVariant" class="gl-mr-2 gl-vertical-align-middle">
+ <gl-icon :name="workItemStateIcon" :size="16" />
+ <span class="gl-display-none gl-sm-display-block gl-ml-2">{{ stateText }}</span>
</gl-badge>
</template>
diff --git a/doc/ci/migration/examples/img/maven-freestyle-plugin.png b/doc/ci/migration/examples/img/maven-freestyle-plugin.png
new file mode 100644
index 00000000000..ab3ece9bf5f
--- /dev/null
+++ b/doc/ci/migration/examples/img/maven-freestyle-plugin.png
Binary files differ
diff --git a/doc/ci/migration/examples/img/maven-freestyle-shell.png b/doc/ci/migration/examples/img/maven-freestyle-shell.png
new file mode 100644
index 00000000000..f60d5320cb9
--- /dev/null
+++ b/doc/ci/migration/examples/img/maven-freestyle-shell.png
Binary files differ
diff --git a/doc/ci/migration/examples/jenkins-maven.md b/doc/ci/migration/examples/jenkins-maven.md
new file mode 100644
index 00000000000..a3590c41094
--- /dev/null
+++ b/doc/ci/migration/examples/jenkins-maven.md
@@ -0,0 +1,141 @@
+---
+stage: Verify
+group: Pipeline Authoring
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: index, howto
+---
+
+# Migrating a Maven build from Jenkins to GitLab CI
+
+If you have a Maven build in Jenkins, you can use a [Java Spring](https://gitlab.com/gitlab-org/project-templates/spring)
+project template to migrate to GitLab. The template uses Maven for its underlying dependency management.
+
+## Jenkins job options
+
+We are going to use three different options in Jenkins to test, build, and install the project:
+
+- Freestyle with shell execution
+- Freestyle with the Maven task plugin
+- A declarative pipeline using a Jenkinsfile
+
+All three of these options execute the same three commands:
+
+- `mvn test`: Run any tests found in the codebase
+- `mvn package -DskipTests`: Compile the code into an executable type defined in the POM and skip running any tests since we did that already
+- `mvn install -DskipTests`: Install the compiled executable into the local maven `.m2` repository and skip running any tests
+
+We are using a single, persistent Jenkins agent to handle running these three methods, which requires Maven to be pre-installed on the agent.
+This method of execution is similar to a GitLab Shell runner.
+
+### Freestyle with shell execution
+
+This example utilizes Jenkins' built-in shell execution option so we can directly call `mvn` commands from the shell on the agent.
+
+![freestyle shell](img/maven-freestyle-shell.png)
+
+### Freestyle with Maven task plugin
+
+This example utilizes the Maven plugin in Jenkins to declare and execute any specific goals in the [Maven build lifecycle](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html)
+
+NOTE:
+This plugin does not install Maven on the Jenkins agent, but rather provides a script wrapper around calling Maven commands. Maven must be installed before you use this method.
+
+![freestyle plugin](img/maven-freestyle-plugin.png)
+
+### Using a declarative pipeline
+
+Finally, we can run these Maven commands through a declarative pipeline stored either in the Jenkins pipeline configuration or directly in the Git repository. By default, this file is named `Jenksinfile`.
+
+This example uses shell execution commands instead of plugins.
+
+```groovy
+ pipeline {
+ agent any
+ stages {
+ stage('Test') {
+ steps {
+ sh "mvn test"
+ }
+ }
+ stage('Build') {
+ steps {
+ sh "mvn package -DskipTests"
+ }
+ }
+ stage('Install') {
+ steps {
+ sh "mvn install -DskipTests"
+ }
+ }
+ }
+ }
+```
+
+## Converting to GitLab CI
+
+Now that we have seen a few examples in Jenkins, let's refactor this process into GitLab CI.
+
+Prerequisites:
+
+- An available runner in GitLab
+- Docker installed on the runner
+- The runner is registered for this project and is using the Docker executor
+
+Instead of using a persistent machine for handling this build process, we are going to rely on an ephemeral Docker container to handle this execution.
+This removes the need for maintaining a virtual machine and the Maven version installed on the virtual machine and allows us some more flexiblity with expanding and extending the functionality of our CI pipeline.
+
+Here is the final `.gitlab-ci.yml` file:
+
+```yaml
+image: maven:3.6.3-openjdk-11
+
+stages:
+ - test
+ - build
+ - install
+
+default:
+ cache:
+ key: $CI_COMMIT_REF_SLUG
+ paths:
+ - .m2/
+
+variables:
+ MAVEN_OPTS: >-
+ -Dhttps.protocols=TLSv1.2
+ -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository
+ MAVEN_CLI_OPTS: >-
+ -DskipTests
+
+test code:
+ stage: test
+ script:
+ - mvn test
+
+build JAR:
+ stage: build
+ script:
+ - mvn $MAVEN_CLI_OPTS package
+
+install JAR:
+ stage: install
+ script:
+ - mvn $MAVEN_CLI_OPTS install
+```
+
+Let's break down the sections of this YAML file:
+
+- **Image**: The base Docker image where we execute all of our commands. In this case, it's an official Maven Docker image with everything we need already installed.
+- **Stages**: Similar to the Jenkinsfile, we are defining three stages that run in order. Test jobs execute first, followed by build jobs, and finally install jobs.
+- **Default**: Global default settings for the pipeline.
+ - **Cache**: Any data to be cached and reused between jobs.
+ - **Key**: The unique identifier for the specific cache archive. In this case, it's a shortened version of the Git commit ref.
+ - **Paths**: Any specific directories or files to include in the cache. In this case, we are caching the `.m2/` directory to avoid re-installing dependencies between jobs.
+- **Variables**: We are setting a few environment variables to be used by each job.
+ - `MAVEN_OPTS`: These are Maven environment variables referenced whenever Maven is executed.
+ - `-Dhttps.protocols=TLSv1.2` sets our TLS protocol to version 1.2 for any HTTP requests we may make in the pipeline.
+ - `-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository` sets the location of our local Maven repository to the GitLab project directory on the runner. This is to make sure we can access and modify the repository.
+ - `MAVEN_CLI_OPTS`: These are specific arguments to be injected into `mvn` commands.
+ - `-DskipTests` skips the 'test' stage in the Maven build lifecycle.
+
+- **Test code**, **Build JAR**, and **Install JAR**: These are the jobs to run in the pipeline. The top line of each section is a user-defined name for the job that shows up in the pipeline. The stage clause defines which stage the job runs in. (A pipeline contains one to many stages and a stage contains one to many jobs). The script section covers commands to run in that job. In this case, each job runs a single command.
diff --git a/doc/development/activitypub/actor.md b/doc/development/activitypub/actor.md
index 044dd730c2b..1d10e421df7 100644
--- a/doc/development/activitypub/actor.md
+++ b/doc/development/activitypub/actor.md
@@ -1,134 +1,11 @@
---
-stage: Create
-group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments"
+redirect_to: 'actors/index.md'
+remove_date: '2023-12-08'
---
-# Implement an ActivityPub actor **(EXPERIMENT)**
+This document was moved to [another location](actors/index.md).
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127023) in GitLab 16.5 [with two flags](../../administration/feature_flags.md) named `activity_pub` and `activity_pub_project`. Disabled by default. This feature is an [Experiment](../../policy/experiment-beta-support.md).
-
-FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available,
-an administrator can [enable the feature flags](../../administration/feature_flags.md)
-named `activity_pub` and `activity_pub_project`.
-On GitLab.com, this feature is not available.
-The feature is not ready for production use.
-
-ActivityPub is based on three standard documents:
-
-- [ActivityPub](https://www.w3.org/TR/activitypub/) defines the HTTP
- requests happening to implement federation.
-- [ActivityStreams](https://www.w3.org/TR/activitystreams-core/) defines the
- format of the JSON messages exchanged by the users of the protocol.
-- [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)
- defines the various messages recognized by default.
-
-The first one is typically handled by controllers, while the two others are
-related to what happen in serializers.
-
-To implement an ActivityPub actor, you must:
-
-- Implement the profile page of the resource.
-- Implement the outbox page.
-- Handle incoming requests on the inbox.
-
-All requests are made using
-`application/ld+json; profile="https://www.w3.org/ns/activitystreams"` as `Accept` HTTP header.
-
-## Profile page
-
-Querying the profile page is used to retrieve:
-
-- General information about it, like name and description.
-- URLs for the inbox and the outbox.
-
-To implement a profile page, create an ActivityStreams
-serializer in `app/serializers/activity_pub/`, making your serializer
-inherit from `ActivityStreamsSerializer`. See below in the serializers
-section about the mandatory fields.
-
-To call your serializer in your controller:
-
-```ruby
-opts = {
- inbox: nil,
- outbox: outbox_project_releases_url(project)
-}
-
-render json: ActivityPub::ReleasesActorSerializer.new.represent(project, opts)
-```
-
-- `outbox` is the endpoint where to find the activities feed for this
-actor.
-- `inbox` is where to POST to subscribe to the feed. Not yet implemented, so pass `nil`.
-
-## Outbox page
-
-The outbox is the list of activities for the resource. It's a feed for the
-resource, and it allows ActivityPub clients to show public activities for
-this actor without having yet subscribed to it.
-
-To implement an outbox page, create an ActivityStreams
-serializer in `app/serializers/activity_pub/`, making your serializer
-inherit from `ActivityStreamsSerializer`. See below in the serializers
-section about the mandatory fields.
-
-You call your serializer in your controller like this:
-
-```ruby
-serializer = ActivityPub::ReleasesOutboxSerializer.new.with_pagination(request, response)
-render json: serializer.represent(releases)
-```
-
-This converts the response to an `OrderedCollection`
-ActivityPub type, with all the correct fields.
-
-## Inbox
-
-Not yet implemented.
-
-The inbox is where the ActivityPub compatible third-parties makes their
-requests, to subscribe to the actor or send it messages.
-
-## ActivityStreams serializers
-
-The serializers implement half the core of ActivityPub support: they're all
-about [ActivityStreams](https://www.w3.org/TR/activitystreams-core/), the
-message format used by ActivityPub.
-
-To leverage the features doing most of the formatting for you, your
-serializer should inherit from `ActivityPub::ActivityStreamsSerializer`.
-
-To use it, call the `#represent` method. It requires you to provide
-`inbox` and `outbox` options (as mentioned above) if it
-is an actor profile page. You don't need those if your serializer
-represents an object that is just meant to be embedded as part of actors,
-like the object representing the contact information for a user.
-
-Each resource serialized (included other objects embedded in your
-actor) must provide an `id` and a `type` field.
-
-`id` is a URL. It's meant to be a unique identifier for the resource, and
-it must point to an existing page: ideally, an actor. Otherwise, you can
-just reference the closest actor and use an anchor, like this:
-
-```plaintext
-https://gitlab.com/user/project/-/releases#release-1
-```
-
-`type` should be taken from ActivityStreams core vocabulary:
-
-- [Activity types](https://www.w3.org/TR/activitystreams-vocabulary/#activity-types)
-- [Actor types](https://www.w3.org/TR/activitystreams-vocabulary/#actor-types)
-- [Object types](https://www.w3.org/TR/activitystreams-vocabulary/#object-types)
-
-The properties you can use are all documented in
-[the ActivityStreams vocabulary document](https://www.w3.org/TR/activitystreams-vocabulary).
-Given the type you have chosen for your resource, find the
-`properties` list, telling you all available properties, direct or
-inherited.
-
-It's worth noting that Mastodon adds one more property, `preferredName`.
-Mastodon expects it to be set on any actor, or that actor is not recognized by
-Mastodon.
+<!-- This redirect file can be deleted after <2023-12-08>. -->
+<!-- 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 -->
diff --git a/doc/development/activitypub/actors/group.md b/doc/development/activitypub/actors/group.md
new file mode 100644
index 00000000000..dad02298170
--- /dev/null
+++ b/doc/development/activitypub/actors/group.md
@@ -0,0 +1,205 @@
+---
+stage: Create
+group: Source Code
+info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments"
+---
+
+# Activities for group actor **(EXPERIMENT)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127023) in GitLab 16.5 [with two flags](../../../administration/feature_flags.md) named `activity_pub` and `activity_pub_project`. Disabled by default. This feature is an [Experiment](../../../policy/experiment-beta-support.md).
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+an administrator can [enable the feature flags](../../../administration/feature_flags.md)
+named `activity_pub` and `activity_pub_project`.
+On GitLab.com, this feature is not available.
+The feature is not ready for production use.
+
+This feature requires two feature flags:
+
+- `activity_pub`: Enables or disables all ActivityPub-related features.
+- `activity_pub_project`: Enables and disable ActivityPub features specific to
+ projects. Requires the `activity_pub` flag to also be enabled.
+
+## Profile
+
+```javascript
+{
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "id": GROUP_URL,
+ "type": "Group",
+ "name": GROUP_NAME,
+ "summary": GROUP_DESCRIPTION,
+ "url": GROUP_URL,
+ "outbox": GROUP_OUTBOX_URL,
+ "inbox": null,
+}
+```
+
+## Outbox
+
+The various activities for a group are:
+
+- [The group was created](#the-group-was-created).
+- All project activities for projects in that group, and its subgroups.
+- [A user joined the group](#a-user-joined-the-group).
+- [A user left the group](#a-user-left-the-group).
+- [The group was deleted](#the-group-was-deleted).
+- [A subgroup was created](#a-subgroup-was-created).
+- [A subgroup was deleted](#a-subgroup-was-deleted).
+
+### The group was created
+
+```javascript
+{
+ "id": GROUP_OUTBOX_URL#event_id,
+ "type": "Create",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": GROUP_URL,
+ "type": "Group",
+ "name": GROUP_NAME,
+ "url": GROUP_URL,
+ }
+}
+```
+
+### A user joined the group
+
+```javascript
+{
+ "id": GROUP_OUTBOX_URL#event_id,
+ "type": "Join",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": GROUP_URL,
+ "type": "Group",
+ "name": GROUP_NAME,
+ "url": GROUP_URL,
+ },
+}
+```
+
+### A user left the group
+
+```javascript
+{
+ "id": GROUP_OUTBOX_URL#event_id,
+ "type": "Leave",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": GROUP_URL,
+ "type": "Group",
+ "name": GROUP_NAME,
+ "url": GROUP_URL,
+ },
+}
+```
+
+### The group was deleted
+
+```javascript
+{
+ "id": GROUP_OUTBOX_URL#event_id,
+ "type": "Delete",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": GROUP_URL,
+ "type": "Group",
+ "name": GROUP_NAME,
+ "url": GROUP_URL,
+ }
+}
+```
+
+### A subgroup was created
+
+```javascript
+{
+ "id": GROUP_OUTBOX_URL#event_id,
+ "type": "Create",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": GROUP_URL,
+ "type": "Group",
+ "name": GROUP_NAME,
+ "url": GROUP_URL,
+ "context": {
+ "id": PARENT_GROUP_URL,
+ "type": "Group",
+ "name": PARENT_GROUP_NAME,
+ "url": PARENT_GROUP_URL,
+ }
+ }
+}
+```
+
+### A subgroup was deleted
+
+```javascript
+{
+ "id": GROUP_OUTBOX_URL#event_id,
+ "type": "Delete",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": GROUP_URL,
+ "type": "Group",
+ "name": GROUP_NAME,
+ "url": GROUP_URL,
+ "context": {
+ "id": PARENT_GROUP_URL,
+ "type": "Group",
+ "name": PARENT_GROUP_NAME,
+ "url": PARENT_GROUP_URL,
+ }
+ }
+}
+```
diff --git a/doc/development/activitypub/actors/index.md b/doc/development/activitypub/actors/index.md
new file mode 100644
index 00000000000..032cb26587a
--- /dev/null
+++ b/doc/development/activitypub/actors/index.md
@@ -0,0 +1,148 @@
+---
+stage: Create
+group: Source Code
+info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments"
+---
+
+# Implement an ActivityPub actor **(EXPERIMENT)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127023) in GitLab 16.5 [with two flags](../../../administration/feature_flags.md) named `activity_pub` and `activity_pub_project`. Disabled by default. This feature is an [Experiment](../../../policy/experiment-beta-support.md).
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+an administrator can [enable the feature flags](../../../administration/feature_flags.md)
+named `activity_pub` and `activity_pub_project`.
+On GitLab.com, this feature is not available.
+The feature is not ready for production use.
+
+This feature requires two feature flags:
+
+- `activity_pub`: Enables or disables all ActivityPub-related features.
+- `activity_pub_project`: Enables and disable ActivityPub features specific to
+ projects. Requires the `activity_pub` flag to also be enabled.
+
+ActivityPub is based on three standard documents:
+
+- [ActivityPub](https://www.w3.org/TR/activitypub/) defines the HTTP
+ requests happening to implement federation.
+- [ActivityStreams](https://www.w3.org/TR/activitystreams-core/) defines the
+ format of the JSON messages exchanged by the users of the protocol.
+- [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)
+ defines the various messages recognized by default.
+
+The first one is typically handled by controllers, while the two others are
+related to what happen in serializers.
+
+To implement an ActivityPub actor, you must:
+
+- Implement the profile page of the resource.
+- Implement the outbox page.
+- Handle incoming requests on the inbox.
+
+All requests are made using
+`application/ld+json; profile="https://www.w3.org/ns/activitystreams"` as `Accept` HTTP header.
+
+The actors we're implementing for the social features:
+
+- [Releases](releases.md)
+- [Topics](topic.md)
+- [Projects](project.md)
+- [Groups](group.md)
+- [Users](user.md)
+
+## Profile page
+
+Querying the profile page is used to retrieve:
+
+- General information about it, like name and description.
+- URLs for the inbox and the outbox.
+
+To implement a profile page, create an ActivityStreams
+serializer in `app/serializers/activity_pub/`, making your serializer
+inherit from `ActivityStreamsSerializer`. See below in the serializers
+section about the mandatory fields.
+
+To call your serializer in your controller:
+
+```ruby
+opts = {
+ inbox: nil,
+ outbox: outbox_project_releases_url(project)
+}
+
+render json: ActivityPub::ReleasesActorSerializer.new.represent(project, opts)
+```
+
+- `outbox` is the endpoint where to find the activities feed for this
+actor.
+- `inbox` is where to POST to subscribe to the feed. Not yet implemented, so pass `nil`.
+
+## Outbox page
+
+The outbox is the list of activities for the resource. It's a feed for the
+resource, and it allows ActivityPub clients to show public activities for
+this actor without having yet subscribed to it.
+
+To implement an outbox page, create an ActivityStreams
+serializer in `app/serializers/activity_pub/`, making your serializer
+inherit from `ActivityStreamsSerializer`. See below in the serializers
+section about the mandatory fields.
+
+You call your serializer in your controller like this:
+
+```ruby
+serializer = ActivityPub::ReleasesOutboxSerializer.new.with_pagination(request, response)
+render json: serializer.represent(releases)
+```
+
+This converts the response to an `OrderedCollection`
+ActivityPub type, with all the correct fields.
+
+## Inbox
+
+Not yet implemented.
+
+The inbox is where the ActivityPub compatible third-parties makes their
+requests, to subscribe to the actor or send it messages.
+
+## ActivityStreams serializers
+
+The serializers implement half the core of ActivityPub support: they're all
+about [ActivityStreams](https://www.w3.org/TR/activitystreams-core/), the
+message format used by ActivityPub.
+
+To leverage the features doing most of the formatting for you, your
+serializer should inherit from `ActivityPub::ActivityStreamsSerializer`.
+
+To use it, call the `#represent` method. It requires you to provide
+`inbox` and `outbox` options (as mentioned above) if it
+is an actor profile page. You don't need those if your serializer
+represents an object that is just meant to be embedded as part of actors,
+like the object representing the contact information for a user.
+
+Each resource serialized (included other objects embedded in your
+actor) must provide an `id` and a `type` field.
+
+`id` is a URL. It's meant to be a unique identifier for the resource, and
+it must point to an existing page: ideally, an actor. Otherwise, you can
+just reference the closest actor and use an anchor, like this:
+
+```plaintext
+https://gitlab.com/user/project/-/releases#release-1
+```
+
+`type` should be taken from ActivityStreams core vocabulary:
+
+- [Activity types](https://www.w3.org/TR/activitystreams-vocabulary/#activity-types)
+- [Actor types](https://www.w3.org/TR/activitystreams-vocabulary/#actor-types)
+- [Object types](https://www.w3.org/TR/activitystreams-vocabulary/#object-types)
+
+The properties you can use are all documented in
+[the ActivityStreams vocabulary document](https://www.w3.org/TR/activitystreams-vocabulary).
+Given the type you have chosen for your resource, find the
+`properties` list, telling you all available properties, direct or
+inherited.
+
+It's worth noting that Mastodon adds one more property, `preferredName`.
+Mastodon expects it to be set on any actor, or that actor is not recognized by
+Mastodon.
diff --git a/doc/development/activitypub/actors/project.md b/doc/development/activitypub/actors/project.md
new file mode 100644
index 00000000000..4f876b9e3fa
--- /dev/null
+++ b/doc/development/activitypub/actors/project.md
@@ -0,0 +1,640 @@
+---
+stage: Create
+group: Source Code
+info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments"
+---
+
+# Activities for project actor **(EXPERIMENT)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127023) in GitLab 16.5 [with two flags](../../../administration/feature_flags.md) named `activity_pub` and `activity_pub_project`. Disabled by default. This feature is an [Experiment](../../../policy/experiment-beta-support.md).
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+an administrator can [enable the feature flags](../../../administration/feature_flags.md)
+named `activity_pub` and `activity_pub_project`.
+On GitLab.com, this feature is not available.
+The feature is not ready for production use.
+
+This feature requires two feature flags:
+
+- `activity_pub`: Enables or disables all ActivityPub-related features.
+- `activity_pub_project`: Enables and disable ActivityPub features specific to
+ projects. Requires the `activity_pub` flag to also be enabled.
+
+## Profile
+
+```javascript
+{
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ "outbox": PROJECT_OUTBOX_URL,
+ "inbox": null,
+}
+```
+
+## Outbox
+
+For a project, we can map the events happening on the project activity
+timeline on GitLab, when a user:
+
+- [Creates the repository](#user-creates-the-repository).
+- [Pushes commits](#user-pushes-commits).
+- [Pushes a tag](#user-pushes-a-tag).
+- [Opens a merge request](#user-opens-a-merge-request).
+- [Accepts a merge request](#user-accepts-a-merge-request).
+- [Closes a merge request](#user-closes-a-merge-request).
+- [Opens an issue](#user-opens-an-issue).
+- [Closes an issue](#user-closes-an-issue).
+- [Reopens an issue](#user-reopens-an-issue).
+- [Comments on a merge request](#user-comments-on-a-merge-request).
+- [Comments on an issue](#user-comments-on-an-issue).
+- [Creates a wiki page](#user-creates-a-wiki-page).
+- [Updates a wiki page](#user-updates-a-wiki-page).
+- [Destroys a wiki page](#user-destroys-a-wiki-page).
+- [Joins the project](#user-joins-the-project).
+- [Leaves the project](#user-leaves-the-project).
+- [Deletes the repository](#user-deletes-the-repository).
+
+There's also a Design tab in the project activities, but it's just empty in
+all projects I follow and I don't see anything related to it in my projects
+sidebar. Maybe it's a premium feature? If so, it's of no concern to us for
+public following through ActivityPub.
+
+### User creates the repository
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Create",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ }
+}
+```
+
+### User pushes commits
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Update",
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+ "result": COMMITS_DIFF_URL,
+}
+```
+
+### User pushes a tag
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Update",
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+ "name": TAG_NAME,
+ "result": COMMIT_URL,
+}
+```
+
+### User opens a merge request
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Add",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": MERGE_REQUEST_URL,
+ "type": "Application",
+ "name": MERGE_REQUEST_TITLE,
+ "url": MERGE_REQUEST_URL,
+ "context": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+ },
+ "target": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+}
+```
+
+### User accepts a merge request
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Accept",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": MERGE_REQUEST_URL,
+ "type": "Application",
+ "name": MERGE_REQUEST_TITLE,
+ "url": MERGE_REQUEST_URL,
+ "context": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+ },
+ "target": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+}
+```
+
+### User closes a merge request
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Remove",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": MERGE_REQUEST_URL,
+ "type": "Application",
+ "name": MERGE_REQUEST_TITLE,
+ "url": MERGE_REQUEST_URL,
+ "context": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+ },
+ "origin": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+}
+```
+
+### User opens an issue
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Add",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": ISSUE_URL,
+ "type": "Page",
+ "name": ISSUE_TITLE,
+ "url": ISSUE_URL,
+ "context": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ }
+ },
+ "target": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ }
+}
+```
+
+Why to add the project both as `object.context` and `target`? For multiple
+consistency reasons:
+
+- The **Add** activity is more commonly used with a `target`.
+- The **Remove** activity used to close the issue is more
+ commonly used with an `origin`.
+- The **Update** activity used to reopen an issue specifies that
+ `target` and `origin` have no specific meaning, making `context` better
+ suited for that.
+- We could use `context` only with **Update**, but merge requests
+ must be taken into consideration.
+
+Merge requests are very similar to issues, so we want their activities to
+be similar. While the best type for issues is `page`, the type chosen for
+merge request is `application`, both to distinguish it from issues and because
+they contain code.
+
+To distinguish merge requests from projects (which are also `application`),
+merge requests are an `application` with another `application` (the project)
+as context. Given the merge request will have a `context` even with the **Add**
+and **Remove** activities, the same is done with issues for consistency.
+
+An alternative that was considered, but dismissed: instead of **Add** for issues,
+use **Create**. That would have allowed us to always use `context`, but
+it creates more problems that it solves. **Accept** and **Reject** could work quite
+well for closing merge requests, but what would we use to close issues?
+**Delete** is incorrect, as the issue is not deleted, just closed.
+Reopening the issue later would require an **Update** after a
+**Delete**.
+
+Using **Create** for opening issues and **Remove** for closing
+issues would be asymmetrical:
+
+- **Create** is mirrored by **Delete**.
+- **Add** is mirrored by **Remove**.
+
+To minimize pain for those who will build on top of those resources, it's best
+to duplicate the project information as `context` and `target` / `origin`.
+
+### User closes an issue
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Remove",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": ISSUE_URL,
+ "type": "Page",
+ "name": ISSUE_TITLE,
+ "url": ISSUE_URL,
+ "context": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+ },
+ "origin": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+}
+```
+
+### User reopens an issue
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Update",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": ISSUE_URL,
+ "type": "Page",
+ "name": ISSUE_TITLE,
+ "url": ISSUE_URL,
+ "context": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+ },
+}
+```
+
+### User comments on a merge request
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Add",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": NOTE_URL,
+ "type": "Note",
+ "content": NOTE_NOTE,
+ },
+ "target": {
+ "id": MERGE_REQUEST_URL,
+ "type": "Application",
+ "name": MERGE_REQUEST_TITLE,
+ "url": MERGE_REQUEST_URL,
+ "context": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+ },
+}
+```
+
+### User comments on an issue
+
+```javascript
+{
+ "id": PROJECT_URL#event_id,
+ "type": "Add",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": NOTE_URL,
+ "type": "Note",
+ "content": NOTE_NOTE,
+ },
+ "target": {
+ "id": ISSUE_URL,
+ "type": "Page",
+ "name": ISSUE_TITLE,
+ "url": ISSUE_URL,
+ "context": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+ },
+}
+```
+
+### User creates a wiki page
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Create",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": WIKI_PAGE_URL,
+ "type": "Page",
+ "name": WIKI_PAGE_HUMAN_TITLE,
+ "url": WIKI_PAGE_URL,
+ }
+}
+```
+
+### User updates a wiki page
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Update",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": WIKI_PAGE_URL,
+ "type": "Page",
+ "name": WIKI_PAGE_HUMAN_TITLE,
+ "url": WIKI_PAGE_URL,
+ }
+}
+```
+
+### User destroys a wiki page
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Delete",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": WIKI_PAGE_URL,
+ "type": "Page",
+ "name": WIKI_PAGE_HUMAN_TITLE,
+ "url": WIKI_PAGE_URL,
+ }
+}
+```
+
+### User joins the project
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Add",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "target": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+}
+```
+
+The GitLab project timeline does not mention who added a member to the
+project, so this does the same. However, the **Add** activity requires an Actor.
+For that reason, we use the same person as actor and object.
+
+In the **Members** page of a project contains a `source` attribute.
+While there is sometimes mention of who added the user, this is used mainly
+to distinguish if the user is a member attached to the project directly, or
+through a group. It would not be a good "actor" (that would rather be an
+`origin` for the membership).
+
+### User leaves the project
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Remove",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "target": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+}
+```
+
+See [User joined the project](#user-joins-the-project).
+
+### User deletes the repository
+
+```javascript
+{
+ "id": PROJECT_OUTBOX_URL#event_id,
+ "type": "Delete",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ }
+}
+```
diff --git a/doc/development/activitypub/actors/releases.md b/doc/development/activitypub/actors/releases.md
new file mode 100644
index 00000000000..009b98b6adf
--- /dev/null
+++ b/doc/development/activitypub/actors/releases.md
@@ -0,0 +1,85 @@
+---
+stage: Create
+group: Source Code
+info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments"
+---
+
+# Activities for following releases actor **(EXPERIMENT)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127023) in GitLab 16.5 [with two flags](../../../administration/feature_flags.md) named `activity_pub` and `activity_pub_project`. Disabled by default. This feature is an [Experiment](../../../policy/experiment-beta-support.md).
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+an administrator can [enable the feature flags](../../../administration/feature_flags.md)
+named `activity_pub` and `activity_pub_project`.
+On GitLab.com, this feature is not available.
+The feature is not ready for production use.
+
+This feature requires two feature flags:
+
+- `activity_pub`: Enables or disables all ActivityPub-related features.
+- `activity_pub_project`: Enables and disable ActivityPub features specific to
+ projects. Requires the `activity_pub` flag to also be enabled.
+
+## Profile
+
+The profile is this actor is a bit different from other actors. We don't want to
+show activities for a given release, but instead the releases for a given project.
+
+The profile endpoint is handled by `Projects::ReleasesController#index`
+on the list of releases, and should reply with something like this:
+
+```javascript
+{
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "id": PROJECT_RELEASES_URL,
+ "type": "Application",
+ "name": PROJECT_NAME + " releases",
+ "url": PROJECT_RELEASES_URL,
+ "content": PROJECT_DESCRIPTION,
+ "context": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+ "outbox": PROJECT_RELEASES_OUTBOX_URL,
+ "inbox": null,
+}
+```
+
+## Outbox
+
+The release actor is relatively simple: the only activity happening is the
+**Create release** event.
+
+```javascript
+{
+ "id": PROJECT_RELEASES_OUTBOX_URL#release_id,
+ "type": "Create",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ },
+ "object": {
+ "id": RELEASE_URL,
+ "type": "Application",
+ "name": RELEASE_TITLE,
+ "url": RELEASE_URL,
+ "content": RELEASE_DESCRIPTION,
+ "context": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "summary": PROJECT_DESCRIPTION,
+ "url": PROJECT_URL,
+ },
+ },
+}
+```
diff --git a/doc/development/activitypub/actors/topic.md b/doc/development/activitypub/actors/topic.md
new file mode 100644
index 00000000000..f99a6e0569a
--- /dev/null
+++ b/doc/development/activitypub/actors/topic.md
@@ -0,0 +1,91 @@
+---
+stage: Create
+group: Source Code
+info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments"
+---
+
+# Activities for topic actor **(EXPERIMENT)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127023) in GitLab 16.5 [with two flags](../../../administration/feature_flags.md) named `activity_pub` and `activity_pub_project`. Disabled by default. This feature is an [Experiment](../../../policy/experiment-beta-support.md).
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+an administrator can [enable the feature flags](../../../administration/feature_flags.md)
+named `activity_pub` and `activity_pub_project`.
+On GitLab.com, this feature is not available.
+The feature is not ready for production use.
+
+This feature requires two feature flags:
+
+- `activity_pub`: Enables or disables all ActivityPub-related features.
+- `activity_pub_project`: Enables and disable ActivityPub features specific to
+ projects. Requires the `activity_pub` flag to also be enabled.
+
+## Profile
+
+```javascript
+{
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "id": TOPIC_URL,
+ "type": "Group",
+ "name": TOPIC_NAME,
+ "url": TOPIC_URL,
+ "summary": TOPIC_DESCRIPTION,
+ "outbox": TOPIC_OUTBOX_URL,
+ "inbox": null,
+}
+```
+
+## Outbox
+
+Like the release actor, the topic specification is not complex. It generates an
+activity only when a new project has been added to the given topic.
+
+```javascript
+{
+ "id": TOPIC_OUTBOX_URL#event_id,
+ "type": "Add",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "actor": {
+ "id": PROJECT_URL,
+ "type": "Application",
+ "name": PROJECT_NAME,
+ "url": PROJECT_URL,
+ },
+ "object": {
+ "id": TOPIC_URL,
+ "type": "Group",
+ "name": TOPIC_NAME,
+ "url": TOPIC_URL,
+ },
+ },
+}
+```
+
+## Possible difficulties
+
+There is hidden complexity here.
+
+The simpler way to build this endpoint is to take the projects associated
+to a topic, and sort them by descending creation date. However,
+if we do that, discrepancies will occur when implementing the
+activity push part of the standard.
+
+Adding the project to a topic is not made at project creation time. It's
+made when a project's topics are _edited_. That action can happen a very long time
+after the project creation date. In that case, a push activity is
+created and sent to federated instances when adding the topic to the
+project. However, the list in the outbox endpoint that sorts projects by descending
+creation date doesn't show the project, because it was created long ago.
+
+No special logic happens when a topic is added to a project, except:
+
+- Cleaning up the topic list.
+- Creating the topic in database, if it doesn't exist yet.
+
+No event is generated. We should add such an event so the activity
+push create an event, ideally in `Projects::UpdateService`. Then, the outbox endpoint
+can list those events to be sure to match what was sent. When doing that, we should
+verify that it doesn't affect other pages or endpoints dealing with events.
diff --git a/doc/development/activitypub/actors/user.md b/doc/development/activitypub/actors/user.md
new file mode 100644
index 00000000000..9fe4f8ec88e
--- /dev/null
+++ b/doc/development/activitypub/actors/user.md
@@ -0,0 +1,47 @@
+---
+stage: Create
+group: Source Code
+info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments"
+---
+
+# Activities for following user actor **(EXPERIMENT)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127023) in GitLab 16.5 [with two flags](../../../administration/feature_flags.md) named `activity_pub` and `activity_pub_project`. Disabled by default. This feature is an [Experiment](../../../policy/experiment-beta-support.md).
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+an administrator can [enable the feature flags](../../../administration/feature_flags.md)
+named `activity_pub` and `activity_pub_project`.
+On GitLab.com, this feature is not available.
+The feature is not ready for production use.
+
+This feature requires two feature flags:
+
+- `activity_pub`: Enables or disables all ActivityPub-related features.
+- `activity_pub_project`: Enables and disable ActivityPub features specific to
+ projects. Requires the `activity_pub` flag to also be enabled.
+
+## Profile
+
+This activity is the first resource ActivityPub has in mind:
+
+```javascript
+{
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "id": USER_PROFILE_URL,
+ "type": "Person",
+ "name": USER_NAME,
+ "url": USER_PROFILE_URL,
+ "outbox": USER_OUTBOX_URL,
+ "inbox": null,
+}
+```
+
+## Outbox
+
+The user actor is special because it can be linked to all events happening on the platform.
+It's a join of events on other resources:
+
+- All release activities.
+- All project activities.
+- All group activities.
diff --git a/doc/development/activitypub/index.md b/doc/development/activitypub/index.md
index 82249ed3856..d89f18080f0 100644
--- a/doc/development/activitypub/index.md
+++ b/doc/development/activitypub/index.md
@@ -15,6 +15,9 @@ named `activity_pub` and `activity_pub_project`.
On GitLab.com, this feature is not available.
The feature is not ready for production use.
+Usage of ActivityPub in GitLab is governed by the
+[GitLab Testing Agreement](https://about.gitlab.com/handbook/legal/testing-agreement/).
+
The goal of those documents is to provide an implementation path for adding
Fediverse capabilities to GitLab.
@@ -22,6 +25,12 @@ This page describes the conceptual and high level point of view, while
sub-pages discuss implementation in more technical depth (as in, how to
implement this in the actual rails codebase of GitLab).
+This feature requires two feature flags:
+
+- `activity_pub`: Enables or disables all ActivityPub-related features.
+- `activity_pub_project`: Enables and disable ActivityPub features specific to
+ projects. Requires the `activity_pub` flag to also be enabled.
+
## What
Feel free to jump to [the Why section](#why) if you already know what
@@ -204,4 +213,4 @@ brings something immediately.
1. **Implement ActivityPub to submit cross-instance merge requests** to enable
submitting merge requests to other instances.
-For now, see [how to implement an ActivityPub actor](actor.md).
+For now, see [how to implement an ActivityPub actor](actors/index.md).
diff --git a/doc/development/work_items.md b/doc/development/work_items.md
index aee903c291d..2b28b2cd4f2 100644
--- a/doc/development/work_items.md
+++ b/doc/development/work_items.md
@@ -85,9 +85,10 @@ end
We already use the concept of WITs within `issues` table through `issue_type`
column. There are `issue`, `incident`, and `test_case` issue types. To extend this
-so that in future we can allow users to define custom WITs, we will move the
-`issue_type` to a separate table: `work_item_types`. The migration process of `issue_type`
-to `work_item_types` will involve creating the set of WITs for all root-level groups.
+so that in future we can allow users to define custom WITs, we will
+move the `issue_type` to a separate table: `work_item_types`. The migration process of `issue_type`
+to `work_item_types` will involve creating the set of WITs for all root-level groups as described in
+[this epic](https://gitlab.com/groups/gitlab-org/-/epics/6536).
NOTE:
At first, defining a WIT will only be possible at the root-level group, which would then be inherited by subgroups.
@@ -100,7 +101,7 @@ assume the following base types: `issue: 0`, `incident: 1`, `test_case: 2`.
The respective `work_item_types` records:
-| `group_id` | `base_type` | `title` |
+| `namespace_id` | `base_type` | `title` |
| -------------- | ----------- | --------- |
| 11 | 0 | Issue |
| 11 | 1 | Incident |
@@ -192,6 +193,164 @@ Until the architecture of WIT widgets is finalized, we are holding off on the cr
types. If a new work item type is absolutely necessary, please reach out to a
member of the [Project Management Engineering Team](https://gitlab.com/gitlab-org/gitlab/-/issues/370599).
+### Creating a new work item type in the database
+
+We have completed the removal of the `issue_type` column from the issues table, in favor of using the new
+`work_item_types` table as described in [this epic](https://gitlab.com/groups/gitlab-org/-/epics/6536)).
+
+After the introduction of the `work_item_types` table, we added more `work_item_types`, and we want to make it
+easier for other teams to do so. To introduce a new `work_item_type`, you must:
+
+1. Write a database migration to create a new record in the `work_item_types` table.
+1. Update `Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter`.
+
+The following MRs demonstrate how to introduce new `work_item_types`:
+
+- [MR example 1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127482)
+- [MR example 2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127917)
+
+#### Write a database migration
+
+First, write a database migration that creates the new record in the `work_item_types` table.
+
+Keep the following in mind when you write your migration:
+
+- **Important:** Exclude new type from existing APIs.
+ - We probably want to exclude newly created work items of this type from showing
+ up in existing features (like issue lists) until we fully release a feature. For this reason,
+ we have to add a new type to
+ [this exclude list](https://gitlab.com/gitlab-org/gitlab/-/blob/a0a52dd05b5d3c6ca820b672f9c0626840d2429b/app/models/work_items/type.rb#L84),
+ unless it is expected that users can create new issues and work items with the new type as soon as the migration
+ is executed.
+- Use a regular migration, not a post-deploy.
+ - We believe it would be beneficial to use
+ [regular migrations](migration_style_guide.md#choose-an-appropriate-migration-type)
+ to add new work item types instead of a
+ [post deploy migration](database/post_deployment_migrations.md).
+ This way, follow-up MRs that depend on the type being created can assume it exists right away,
+ instead of having to wait for the next release.
+- Migrations should avoid failures.
+ - We expect data related to `work_item_types` to be in a certain state when running the migration that will create a new
+ type. At the moment, we write migrations that check the data and don't fail in the event we find
+ it in an inconsistent state. There's a discussion about how much we can rely on the state of data based on seeds and
+ migrations in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/423483). We can only
+ have a successful pipeline if we write the migration so it doesn't fail if data exists in an inconsistent
+ state. We probably need to update some of the database jobs in order to change this.
+- Add widget definitions for the new type.
+ - The migration adds the new work item type as well as the widget definitions that are required for each work item.
+ The widgets you choose depend on the feature the new work item supports, but there are some that probably
+ all new work items need, like `Description`.
+- Optional. Create hierarchy restrictions.
+ - In one of the example MRs we also insert records in the `work_item_hierarchy_restrictions` table. This is only
+ necessary if the new work item type is going to use the `Hierarchy` widget. In this table, you must add what
+ work item type can have children and of what type. Also, you should specify the hierarchy depth for work items of the same
+ type.
+
+##### Example of adding a ticket work item
+
+The `Ticket` work item type already exists in the database, but we'll use it as an example migration.
+Note that for a new type you need to use a new name and ENUM value.
+
+```ruby
+class AddTicketWorkItemType < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ ISSUE_ENUM_VALUE = 0
+ # Enum value comes from the model where the enum is defined in
+ # https://gitlab.com/gitlab-org/gitlab/-/blob/1253f12abddb69cd1418c9e13e289d828b489f36/app/models/work_items/type.rb#L30.
+ # A new work item type should simply pick the next integer value.
+ TICKET_ENUM_VALUE = 8
+ TICKET_NAME = 'Ticket'
+ # Widget definitions also have an enum defined in
+ # https://gitlab.com/gitlab-org/gitlab/-/blob/1253f12abddb69cd1418c9e13e289d828b489f36/app/models/work_items/widget_definition.rb#L17.
+ # We need to provide both the enum and name as we plan to support custom widget names in the future.
+ TICKET_WIDGETS = {
+ 'Assignees' => 0,
+ 'Description' => 1,
+ 'Hierarchy' => 2,
+ 'Labels' => 3,
+ 'Milestone' => 4,
+ 'Notes' => 5,
+ 'Start and due date' => 6,
+ 'Health status' => 7,
+ 'Weight' => 8,
+ 'Iteration' => 9,
+ 'Notifications' => 14,
+ 'Current user todos' => 15,
+ 'Award emoji' => 16
+ }.freeze
+
+ class MigrationWorkItemType < MigrationRecord
+ self.table_name = 'work_item_types'
+ end
+
+ class MigrationWidgetDefinition < MigrationRecord
+ self.table_name = 'work_item_widget_definitions'
+ end
+
+ class MigrationHierarchyRestriction < MigrationRecord
+ self.table_name = 'work_item_hierarchy_restrictions'
+ end
+
+ def up
+ existing_ticket_work_item_type = MigrationWorkItemType.find_by(base_type: TICKET_ENUM_VALUE, namespace_id: nil)
+
+ return say('Ticket work item type record exists, skipping creation') if existing_ticket_work_item_type
+
+ new_ticket_work_item_type = MigrationWorkItemType.create(
+ name: TICKET_NAME,
+ namespace_id: nil,
+ base_type: TICKET_ENUM_VALUE,
+ icon_name: 'issue-type-issue'
+ )
+
+ return say('Ticket work item type create record failed, skipping creation') if new_ticket_work_item_type.new_record?
+
+ widgets = TICKET_WIDGETS.map do |widget_name, widget_enum_value|
+ {
+ work_item_type_id: new_ticket_work_item_type.id,
+ name: widget_name,
+ widget_type: widget_enum_value
+ }
+ end
+
+ MigrationWidgetDefinition.upsert_all(
+ widgets,
+ unique_by: :index_work_item_widget_definitions_on_default_witype_and_name
+ )
+
+ issue_type = MigrationWorkItemType.find_by(base_type: ISSUE_ENUM_VALUE, namespace_id: nil)
+ return say('Issue work item type not found, skipping hierarchy restrictions creation') unless issue_type
+
+ # This part of the migration is only necessary if the new type uses the `Hierarchy` widget.
+ restrictions = [
+ { parent_type_id: new_ticket_work_item_type.id, child_type_id: new_ticket_work_item_type.id, maximum_depth: 1 },
+ { parent_type_id: new_ticket_work_item_type.id, child_type_id: issue_type.id, maximum_depth: 1 }
+ ]
+
+ MigrationHierarchyRestriction.upsert_all(
+ restrictions,
+ unique_by: :index_work_item_hierarchy_restrictions_on_parent_and_child
+ )
+ end
+
+ def down
+ # There's the remote possibility that issues could already be
+ # using this issue type, with a tight foreign constraint.
+ # Therefore we will not attempt to remove any data.
+ end
+end
+```
+
+<!-- markdownlint-disable-next-line MD044 -->
+#### Update Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter
+
+The [BaseTypeImporter](https://gitlab.com/gitlab-org/gitlab/-/blob/f816a369d7d6bbd1d8d53d6c0bca4ca3389fdba7/lib/gitlab/database_importers/work_items/base_type_importer.rb)
+is where we can clearly visualize the structure of the types we have and what widgets are associated with each of them.
+`BaseTypeImporter` is the single source of truth for fresh GitLab installs and also our test suite. This should always
+reflect what we change with migrations.
+
### Custom work item types
With the WIT widget metadata and the workflow around mapping WIT to specific
diff --git a/spec/frontend/vue_shared/components/confidentiality_badge_spec.js b/spec/frontend/vue_shared/components/confidentiality_badge_spec.js
index 92cd7597637..7f6d97e8e68 100644
--- a/spec/frontend/vue_shared/components/confidentiality_badge_spec.js
+++ b/spec/frontend/vue_shared/components/confidentiality_badge_spec.js
@@ -1,15 +1,20 @@
-import { GlBadge } from '@gitlab/ui';
+import { GlBadge, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { TYPE_ISSUE, TYPE_EPIC, WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue';
-const createComponent = ({ workspaceType = WORKSPACE_PROJECT, issuableType = TYPE_ISSUE } = {}) =>
+const createComponent = ({
+ workspaceType = WORKSPACE_PROJECT,
+ issuableType = TYPE_ISSUE,
+ hideTextInSmallScreens = false,
+} = {}) =>
shallowMount(ConfidentialityBadge, {
propsData: {
workspaceType,
issuableType,
+ hideTextInSmallScreens,
},
});
@@ -20,6 +25,11 @@ describe('ConfidentialityBadge', () => {
wrapper = createComponent();
});
+ const findConfidentialityBadgeText = () =>
+ wrapper.find('[data-testid="confidential-badge-text"]');
+ const findBadge = () => wrapper.findComponent(GlBadge);
+ const findBadgeIcon = () => wrapper.findComponent(GlIcon);
+
it.each`
workspaceType | issuableType | expectedTooltip
${WORKSPACE_PROJECT} | ${TYPE_ISSUE} | ${'Only project members with at least the Reporter role, the author, and assignees can view or be notified about this issue.'}
@@ -32,14 +42,30 @@ describe('ConfidentialityBadge', () => {
issuableType,
});
- const badgeEl = wrapper.findComponent(GlBadge);
-
- expect(badgeEl.props()).toMatchObject({
- icon: 'eye-slash',
+ expect(findBadgeIcon().props('name')).toBe('eye-slash');
+ expect(findBadge().props()).toMatchObject({
variant: 'warning',
});
- expect(badgeEl.attributes('title')).toBe(expectedTooltip);
- expect(badgeEl.text()).toBe('Confidential');
+ expect(findBadge().attributes('title')).toBe(expectedTooltip);
+ expect(findBadge().text()).toBe('Confidential');
},
);
+
+ it('does not have `gl-sm-display-block` and `gl-display-none` when `hideTextInSmallScreens` is false', () => {
+ wrapper = createComponent({ hideTextInSmallScreens: false });
+
+ expect(findConfidentialityBadgeText().classes()).not.toContain(
+ 'gl-display-none',
+ 'gl-sm-display-block',
+ );
+ });
+
+ it('has `gl-sm-display-block` and `gl-display-none` when `hideTextInSmallScreens` is true', () => {
+ wrapper = createComponent({ hideTextInSmallScreens: true });
+
+ expect(findConfidentialityBadgeText().classes()).toContain(
+ 'gl-display-none',
+ 'gl-sm-display-block',
+ );
+ });
});
diff --git a/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js b/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js
index 4d08ad54e58..a3d7b244685 100644
--- a/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js
+++ b/spec/frontend/vue_shared/issuable/show/components/issuable_header_spec.js
@@ -107,6 +107,7 @@ describe('IssuableHeader component', () => {
expect(findConfidentialityBadge().props()).toEqual({
issuableType: 'issue',
workspaceType: 'project',
+ hideTextInSmallScreens: false,
});
});
diff --git a/spec/frontend/work_items/components/work_item_state_badge_spec.js b/spec/frontend/work_items/components/work_item_state_badge_spec.js
index 888d712cc5a..248f16a4081 100644
--- a/spec/frontend/work_items/components/work_item_state_badge_spec.js
+++ b/spec/frontend/work_items/components/work_item_state_badge_spec.js
@@ -1,4 +1,4 @@
-import { GlBadge } from '@gitlab/ui';
+import { GlBadge, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { STATE_OPEN, STATE_CLOSED } from '~/work_items/constants';
import WorkItemStateBadge from '~/work_items/components/work_item_state_badge.vue';
@@ -14,6 +14,7 @@ describe('WorkItemStateBadge', () => {
});
};
const findStatusBadge = () => wrapper.findComponent(GlBadge);
+ const findStatusBadgeIcon = () => wrapper.findComponent(GlIcon);
it.each`
state | icon | stateText | variant
@@ -24,7 +25,7 @@ describe('WorkItemStateBadge', () => {
({ state, icon, stateText, variant }) => {
createComponent({ workItemState: state });
- expect(findStatusBadge().props('icon')).toBe(icon);
+ expect(findStatusBadgeIcon().props('name')).toBe(icon);
expect(findStatusBadge().props('variant')).toBe(variant);
expect(findStatusBadge().text()).toBe(stateText);
},