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
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/alert_management/components/alert_management_table.vue7
-rw-r--r--app/assets/javascripts/editor/schema/ci.json506
-rw-r--r--app/assets/javascripts/integrations/edit/components/integration_form.vue32
-rw-r--r--app/assets/javascripts/notes/components/discussion_filter_note.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/constants.js2
-rw-r--r--app/assets/stylesheets/components/milestone_combobox.scss5
-rw-r--r--app/assets/stylesheets/components/release_block.scss3
-rw-r--r--app/assets/stylesheets/page_bundles/operations.scss (renamed from app/assets/stylesheets/components/dashboard_skeleton.scss)24
-rw-r--r--app/assets/stylesheets/page_bundles/releases.scss (renamed from app/assets/stylesheets/components/release_block_milestone_info.scss)6
-rw-r--r--app/assets/stylesheets/pages/notes.scss4
-rw-r--r--app/finders/labels_finder.rb2
-rw-r--r--app/graphql/resolvers/base_issues_resolver.rb1
-rw-r--r--app/graphql/resolvers/bulk_labels_resolver.rb27
-rw-r--r--app/graphql/resolvers/concerns/resolves_merge_requests.rb1
-rw-r--r--app/graphql/types/issue_type.rb6
-rw-r--r--app/graphql/types/merge_request_type.rb7
-rw-r--r--app/models/ci/secure_file.rb32
-rw-r--r--app/models/group_label.rb1
-rw-r--r--app/models/label.rb9
-rw-r--r--app/models/preloaders/labels_preloader.rb2
-rw-r--r--app/models/project_label.rb1
-rw-r--r--app/models/user.rb1
-rw-r--r--app/policies/group_label_policy.rb2
-rw-r--r--app/policies/project_label_policy.rb2
-rw-r--r--app/services/labels/promote_service.rb4
-rw-r--r--app/services/merge_requests/merge_service.rb24
-rw-r--r--app/services/pages_domains/create_acme_order_service.rb10
-rw-r--r--app/views/projects/releases/index.html.haml1
-rw-r--r--app/views/projects/releases/show.html.haml1
-rw-r--r--app/workers/all_queues.yml9
-rw-r--r--app/workers/ci/parse_secure_file_metadata_worker.rb15
31 files changed, 563 insertions, 186 deletions
diff --git a/app/assets/javascripts/alert_management/components/alert_management_table.vue b/app/assets/javascripts/alert_management/components/alert_management_table.vue
index 37a6ea16018..c0cac958a42 100644
--- a/app/assets/javascripts/alert_management/components/alert_management_table.vue
+++ b/app/assets/javascripts/alert_management/components/alert_management_table.vue
@@ -216,8 +216,11 @@ export default {
this.pagination = initialPaginationState;
this.sort = sortObjectToString({ sortBy, sortDesc });
},
+ showAlertLink({ iid }) {
+ return joinPaths(window.location.pathname, iid, 'details');
+ },
navigateToAlertDetails({ iid }, index, { metaKey }) {
- return visitUrl(joinPaths(window.location.pathname, iid, 'details'), metaKey);
+ return visitUrl(this.showAlertLink({ iid }), metaKey);
},
hasAssignees(assignees) {
return Boolean(assignees.nodes?.length);
@@ -357,7 +360,7 @@ export default {
:title="`${item.iid} - ${item.title}`"
data-testid="idField"
>
- #{{ item.iid }} {{ item.title }}
+ <gl-link :href="showAlertLink(item)"> #{{ item.iid }} {{ item.title }} </gl-link>
</div>
</template>
diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json
index afdf6b9eee5..52bf9d25e6b 100644
--- a/app/assets/javascripts/editor/schema/ci.json
+++ b/app/assets/javascripts/editor/schema/ci.json
@@ -8,34 +8,74 @@
"type": "string",
"format": "uri"
},
- "image": { "$ref": "#/definitions/image" },
- "services": { "$ref": "#/definitions/services" },
- "before_script": { "$ref": "#/definitions/before_script" },
- "after_script": { "$ref": "#/definitions/after_script" },
- "variables": { "$ref": "#/definitions/globalVariables" },
- "cache": { "$ref": "#/definitions/cache" },
- "!reference": {"$ref" : "#/definitions/!reference"},
+ "image": {
+ "$ref": "#/definitions/image"
+ },
+ "services": {
+ "$ref": "#/definitions/services"
+ },
+ "before_script": {
+ "$ref": "#/definitions/before_script"
+ },
+ "after_script": {
+ "$ref": "#/definitions/after_script"
+ },
+ "variables": {
+ "$ref": "#/definitions/globalVariables"
+ },
+ "cache": {
+ "$ref": "#/definitions/cache"
+ },
+ "!reference": {
+ "$ref": "#/definitions/!reference"
+ },
"default": {
"type": "object",
"properties": {
- "after_script": { "$ref": "#/definitions/after_script" },
- "artifacts": { "$ref": "#/definitions/artifacts" },
- "before_script": { "$ref": "#/definitions/before_script" },
- "cache": { "$ref": "#/definitions/cache" },
- "image": { "$ref": "#/definitions/image" },
- "interruptible": { "$ref": "#/definitions/interruptible" },
- "retry": { "$ref": "#/definitions/retry" },
- "services": { "$ref": "#/definitions/services" },
- "tags": { "$ref": "#/definitions/tags" },
- "timeout": { "$ref": "#/definitions/timeout" },
- "!reference": {"$ref" : "#/definitions/!reference"}
+ "after_script": {
+ "$ref": "#/definitions/after_script"
+ },
+ "artifacts": {
+ "$ref": "#/definitions/artifacts"
+ },
+ "before_script": {
+ "$ref": "#/definitions/before_script"
+ },
+ "cache": {
+ "$ref": "#/definitions/cache"
+ },
+ "image": {
+ "$ref": "#/definitions/image"
+ },
+ "interruptible": {
+ "$ref": "#/definitions/interruptible"
+ },
+ "retry": {
+ "$ref": "#/definitions/retry"
+ },
+ "services": {
+ "$ref": "#/definitions/services"
+ },
+ "tags": {
+ "$ref": "#/definitions/tags"
+ },
+ "timeout": {
+ "$ref": "#/definitions/timeout"
+ },
+ "!reference": {
+ "$ref": "#/definitions/!reference"
+ }
},
"additionalProperties": false
},
"stages": {
"type": "array",
"markdownDescription": "Groups jobs into stages. All jobs in one stage must complete before next stage is executed. Defaults to ['build', 'test', 'deploy']. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#stages).",
- "default": ["build", "test", "deploy"],
+ "default": [
+ "build",
+ "test",
+ "deploy"
+ ],
"items": {
"type": "string"
},
@@ -45,10 +85,14 @@
"include": {
"markdownDescription": "Can be `IncludeItem` or `IncludeItem[]`. Each `IncludeItem` will be a string, or an object with properties for the method if including external YAML file. The external content will be fetched, included and evaluated along the `.gitlab-ci.yml`. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#include).",
"oneOf": [
- { "$ref": "#/definitions/include_item" },
+ {
+ "$ref": "#/definitions/include_item"
+ },
{
"type": "array",
- "items": { "$ref": "#/definitions/include_item" }
+ "items": {
+ "$ref": "#/definitions/include_item"
+ }
}
]
},
@@ -63,17 +107,36 @@
"type": "array",
"items": {
"anyOf": [
- {"type": "object"},
- {"type": "array", "minLength": 1, "items": { "type": "string" }}
+ {
+ "type": "object"
+ },
+ {
+ "type": "array",
+ "minLength": 1,
+ "items": {
+ "type": "string"
+ }
+ }
],
"properties": {
- "if": { "$ref": "#/definitions/if" },
- "changes": { "$ref": "#/definitions/changes" },
- "exists": { "$ref": "#/definitions/exists" },
- "variables": { "$ref": "#/definitions/variables" },
+ "if": {
+ "$ref": "#/definitions/if"
+ },
+ "changes": {
+ "$ref": "#/definitions/changes"
+ },
+ "exists": {
+ "$ref": "#/definitions/exists"
+ },
+ "variables": {
+ "$ref": "#/definitions/variables"
+ },
"when": {
"type": "string",
- "enum": ["always", "never"]
+ "enum": [
+ "always",
+ "never"
+ ]
}
},
"additionalProperties": false
@@ -86,8 +149,12 @@
"^[.]": {
"description": "Hidden keys.",
"anyOf": [
- { "$ref": "#/definitions/job_template" },
- { "description": "Arbitrary YAML anchor." }
+ {
+ "$ref": "#/definitions/job_template"
+ },
+ {
+ "description": "Arbitrary YAML anchor."
+ }
]
}
},
@@ -134,15 +201,21 @@
"default": "on_success",
"oneOf": [
{
- "enum": ["on_success"],
+ "enum": [
+ "on_success"
+ ],
"description": "Upload artifacts only when the job succeeds (this is the default)."
},
{
- "enum": ["on_failure"],
+ "enum": [
+ "on_failure"
+ ],
"description": "Upload artifacts only when the job fails."
},
{
- "enum": ["always"],
+ "enum": [
+ "always"
+ ],
"description": "Upload artifacts regardless of job status."
}
]
@@ -180,7 +253,9 @@
"properties": {
"coverage_format": {
"description": "Code coverage format used by the test framework.",
- "enum": ["cobertura"]
+ "enum": [
+ "cobertura"
+ ]
},
"path": {
"description": "Path to the coverage report file that should be parsed.",
@@ -284,9 +359,13 @@
"format": "uri-reference",
"pattern": "\\.ya?ml$"
},
- "rules": { "$ref": "#/definitions/rules" }
+ "rules": {
+ "$ref": "#/definitions/rules"
+ }
},
- "required": ["local"]
+ "required": [
+ "local"
+ ]
},
{
"type": "object",
@@ -319,7 +398,10 @@
]
}
},
- "required": ["project", "file"]
+ "required": [
+ "project",
+ "file"
+ ]
},
{
"type": "object",
@@ -332,7 +414,9 @@
"pattern": "\\.ya?ml$"
}
},
- "required": ["template"]
+ "required": [
+ "template"
+ ]
},
{
"type": "object",
@@ -345,7 +429,9 @@
"pattern": "^https?://.+\\.ya?ml$"
}
},
- "required": ["remote"]
+ "required": [
+ "remote"
+ ]
}
]
},
@@ -406,7 +492,9 @@
]
}
},
- "required": ["name"]
+ "required": [
+ "name"
+ ]
},
{
"type": "array",
@@ -487,7 +575,9 @@
"minLength": 1
}
},
- "required": ["name"]
+ "required": [
+ "name"
+ ]
}
]
}
@@ -511,20 +601,37 @@
"engine": {
"type": "object",
"properties": {
- "name": { "type": "string" },
- "path": { "type": "string" }
+ "name": {
+ "type": "string"
+ },
+ "path": {
+ "type": "string"
+ }
},
- "required": ["name", "path"]
+ "required": [
+ "name",
+ "path"
+ ]
},
- "path": { "type": "string" },
- "field": { "type": "string" }
+ "path": {
+ "type": "string"
+ },
+ "field": {
+ "type": "string"
+ }
},
- "required": ["engine", "path", "field"]
+ "required": [
+ "engine",
+ "path",
+ "field"
+ ]
}
]
}
},
- "required": ["vault"]
+ "required": [
+ "vault"
+ ]
}
},
"before_script": {
@@ -570,17 +677,40 @@
"type": "object",
"additionalProperties": false,
"properties": {
- "if": { "$ref": "#/definitions/if" },
- "changes": { "$ref": "#/definitions/changes" },
- "exists": { "$ref": "#/definitions/exists" },
- "variables": { "$ref": "#/definitions/variables" },
- "when": { "$ref": "#/definitions/when" },
- "start_in": { "$ref": "#/definitions/start_in" },
- "allow_failure": { "$ref": "#/definitions/allow_failure" }
+ "if": {
+ "$ref": "#/definitions/if"
+ },
+ "changes": {
+ "$ref": "#/definitions/changes"
+ },
+ "exists": {
+ "$ref": "#/definitions/exists"
+ },
+ "variables": {
+ "$ref": "#/definitions/variables"
+ },
+ "when": {
+ "$ref": "#/definitions/when"
+ },
+ "start_in": {
+ "$ref": "#/definitions/start_in"
+ },
+ "allow_failure": {
+ "$ref": "#/definitions/allow_failure"
+ }
}
},
- {"type": "string", "minLength": 1},
- {"type": "array", "minLength": 1, "items": { "type": "string" }}
+ {
+ "type": "string",
+ "minLength": 1
+ },
+ {
+ "type": "array",
+ "minLength": 1,
+ "items": {
+ "type": "string"
+ }
+ }
]
}
},
@@ -591,7 +721,10 @@
".*": {
"oneOf": [
{
- "type": ["string", "number"]
+ "type": [
+ "string",
+ "number"
+ ]
},
{
"type": "object",
@@ -621,7 +754,9 @@
{
"type": "object",
"additionalProperties": false,
- "required": ["paths"],
+ "required": [
+ "paths"
+ ],
"properties": {
"paths": {
"type": "array",
@@ -656,7 +791,10 @@
"type": "object",
"patternProperties": {
".*": {
- "type": ["string", "number"]
+ "type": [
+ "string",
+ "number"
+ ]
},
"additionalProperties": false
}
@@ -683,7 +821,9 @@
"description": "Exit code that are not considered failure. The job fails for any other exit code.",
"type": "object",
"additionalProperties": false,
- "required": ["exit_codes"],
+ "required": [
+ "exit_codes"
+ ],
"properties": {
"exit_codes": {
"type": "integer"
@@ -694,7 +834,9 @@
"description": "You can list which exit codes are not considered failures. The job fails for any other exit code.",
"type": "object",
"additionalProperties": false,
- "required": ["exit_codes"],
+ "required": [
+ "exit_codes"
+ ],
"properties": {
"exit_codes": {
"type": "array",
@@ -713,27 +855,39 @@
"default": "on_success",
"oneOf": [
{
- "enum": ["on_success"],
+ "enum": [
+ "on_success"
+ ],
"description": "Execute job only when all jobs from prior stages succeed."
},
{
- "enum": ["on_failure"],
+ "enum": [
+ "on_failure"
+ ],
"description": "Execute job when at least one job from prior stages fails."
},
{
- "enum": ["always"],
+ "enum": [
+ "always"
+ ],
"description": "Execute job regardless of the status from prior stages."
},
{
- "enum": ["manual"],
+ "enum": [
+ "manual"
+ ],
"markdownDescription": "Execute the job manually from Gitlab UI or API. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#when)."
},
{
- "enum": ["delayed"],
+ "enum": [
+ "delayed"
+ ],
"markdownDescription": "Execute a job after the time limit in 'start_in' expires. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#when)."
},
{
- "enum": ["never"],
+ "enum": [
+ "never"
+ ],
"description": "Never execute the job."
}
]
@@ -745,15 +899,21 @@
"default": "on_success",
"oneOf": [
{
- "enum": ["on_success"],
+ "enum": [
+ "on_success"
+ ],
"description": "Save the cache only when the job succeeds."
},
{
- "enum": ["on_failure"],
+ "enum": [
+ "on_failure"
+ ],
"description": "Save the cache only when the job fails. "
},
{
- "enum": ["always"],
+ "enum": [
+ "always"
+ ],
"description": "Always save the cache. "
}
]
@@ -805,15 +965,21 @@
"default": "pull-push",
"oneOf": [
{
- "enum": ["pull"],
+ "enum": [
+ "pull"
+ ],
"description": "Pull will download cache but skip uploading after job completes."
},
{
- "enum": ["push"],
+ "enum": [
+ "push"
+ ],
"description": "Push will skip downloading cache and always recreate cache after job completes."
},
{
- "enum": ["pull-push"],
+ "enum": [
+ "pull-push"
+ ],
"description": "Pull-push will both download cache at job start and upload cache on job success."
}
]
@@ -828,39 +994,57 @@
{
"oneOf": [
{
- "enum": ["branches"],
+ "enum": [
+ "branches"
+ ],
"description": "When a branch is pushed."
},
{
- "enum": ["tags"],
+ "enum": [
+ "tags"
+ ],
"description": "When a tag is pushed."
},
{
- "enum": ["api"],
+ "enum": [
+ "api"
+ ],
"description": "When a pipeline has been triggered by a second pipelines API (not triggers API)."
},
{
- "enum": ["external"],
+ "enum": [
+ "external"
+ ],
"description": "When using CI services other than Gitlab"
},
{
- "enum": ["pipelines"],
+ "enum": [
+ "pipelines"
+ ],
"description": "For multi-project triggers, created using the API with 'CI_JOB_TOKEN'."
},
{
- "enum": ["pushes"],
+ "enum": [
+ "pushes"
+ ],
"description": "Pipeline is triggered by a `git push` by the user"
},
{
- "enum": ["schedules"],
+ "enum": [
+ "schedules"
+ ],
"description": "For scheduled pipelines."
},
{
- "enum": ["triggers"],
+ "enum": [
+ "triggers"
+ ],
"description": "For pipelines created using a trigger token."
},
{
- "enum": ["web"],
+ "enum": [
+ "web"
+ ],
"description": "For pipelines created using *Run pipeline* button in Gitlab UI (under your project's *Pipelines*)."
}
]
@@ -888,7 +1072,9 @@
"$ref": "#/definitions/filter_refs"
},
"kubernetes": {
- "enum": ["active"],
+ "enum": [
+ "active"
+ ],
"description": "Filter job based on if Kubernetes integration is active."
},
"variables": {
@@ -912,16 +1098,22 @@
"retry": {
"markdownDescription": "Retry a job if it fails. Can be a simple integer or object definition. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#retry).",
"oneOf": [
- { "$ref": "#/definitions/retry_max" },
+ {
+ "$ref": "#/definitions/retry_max"
+ },
{
"type": "object",
"additionalProperties": false,
"properties": {
- "max": { "$ref": "#/definitions/retry_max" },
+ "max": {
+ "$ref": "#/definitions/retry_max"
+ },
"when": {
"markdownDescription": "Either a single or array of error types to trigger job retry. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#retrywhen).",
"oneOf": [
- { "$ref": "#/definitions/retry_errors" },
+ {
+ "$ref": "#/definitions/retry_errors"
+ },
{
"type": "array",
"items": {
@@ -1004,21 +1196,39 @@
},
"job": {
"allOf": [
- { "$ref": "#/definitions/job_template" }
+ {
+ "$ref": "#/definitions/job_template"
+ }
]
},
"job_template": {
"type": "object",
"additionalProperties": false,
"properties": {
- "image": { "$ref": "#/definitions/image" },
- "services": { "$ref": "#/definitions/services" },
- "before_script": { "$ref": "#/definitions/before_script" },
- "after_script": { "$ref": "#/definitions/after_script" },
- "rules": { "$ref": "#/definitions/rules" },
- "variables": { "$ref": "#/definitions/variables" },
- "cache": { "$ref": "#/definitions/cache" },
- "secrets": { "$ref": "#/definitions/secrets" },
+ "image": {
+ "$ref": "#/definitions/image"
+ },
+ "services": {
+ "$ref": "#/definitions/services"
+ },
+ "before_script": {
+ "$ref": "#/definitions/before_script"
+ },
+ "after_script": {
+ "$ref": "#/definitions/after_script"
+ },
+ "rules": {
+ "$ref": "#/definitions/rules"
+ },
+ "variables": {
+ "$ref": "#/definitions/variables"
+ },
+ "cache": {
+ "$ref": "#/definitions/cache"
+ },
+ "secrets": {
+ "$ref": "#/definitions/secrets"
+ },
"script": {
"markdownDescription": "Shell scripts executed by the Runner. The only required property of jobs. Be careful with special characters (e.g. `:`, `{`, `}`, `&`) and use single or double quotes to avoid issues. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#script)",
"oneOf": [
@@ -1060,7 +1270,7 @@
}
}
]
- },
+ },
"only": {
"$ref": "#/definitions/filter",
"description": "Job will run *only* when these filtering options match."
@@ -1102,7 +1312,9 @@
"type": "boolean"
}
},
- "required": ["job"]
+ "required": [
+ "job"
+ ]
},
{
"type": "object",
@@ -1118,7 +1330,10 @@
"type": "boolean"
}
},
- "required": ["job", "pipeline"]
+ "required": [
+ "job",
+ "pipeline"
+ ]
},
{
"type": "object",
@@ -1137,7 +1352,11 @@
"type": "boolean"
}
},
- "required": ["job", "project", "ref"]
+ "required": [
+ "job",
+ "project",
+ "ref"
+ ]
}
]
}
@@ -1174,7 +1393,9 @@
"environment": {
"description": "Used to associate environment metadata with a deploy. Environment can have a name and URL attached to it, and will be displayed under /environments under the project.",
"oneOf": [
- { "type": "string" },
+ {
+ "type": "string"
+ },
{
"type": "object",
"additionalProperties": false,
@@ -1195,7 +1416,13 @@
"description": "The name of a job to execute when the environment is about to be stopped."
},
"action": {
- "enum": ["start", "prepare", "stop", "verify", "access"],
+ "enum": [
+ "start",
+ "prepare",
+ "stop",
+ "verify",
+ "access"
+ ],
"description": "Specifies what this job will do. 'start' (default) indicates the job will start the deployment. 'prepare'/'verify'/'access' indicates this will not affect the deployment. 'stop' indicates this will stop the deployment.",
"default": "start"
},
@@ -1226,7 +1453,9 @@
]
}
},
- "required": ["name"]
+ "required": [
+ "name"
+ ]
}
]
},
@@ -1306,15 +1535,23 @@
]
}
},
- "required": ["name", "url"]
+ "required": [
+ "name",
+ "url"
+ ]
},
"minItems": 1
}
},
- "required": ["links"]
+ "required": [
+ "links"
+ ]
}
},
- "required": ["tag_name", "description"]
+ "required": [
+ "tag_name",
+ "description"
+ ]
},
"coverage": {
"type": "string",
@@ -1345,14 +1582,20 @@
"type": "object",
"description": "Defines environment variables for specific job.",
"additionalProperties": {
- "type": ["string", "number", "array"]
+ "type": [
+ "string",
+ "number",
+ "array"
+ ]
}
},
"maxItems": 50
}
},
"additionalProperties": false,
- "required": ["matrix"]
+ "required": [
+ "matrix"
+ ]
}
]
},
@@ -1383,7 +1626,9 @@
"strategy": {
"description": "You can mirror the pipeline status from the triggered pipeline to the source bridge job by using strategy: depend",
"type": "string",
- "enum": ["depend"]
+ "enum": [
+ "depend"
+ ]
},
"forward": {
"description": "Specify what to forward to the downstream pipeline.",
@@ -1403,9 +1648,13 @@
}
}
},
- "required": ["project"],
+ "required": [
+ "project"
+ ],
"dependencies": {
- "branch": ["project"]
+ "branch": [
+ "project"
+ ]
}
},
{
@@ -1466,7 +1715,10 @@
"type": "string"
}
},
- "required": ["artifact", "job"]
+ "required": [
+ "artifact",
+ "job"
+ ]
},
{
"type": "object",
@@ -1489,7 +1741,10 @@
"pattern": "\\.ya?ml$"
}
},
- "required": ["project", "file"]
+ "required": [
+ "project",
+ "file"
+ ]
}
]
}
@@ -1499,7 +1754,9 @@
"strategy": {
"description": "You can mirror the pipeline status from the triggered pipeline to the source bridge job by using strategy: depend",
"type": "string",
- "enum": ["depend"]
+ "enum": [
+ "depend"
+ ]
},
"forward": {
"description": "Specify what to forward to the downstream pipeline.",
@@ -1560,7 +1817,9 @@
"variables": {
"markdownDescription": "Whether to inherit all globally-defined variables or not. Or subset of inherited variables. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#inheritvariables).",
"oneOf": [
- { "type": "boolean" },
+ {
+ "type": "boolean"
+ },
{
"type": "array",
"items": {
@@ -1576,15 +1835,24 @@
"oneOf": [
{
"properties": {
- "when": { "enum": ["delayed"] }
+ "when": {
+ "enum": [
+ "delayed"
+ ]
+ }
},
- "required": ["when", "start_in"]
+ "required": [
+ "when",
+ "start_in"
+ ]
},
{
"properties": {
"when": {
"not": {
- "enum": ["delayed"]
+ "enum": [
+ "delayed"
+ ]
}
}
}
@@ -1612,4 +1880,4 @@
}
}
}
-}
+} \ No newline at end of file
diff --git a/app/assets/javascripts/integrations/edit/components/integration_form.vue b/app/assets/javascripts/integrations/edit/components/integration_form.vue
index 3c07dee025f..4b7d606b52c 100644
--- a/app/assets/javascripts/integrations/edit/components/integration_form.vue
+++ b/app/assets/javascripts/integrations/edit/components/integration_form.vue
@@ -230,10 +230,8 @@ export default {
@change="setOverride"
/>
- <div v-if="!hasSections" class="row">
- <div class="col-lg-4"></div>
-
- <div class="col-lg-8">
+ <section v-if="!hasSections" class="gl-lg-display-flex gl-justify-content-end">
+ <div class="gl-flex-basis-two-thirds">
<!-- helpHtml is trusted input -->
<div v-if="helpHtml" v-safe-html:[$options.helpHtmlConfig]="helpHtml"></div>
@@ -249,7 +247,7 @@ export default {
:type="propsSource.type"
/>
</div>
- </div>
+ </section>
<template v-if="hasSections">
<div
@@ -258,8 +256,8 @@ export default {
:class="{ 'gl-border-b gl-pb-3 gl-mb-6': index !== customState.sections.length - 1 }"
data-testid="integration-section"
>
- <div class="row">
- <div class="col-lg-4">
+ <section class="gl-lg-display-flex">
+ <div class="gl-flex-basis-third">
<h4 class="gl-mt-0">
{{ section.title
}}<gl-badge
@@ -277,7 +275,7 @@ export default {
<p v-safe-html="section.description"></p>
</div>
- <div class="col-lg-8">
+ <div class="gl-flex-basis-two-thirds">
<component
:is="$options.integrationFormSectionComponents[section.type]"
:fields="fieldsForSection(section)"
@@ -286,14 +284,12 @@ export default {
@request-jira-issue-types="onRequestJiraIssueTypes"
/>
</div>
- </div>
+ </section>
</div>
</template>
- <div v-if="hasFieldsWithoutSection" class="row">
- <div class="col-lg-4"></div>
-
- <div class="col-lg-8">
+ <section v-if="hasFieldsWithoutSection" class="gl-lg-display-flex gl-justify-content-end">
+ <div class="gl-flex-basis-two-thirds">
<dynamic-field
v-for="field in fieldsWithoutSection"
:key="`${currentKey}-${field.name}`"
@@ -302,12 +298,12 @@ export default {
:data-qa-selector="`${field.name}_div`"
/>
</div>
- </div>
+ </section>
- <div v-if="isEditable" class="row">
- <div :class="hasSections ? 'col' : 'col-lg-8 offset-lg-4'">
+ <section v-if="isEditable" :class="!hasSections && 'gl-lg-display-flex gl-justify-content-end'">
+ <div :class="!hasSections && 'gl-flex-basis-two-thirds'">
<div
- class="footer-block row-content-block gl-display-flex gl-justify-content-space-between"
+ class="footer-block row-content-block gl-lg-display-flex gl-justify-content-space-between"
>
<div>
<template v-if="isInstanceOrGroupLevel">
@@ -369,6 +365,6 @@ export default {
</template>
</div>
</div>
- </div>
+ </section>
</gl-form>
</template>
diff --git a/app/assets/javascripts/notes/components/discussion_filter_note.vue b/app/assets/javascripts/notes/components/discussion_filter_note.vue
index 61af0b06535..39b3df899a5 100644
--- a/app/assets/javascripts/notes/components/discussion_filter_note.vue
+++ b/app/assets/javascripts/notes/components/discussion_filter_note.vue
@@ -31,7 +31,7 @@ export default {
<div class="timeline-icon d-none d-lg-flex">
<gl-icon name="comment" />
</div>
- <div class="timeline-content">
+ <div class="timeline-content gl-pl-8">
<div data-testid="discussion-filter-timeline-content">
<gl-sprintf :message="$options.i18n.information">
<template #bold="{ content }">
diff --git a/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/constants.js b/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/constants.js
index b7768cfa5b9..df1188d365b 100644
--- a/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/constants.js
+++ b/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/constants.js
@@ -4,7 +4,7 @@ export const tdClass =
'table-col gl-display-flex d-md-table-cell gl-align-items-center gl-white-space-nowrap';
export const thClass = 'gl-hover-bg-blue-50';
export const bodyTrClass =
- 'gl-border-1 gl-border-t-solid gl-border-gray-100 gl-hover-cursor-pointer gl-hover-bg-blue-50 gl-hover-border-b-solid gl-hover-border-blue-200';
+ 'gl-border-1 gl-border-t-solid gl-border-gray-100 gl-hover-cursor-pointer gl-hover-bg-gray-50 gl-hover-border-b-solid';
export const defaultPageSize = 20;
diff --git a/app/assets/stylesheets/components/milestone_combobox.scss b/app/assets/stylesheets/components/milestone_combobox.scss
deleted file mode 100644
index 94d295c324b..00000000000
--- a/app/assets/stylesheets/components/milestone_combobox.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-.milestone-combobox {
- .dropdown-menu.show {
- overflow: hidden;
- }
-}
diff --git a/app/assets/stylesheets/components/release_block.scss b/app/assets/stylesheets/components/release_block.scss
deleted file mode 100644
index 7e82d0960d7..00000000000
--- a/app/assets/stylesheets/components/release_block.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.release-block {
- transition: background-color 1s linear;
-}
diff --git a/app/assets/stylesheets/components/dashboard_skeleton.scss b/app/assets/stylesheets/page_bundles/operations.scss
index 1dcaa47470b..497cb63033c 100644
--- a/app/assets/stylesheets/components/dashboard_skeleton.scss
+++ b/app/assets/stylesheets/page_bundles/operations.scss
@@ -1,3 +1,5 @@
+@import 'mixins_and_variables_and_functions';
+
.dashboard-cards {
margin-right: -$gl-padding-8;
margin-left: -$gl-padding-8;
@@ -8,7 +10,7 @@
&-header {
&-warning {
- background-color: $orange-100;
+ background-color: var(--orange-100, $orange-100);
}
}
@@ -16,16 +18,16 @@
min-height: 120px;
&-warning {
- background-color: $orange-50;
+ background-color: var(--orange-50, $orange-50);
}
&-failed {
- background-color: $red-50;
+ background-color: var(--red-50, $red-50);
}
}
&-icon {
- color: $gray-300;
+ color: var(--gray-300, $gray-300);
}
&-footer {
@@ -33,7 +35,7 @@
height: $gl-padding-32;
&-arrow {
- color: $gray-200;
+ color: var(--gray-200, $gray-200);
}
&-downstream {
@@ -41,7 +43,7 @@
}
&-extra {
- background-color: $gray-200;
+ background-color: var(--gray-200, $gray-200);
font-size: 10px;
line-height: $gl-line-height;
width: $gl-padding;
@@ -50,7 +52,7 @@
&-header {
&-failed {
- background-color: $red-100;
+ background-color: var(--red-100, $red-100);
}
}
@@ -66,10 +68,10 @@
background-repeat: no-repeat;
background-size: cover;
background-image: linear-gradient(to right,
- $gray-50 0%,
- $gray-10 20%,
- $gray-50 40%,
- $gray-50 100%);
+ var(--gray-50, $gray-50) 0%,
+ var(--gray-10, $gray-10) 20%,
+ var(--gray-50, $gray-50) 40%,
+ var(--gray-50, $gray-50) 100%);
border-radius: $gl-padding;
height: $gl-padding;
margin-top: -$gl-padding-8;
diff --git a/app/assets/stylesheets/components/release_block_milestone_info.scss b/app/assets/stylesheets/page_bundles/releases.scss
index b6a85ae965a..24ffbf9b90c 100644
--- a/app/assets/stylesheets/components/release_block_milestone_info.scss
+++ b/app/assets/stylesheets/page_bundles/releases.scss
@@ -1,3 +1,9 @@
+@import 'mixins_and_variables_and_functions';
+
+.release-block {
+ transition: background-color 1s linear;
+}
+
.release-block-milestone-info {
.milestone-progress-bar-container {
width: 300px;
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index bd61e8df699..cfad34760ad 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -435,8 +435,8 @@ $system-note-svg-size: 1rem;
.discussion-filter-note {
.timeline-icon {
- width: $system-note-icon-size + 6;
- height: $system-note-icon-size + 6;
+ width: $system-note-icon-size;
+ height: $system-note-icon-size;
margin-top: -8px;
}
}
diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb
index ecd6270ed47..9f9d0da6efd 100644
--- a/app/finders/labels_finder.rb
+++ b/app/finders/labels_finder.rb
@@ -19,7 +19,7 @@ class LabelsFinder < UnionFinder
items = with_title(items)
items = by_subscription(items)
items = by_search(items)
- sort(items)
+ sort(items.with_preloaded_container)
end
private
diff --git a/app/graphql/resolvers/base_issues_resolver.rb b/app/graphql/resolvers/base_issues_resolver.rb
index 479b1df6876..6357132705e 100644
--- a/app/graphql/resolvers/base_issues_resolver.rb
+++ b/app/graphql/resolvers/base_issues_resolver.rb
@@ -47,7 +47,6 @@ module Resolvers
def preloads
{
alert_management_alert: [:alert_management_alert],
- labels: [:labels],
assignees: [:assignees],
participants: Issue.participant_includes,
timelogs: [:timelogs],
diff --git a/app/graphql/resolvers/bulk_labels_resolver.rb b/app/graphql/resolvers/bulk_labels_resolver.rb
new file mode 100644
index 00000000000..a758ef70f93
--- /dev/null
+++ b/app/graphql/resolvers/bulk_labels_resolver.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class BulkLabelsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type Types::LabelType.connection_type, null: true
+
+ def resolve
+ authorize!(object)
+
+ BatchLoader::GraphQL.for(object.id).batch(cache: false) do |ids, loader, args|
+ labels = Label.for_targets(ids, object.class.name).group_by(&:target_id)
+
+ ids.each do |id|
+ loader.call(id, labels[id] || [])
+ end
+ end
+ end
+
+ private
+
+ def authorized_resource?(object)
+ Ability.allowed?(current_user, :read_label, object.issuing_parent)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/concerns/resolves_merge_requests.rb b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
index 697cc6f5b03..b6510e7c5fa 100644
--- a/app/graphql/resolvers/concerns/resolves_merge_requests.rb
+++ b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
@@ -42,7 +42,6 @@ module ResolvesMergeRequests
assignees: [:assignees],
reviewers: [:reviewers],
participants: MergeRequest.participant_includes,
- labels: [:labels],
author: [:author],
merged_at: [:metrics],
commit_count: [:metrics],
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index ca4eb044bab..76fac831199 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -43,8 +43,10 @@ module Types
field :updated_by, Types::UserType, null: true,
description: 'User that last updated the issue.'
- field :labels, Types::LabelType.connection_type, null: true,
- description: 'Labels of the issue.'
+ field :labels, Types::LabelType.connection_type,
+ null: true,
+ description: 'Labels of the issue.',
+ resolver: Resolvers::BulkLabelsResolver
field :milestone, Types::MilestoneType, null: true,
description: 'Milestone of the issue.'
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 3a39236bafa..8cc600fc68e 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -158,8 +158,11 @@ module Types
description: 'Human-readable time estimate of the merge request.'
field :human_total_time_spent, GraphQL::Types::String, null: true,
description: 'Human-readable total time reported as spent on the merge request.'
- field :labels, Types::LabelType.connection_type, null: true, complexity: 5,
- description: 'Labels of the merge request.'
+ field :labels, Types::LabelType.connection_type,
+ null: true, complexity: 5,
+ description: 'Labels of the merge request.',
+ resolver: Resolvers::BulkLabelsResolver
+
field :milestone, Types::MilestoneType, null: true,
description: 'Milestone of the merge request.'
field :participants, Types::MergeRequests::ParticipantType.connection_type, null: true, complexity: 15,
diff --git a/app/models/ci/secure_file.rb b/app/models/ci/secure_file.rb
index 0644c9f3235..3cc57e8f907 100644
--- a/app/models/ci/secure_file.rb
+++ b/app/models/ci/secure_file.rb
@@ -7,6 +7,7 @@ module Ci
FILE_SIZE_LIMIT = 5.megabytes.freeze
CHECKSUM_ALGORITHM = 'sha256'
+ PARSABLE_EXTENSIONS = ['cer'].freeze
self.limit_scope = :project
self.limit_name = 'project_ci_secure_files'
@@ -34,6 +35,37 @@ module Ci
CHECKSUM_ALGORITHM
end
+ def file_extension
+ File.extname(name).delete_prefix('.')
+ end
+
+ def metadata_parsable?
+ PARSABLE_EXTENSIONS.include?(file_extension)
+ end
+
+ def metadata_parser
+ return unless metadata_parsable?
+
+ case file_extension
+ when 'cer'
+ Gitlab::Ci::SecureFiles::Cer.new(file.read)
+ end
+ end
+
+ def update_metadata!
+ return unless metadata_parser
+
+ begin
+ parser = metadata_parser
+ self.metadata = parser.metadata
+ self.expires_at = parser.expires_at if parser.respond_to?(:expires_at)
+ save!
+ rescue StandardError => err
+ Gitlab::AppLogger.error("Secure File Parser Failure (#{id}): #{err.message} - #{parser.error}.")
+ nil
+ end
+ end
+
private
def assign_checksum
diff --git a/app/models/group_label.rb b/app/models/group_label.rb
index ff14529c6e6..0d2eb524929 100644
--- a/app/models/group_label.rb
+++ b/app/models/group_label.rb
@@ -2,6 +2,7 @@
class GroupLabel < Label
belongs_to :group
+ belongs_to :parent_container, foreign_key: :group_id, class_name: 'Group'
validates :group, presence: true
diff --git a/app/models/label.rb b/app/models/label.rb
index 6608a0573cb..35daca92089 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -42,6 +42,7 @@ class Label < ApplicationRecord
scope :order_name_asc, -> { reorder(title: :asc) }
scope :order_name_desc, -> { reorder(title: :desc) }
scope :subscribed_by, ->(user_id) { joins(:subscriptions).where(subscriptions: { user_id: user_id, subscribed: true }) }
+ scope :with_preloaded_container, -> { preload(parent_container: :route) }
scope :top_labels_by_target, -> (target_relation) {
label_id_column = arel_table[:id]
@@ -59,6 +60,14 @@ class Label < ApplicationRecord
.distinct
}
+ scope :for_targets, ->(target_ids, targets_type) do
+ joins(:label_links)
+ .where(label_links: { target_id: target_ids })
+ .where(label_links: { target_type: targets_type })
+ .select("labels.*, target_id")
+ .with_preloaded_container
+ end
+
def self.prioritized(project)
joins(:priorities)
.where(label_priorities: { project_id: project })
diff --git a/app/models/preloaders/labels_preloader.rb b/app/models/preloaders/labels_preloader.rb
index 722d588d8bc..b6e73c1cd02 100644
--- a/app/models/preloaders/labels_preloader.rb
+++ b/app/models/preloaders/labels_preloader.rb
@@ -21,8 +21,10 @@ module Preloaders
def preload_all
preloader = ActiveRecord::Associations::Preloader.new
+ preloader.preload(labels, parent_container: :route)
preloader.preload(labels.select { |l| l.is_a? ProjectLabel }, { project: [:project_feature, namespace: :route] })
preloader.preload(labels.select { |l| l.is_a? GroupLabel }, { group: :route })
+
labels.each do |label|
label.lazy_subscription(user)
label.lazy_subscription(user, project) if project.present?
diff --git a/app/models/project_label.rb b/app/models/project_label.rb
index d0b16cc98b4..dc647901b46 100644
--- a/app/models/project_label.rb
+++ b/app/models/project_label.rb
@@ -4,6 +4,7 @@ class ProjectLabel < Label
MAX_NUMBER_OF_PRIORITIES = 1
belongs_to :project
+ belongs_to :parent_container, foreign_key: :project_id, class_name: 'Project'
validates :project, presence: true
diff --git a/app/models/user.rb b/app/models/user.rb
index d64a52ff7b9..be3bec0d142 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -935,6 +935,7 @@ class User < ApplicationRecord
# that the password is the user's password
def valid_password?(password)
return false unless password_allowed?(password)
+ return false if password_automatically_set?
return super if Feature.enabled?(:pbkdf2_password_encryption)
Devise::Encryptor.compare(self.class, encrypted_password, password)
diff --git a/app/policies/group_label_policy.rb b/app/policies/group_label_policy.rb
index 9f3acd44b23..4a848e44fec 100644
--- a/app/policies/group_label_policy.rb
+++ b/app/policies/group_label_policy.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class GroupLabelPolicy < BasePolicy
- delegate { @subject.group }
+ delegate { @subject.parent_container }
end
diff --git a/app/policies/project_label_policy.rb b/app/policies/project_label_policy.rb
index 5ce896ecaf2..6656d5990a5 100644
--- a/app/policies/project_label_policy.rb
+++ b/app/policies/project_label_policy.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class ProjectLabelPolicy < BasePolicy
- delegate { @subject.project }
+ delegate { @subject.parent_container }
end
diff --git a/app/services/labels/promote_service.rb b/app/services/labels/promote_service.rb
index e3b110f8f26..2786a2e357e 100644
--- a/app/services/labels/promote_service.rb
+++ b/app/services/labels/promote_service.rb
@@ -9,7 +9,7 @@ module Labels
return unless project.group &&
label.is_a?(ProjectLabel)
- Label.transaction do
+ ProjectLabel.transaction do
# use the existing group label if it exists
group_label = find_or_create_group_label(label)
@@ -50,7 +50,7 @@ module Labels
.new(current_user, title: group_label.title, group_id: project.group.id)
.execute(skip_authorization: true)
.where.not(id: group_label)
- .select(:id) # Can't use pluck() to avoid object-creation because of the batching
+ .select(:id, :project_id, :group_id, :type) # Can't use pluck() to avoid object-creation because of the batching
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 6d31a29f5a7..6b4f9dbe509 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -26,6 +26,7 @@ module MergeRequests
@merge_request = merge_request
@options = options
+ jid = merge_jid
validate!
@@ -37,7 +38,7 @@ module MergeRequests
end
end
- log_info("Merge process finished on JID #{merge_jid} with state #{state}")
+ log_info("Merge process finished on JID #{jid} with state #{state}")
rescue MergeError => e
handle_merge_error(log_message: e.message, save_message_on_model: true)
ensure
@@ -159,17 +160,32 @@ module MergeRequests
end
def handle_merge_error(log_message:, save_message_on_model: false)
- Gitlab::AppLogger.error("MergeService ERROR: #{merge_request_info} - #{log_message}")
+ log_error("MergeService ERROR: #{merge_request_info} - #{log_message}")
@merge_request.update(merge_error: log_message) if save_message_on_model
end
def log_info(message)
+ payload = log_payload("#{merge_request_info} - #{message}")
+ logger.info(**payload)
+ end
+
+ def log_error(message)
+ payload = log_payload(message)
+ logger.error(**payload)
+ end
+
+ def logger
@logger ||= Gitlab::AppLogger
- @logger.info("#{merge_request_info} - #{message}")
+ end
+
+ def log_payload(message)
+ Gitlab::ApplicationContext.current
+ .merge(merge_request_info: merge_request_info,
+ message: message)
end
def merge_request_info
- merge_request.to_reference(full: true)
+ @merge_request_info ||= merge_request.to_reference(full: true)
end
def source_matches?
diff --git a/app/services/pages_domains/create_acme_order_service.rb b/app/services/pages_domains/create_acme_order_service.rb
index e289a78091b..c600f497fa5 100644
--- a/app/services/pages_domains/create_acme_order_service.rb
+++ b/app/services/pages_domains/create_acme_order_service.rb
@@ -2,9 +2,6 @@
module PagesDomains
class CreateAcmeOrderService
- # elliptic curve algorithm to generate the private key
- ECDSA_CURVE = "prime256v1"
-
attr_reader :pages_domain
def initialize(pages_domain)
@@ -17,12 +14,7 @@ module PagesDomains
challenge = order.new_challenge
- private_key = if Feature.enabled?(:pages_lets_encrypt_ecdsa, pages_domain.project)
- OpenSSL::PKey::EC.generate(ECDSA_CURVE)
- else
- OpenSSL::PKey::RSA.new(4096)
- end
-
+ private_key = OpenSSL::PKey::RSA.new(4096)
saved_order = pages_domain.acme_orders.create!(
url: order.url,
expires_at: order.expires,
diff --git a/app/views/projects/releases/index.html.haml b/app/views/projects/releases/index.html.haml
index 9ddf2201fad..975abaefc6c 100644
--- a/app/views/projects/releases/index.html.haml
+++ b/app/views/projects/releases/index.html.haml
@@ -1,4 +1,5 @@
- page_title _('Releases')
+- add_page_specific_style 'page_bundles/releases'
- if use_startup_query_for_index_page?
- add_page_startup_graphql_call('releases/all_releases', index_page_startup_query_variables)
diff --git a/app/views/projects/releases/show.html.haml b/app/views/projects/releases/show.html.haml
index 91ee9ad70a3..66b187c8c72 100644
--- a/app/views/projects/releases/show.html.haml
+++ b/app/views/projects/releases/show.html.haml
@@ -1,5 +1,6 @@
- add_to_breadcrumbs _("Releases"), project_releases_path(@project)
- page_title @release.name
- page_description @release.description_html
+- add_page_specific_style 'page_bundles/releases'
#js-show-release-page{ data: data_for_show_page }
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 26f9f45e4bd..e02285e7f34 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -2181,6 +2181,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: ci_parse_secure_file_metadata
+ :worker_name: Ci::ParseSecureFileMetadataWorker
+ :feature_category: :mobile_signing_deployment
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: ci_runners_process_runner_version_update
:worker_name: Ci::Runners::ProcessRunnerVersionUpdateWorker
:feature_category: :runner_fleet
diff --git a/app/workers/ci/parse_secure_file_metadata_worker.rb b/app/workers/ci/parse_secure_file_metadata_worker.rb
new file mode 100644
index 00000000000..0d2495d3155
--- /dev/null
+++ b/app/workers/ci/parse_secure_file_metadata_worker.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Ci
+ class ParseSecureFileMetadataWorker
+ include ::ApplicationWorker
+
+ feature_category :mobile_signing_deployment
+ urgency :low
+ idempotent!
+
+ def perform(secure_file_id)
+ ::Ci::SecureFile.find_by_id(secure_file_id).try(&:update_metadata!)
+ end
+ end
+end