Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1287690a36
commit
ca3ff7f842
|
@ -521,6 +521,7 @@ Style/ClassAndModuleChildren:
|
|||
- 'ee/db/fixtures/development/21_dast_profiles.rb'
|
||||
- 'ee/db/fixtures/development/30_customizable_cycle_analytics.rb'
|
||||
- 'ee/db/fixtures/development/32_compliance_report_violations.rb'
|
||||
- 'ee/db/fixtures/development/35_merge_request_predictions.rb'
|
||||
- 'ee/db/fixtures/development/90_productivity_analytics.rb'
|
||||
- 'ee/lib/ee/gitlab/analytics/cycle_analytics/aggregated/base_query_builder.rb'
|
||||
- 'ee/lib/ee/gitlab/analytics/cycle_analytics/base_query_builder.rb'
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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 }">
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
.milestone-combobox {
|
||||
.dropdown-menu.show {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
.release-block {
|
||||
transition: background-color 1s linear;
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -47,7 +47,6 @@ module Resolvers
|
|||
def preloads
|
||||
{
|
||||
alert_management_alert: [:alert_management_alert],
|
||||
labels: [:labels],
|
||||
assignees: [:assignees],
|
||||
participants: Issue.participant_includes,
|
||||
timelogs: [:timelogs],
|
||||
|
|
|
@ -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
|
|
@ -42,7 +42,6 @@ module ResolvesMergeRequests
|
|||
assignees: [:assignees],
|
||||
reviewers: [:reviewers],
|
||||
participants: MergeRequest.participant_includes,
|
||||
labels: [:labels],
|
||||
author: [:author],
|
||||
merged_at: [:metrics],
|
||||
commit_count: [:metrics],
|
||||
|
|
|
@ -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.'
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class GroupLabelPolicy < BasePolicy
|
||||
delegate { @subject.group }
|
||||
delegate { @subject.parent_container }
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProjectLabelPolicy < BasePolicy
|
||||
delegate { @subject.project }
|
||||
delegate { @subject.parent_container }
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -294,6 +294,7 @@ module Gitlab
|
|||
config.assets.precompile << "page_bundles/milestone.css"
|
||||
config.assets.precompile << "page_bundles/new_namespace.css"
|
||||
config.assets.precompile << "page_bundles/oncall_schedules.css"
|
||||
config.assets.precompile << "page_bundles/operations.css"
|
||||
config.assets.precompile << "page_bundles/escalation_policies.css"
|
||||
config.assets.precompile << "page_bundles/pipeline.css"
|
||||
config.assets.precompile << "page_bundles/pipeline_schedules.css"
|
||||
|
@ -306,6 +307,7 @@ module Gitlab
|
|||
config.assets.precompile << "page_bundles/project.css"
|
||||
config.assets.precompile << "page_bundles/projects_edit.css"
|
||||
config.assets.precompile << "page_bundles/prometheus.css"
|
||||
config.assets.precompile << "page_bundles/releases.css"
|
||||
config.assets.precompile << "page_bundles/reports.css"
|
||||
config.assets.precompile << "page_bundles/roadmap.css"
|
||||
config.assets.precompile << "page_bundles/requirements.css"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: pages_lets_encrypt_ecdsa
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88125
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363026
|
||||
milestone: '15.1'
|
||||
name: approval_rules_eligible_filter
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100192
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/376331
|
||||
milestone: '15.5'
|
||||
type: development
|
||||
group: group::editor
|
||||
group: group::source code
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: secure_files_metadata_parsers
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99046
|
||||
rollout_issue_url:
|
||||
milestone: '15.5'
|
||||
type: development
|
||||
group: group::incubation
|
||||
default_enabled: false
|
|
@ -93,6 +93,8 @@
|
|||
- 1
|
||||
- - ci_job_artifacts_expire_project_build_artifacts
|
||||
- 1
|
||||
- - ci_parse_secure_file_metadata
|
||||
- 1
|
||||
- - ci_runners_process_runner_version_update
|
||||
- 1
|
||||
- - ci_upstream_projects_subscriptions_cleanup
|
||||
|
|
|
@ -218,7 +218,7 @@ Instead of:
|
|||
|
||||
## cannot, can not
|
||||
|
||||
Use **cannot** instead of **can not**. You can also use **can't**.
|
||||
Use **cannot** instead of **can not**.
|
||||
|
||||
See also [contractions](index.md#contractions).
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ Some places in the code refer to both the GitLab and GitHub specifications
|
|||
simultaneous in the same areas of logic. In these situations,
|
||||
_GitHub_ Flavored Markdown may be referred to with variable or constant names like
|
||||
`ghfm_` to avoid confusion. For example, we use the `ghfm` acronym for the
|
||||
[`ghfm_spec_v_0.29.txt` GitHub Flavored Markdown specification file](#github-flavored-markdown-specification)
|
||||
[`ghfm_spec_v_0.29.md` GitHub Flavored Markdown specification file](#github-flavored-markdown-specification),
|
||||
which is committed to the `gitlab` repository and used as input to the
|
||||
[`update_specification.rb` script](#update-specificationrb-script).
|
||||
|
||||
|
@ -618,8 +618,8 @@ subgraph script:
|
|||
A --> B{Backend Markdown API}
|
||||
end
|
||||
subgraph input:<br/>input specification files
|
||||
C[ghfm_spec_v_0.29.txt] --> A
|
||||
D[glfm_intro.txt] --> A
|
||||
C[ghfm_spec_v_0.29.md] --> A
|
||||
D[glfm_intro.md] --> A
|
||||
E[glfm_official_specification_examples.md] --> A
|
||||
F[glfm_internal_extension_examples.md] --> A
|
||||
end
|
||||
|
@ -749,7 +749,7 @@ subcategories based on their usage and purpose:
|
|||
These are the original input to drive all other automated GLFM
|
||||
specification scripts, processes, or tests.
|
||||
- `github_flavored_markdown`: Contains only the downloaded and committed
|
||||
[`ghfm_spec_v_0.29.txt`](#github-flavored-markdown-specification) specification.
|
||||
[`ghfm_spec_v_0.29.md`](#github-flavored-markdown-specification) specification.
|
||||
- `gitlab_flavored_markdown`: Contains all `glfm_*` files.
|
||||
- `*.md` [input specification files](#input-specification-files),
|
||||
which represent the GLFM specification itself.
|
||||
|
@ -769,19 +769,20 @@ See the main [specification files](#specification-files) section for more contex
|
|||
|
||||
##### GitHub Flavored Markdown specification
|
||||
|
||||
[`glfm_specification/input/github_flavored_markdown/ghfm_spec_v_0.29.txt`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/input/github_flavored_markdown/ghfm_spec_v_0.29.txt)
|
||||
is the official latest [GFM `spec.txt`](https://github.com/github/cmark-gfm/blob/master/test/spec.txt).
|
||||
[`glfm_specification/input/github_flavored_markdown/ghfm_spec_v_0.29.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/input/github_flavored_markdown/ghfm_spec_v_0.29.md)
|
||||
is a copy of the official latest [GFM `spec.txt`](https://github.com/github/cmark-gfm/blob/master/test/spec.txt).
|
||||
|
||||
- It is automatically downloaded and updated by `update-specification.rb` script.
|
||||
- It is automatically downloaded and updated by the `update-specification.rb` script.
|
||||
- When it is downloaded, the version number is added to the filename.
|
||||
- The extension is changed from `*.txt` to `*.md` so that it can be handled better by Markdown editors.
|
||||
|
||||
NOTE:
|
||||
For extra clarity, this file uses the `ghfm` acronym in its name instead of `gfm`, as
|
||||
explained in the [Acronyms section](#acronyms-glfm-ghfm-gfm-commonmark).
|
||||
|
||||
##### `glfm_intro.txt`
|
||||
##### `glfm_intro.md`
|
||||
|
||||
[`glfm_specification/input/gitlab_flavored_markdown/glfm_intro.txt`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/input/gitlab_flavored_markdown/glfm_intro.txt)
|
||||
[`glfm_specification/input/gitlab_flavored_markdown/glfm_intro.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/input/gitlab_flavored_markdown/glfm_intro.md)
|
||||
is the GitLab-specific version of the prose in the introduction section of the GLFM specification.
|
||||
|
||||
- It is manually updated.
|
||||
|
|
|
@ -2077,7 +2077,7 @@ followed by one of the strings (case-insensitive) `address`,
|
|||
`h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `head`, `header`, `hr`,
|
||||
`html`, `iframe`, `legend`, `li`, `link`, `main`, `menu`, `menuitem`,
|
||||
`nav`, `noframes`, `ol`, `optgroup`, `option`, `p`, `param`,
|
||||
`section`, `source`, `summary`, `table`, `tbody`, `td`,
|
||||
`section`, `summary`, `table`, `tbody`, `td`,
|
||||
`tfoot`, `th`, `thead`, `title`, `tr`, `track`, `ul`, followed
|
||||
by [whitespace], the end of the line, the string `>`, or
|
||||
the string `/>`.\
|
||||
|
@ -10224,4 +10224,3 @@ closers:
|
|||
|
||||
After we're done, we remove all delimiters above `stack_bottom` from the
|
||||
delimiter stack.
|
||||
|
|
@ -1791,7 +1791,7 @@ followed by one of the strings (case-insensitive) `address`,
|
|||
`h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `head`, `header`, `hr`,
|
||||
`html`, `iframe`, `legend`, `li`, `link`, `main`, `menu`, `menuitem`,
|
||||
`nav`, `noframes`, `ol`, `optgroup`, `option`, `p`, `param`,
|
||||
`section`, `source`, `summary`, `table`, `tbody`, `td`,
|
||||
`section`, `summary`, `table`, `tbody`, `td`,
|
||||
`tfoot`, `th`, `thead`, `title`, `tr`, `track`, `ul`, followed
|
||||
by [whitespace], the end of the line, the string `>`, or
|
||||
the string `/>`.\
|
||||
|
@ -10328,4 +10328,3 @@ closers:
|
|||
|
||||
After we're done, we remove all delimiters above `stack_bottom` from the
|
||||
delimiter stack.
|
||||
|
||||
|
|
|
@ -74,6 +74,10 @@ module API
|
|||
file_too_large! unless secure_file.file.size < ::Ci::SecureFile::FILE_SIZE_LIMIT.to_i
|
||||
|
||||
if secure_file.save
|
||||
if Feature.enabled?(:secure_files_metadata_parsers, user_project)
|
||||
::Ci::ParseSecureFileMetadataWorker.perform_async(secure_file.id) # rubocop:disable CodeReuse/Worker
|
||||
end
|
||||
|
||||
present secure_file, with: Entities::Ci::SecureFile
|
||||
else
|
||||
render_validation_error!(secure_file)
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Ci
|
||||
module SecureFiles
|
||||
class Cer
|
||||
attr_reader :error
|
||||
|
||||
def initialize(filedata)
|
||||
@filedata = filedata
|
||||
@error = nil
|
||||
end
|
||||
|
||||
def certificate_data
|
||||
@certificate_data ||= begin
|
||||
OpenSSL::X509::Certificate.new(@filedata)
|
||||
rescue StandardError => err
|
||||
@error = err.to_s
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def metadata
|
||||
return {} unless certificate_data
|
||||
|
||||
{
|
||||
issuer: issuer,
|
||||
subject: subject,
|
||||
id: id,
|
||||
expires_at: expires_at
|
||||
}
|
||||
end
|
||||
|
||||
def expires_at
|
||||
return unless certificate_data
|
||||
|
||||
certificate_data.not_before
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def id
|
||||
certificate_data.serial.to_s
|
||||
end
|
||||
|
||||
def issuer
|
||||
X509Name.parse(certificate_data.issuer)
|
||||
end
|
||||
|
||||
def subject
|
||||
X509Name.parse(certificate_data.subject)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Ci
|
||||
module SecureFiles
|
||||
class X509Name
|
||||
def self.parse(x509_name)
|
||||
x509_name.to_utf8.split(',').to_h { |a| a.split('=') }
|
||||
rescue StandardError
|
||||
{}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -42,3 +42,8 @@ variables:
|
|||
test:
|
||||
script:
|
||||
- vendor/bin/phpunit --configuration phpunit.xml --coverage-text --colors=never
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script: echo "Define your deployment script!"
|
||||
environment: production
|
||||
|
|
|
@ -55,3 +55,8 @@ pages:
|
|||
- public
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script: echo "Define your deployment script!"
|
||||
environment: production
|
||||
|
|
|
@ -40,3 +40,8 @@ test:cargo:
|
|||
# when: always
|
||||
# reports:
|
||||
# junit: $CI_PROJECT_DIR/tests/*.xml
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script: echo "Define your deployment script!"
|
||||
environment: production
|
||||
|
|
|
@ -30,3 +30,8 @@ test:
|
|||
script:
|
||||
# Execute your project's tests
|
||||
- sbt clean test
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script: echo "Define your deployment script!"
|
||||
environment: production
|
||||
|
|
|
@ -38,3 +38,8 @@ archive_project:
|
|||
- ios_11-3
|
||||
- xcode_9-3
|
||||
- macos_10-13
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script: echo "Define your deployment script!"
|
||||
environment: production
|
||||
|
|
|
@ -12,7 +12,7 @@ module Gitlab
|
|||
return if jira_labels.blank?
|
||||
|
||||
existing_labels = LabelsFinder.new(nil, project: project, title: jira_labels)
|
||||
.execute(skip_authorization: true).select(:id, :name)
|
||||
.execute(skip_authorization: true).select(:id, :project_id, :group_id, :type, :name)
|
||||
new_labels = create_missing_labels(existing_labels)
|
||||
|
||||
label_ids = existing_labels.map(&:id)
|
||||
|
|
|
@ -10,12 +10,12 @@ module Glfm
|
|||
# GitHub Flavored Markdown specification file
|
||||
GHFM_SPEC_TXT_URI = 'https://raw.githubusercontent.com/github/cmark-gfm/master/test/spec.txt'
|
||||
GHFM_SPEC_VERSION = '0.29'
|
||||
GHFM_SPEC_TXT_FILENAME = "ghfm_spec_v_#{GHFM_SPEC_VERSION}.txt"
|
||||
GHFM_SPEC_TXT_PATH = specification_path.join('input/github_flavored_markdown', GHFM_SPEC_TXT_FILENAME)
|
||||
GHFM_SPEC_MD_FILENAME = "ghfm_spec_v_#{GHFM_SPEC_VERSION}.md"
|
||||
GHFM_SPEC_MD_PATH = specification_path.join('input/github_flavored_markdown', GHFM_SPEC_MD_FILENAME)
|
||||
|
||||
# GitLab Flavored Markdown specification files
|
||||
specification_input_glfm_path = specification_path.join('input/gitlab_flavored_markdown')
|
||||
GLFM_INTRO_TXT_PATH = specification_input_glfm_path.join('glfm_intro.txt')
|
||||
GLFM_INTRO_MD_PATH = specification_input_glfm_path.join('glfm_intro.md')
|
||||
GLFM_EXAMPLES_TXT_PATH = specification_input_glfm_path.join('glfm_canonical_examples.txt')
|
||||
GLFM_EXAMPLE_STATUS_YML_PATH = specification_input_glfm_path.join('glfm_example_status.yml')
|
||||
GLFM_EXAMPLE_METADATA_YML_PATH =
|
||||
|
|
|
@ -12,16 +12,16 @@ module Glfm
|
|||
|
||||
def process
|
||||
output('Updating specification...')
|
||||
ghfm_spec_txt_lines = load_ghfm_spec_txt
|
||||
glfm_spec_txt_string = build_glfm_spec_txt(ghfm_spec_txt_lines)
|
||||
ghfm_spec_lines = load_ghfm_spec
|
||||
glfm_spec_txt_string = build_glfm_spec_txt(ghfm_spec_lines)
|
||||
write_glfm_spec_txt(glfm_spec_txt_string)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_ghfm_spec_txt
|
||||
def load_ghfm_spec
|
||||
# We only re-download the GitHub Flavored Markdown specification if the
|
||||
# UPDATE_GHFM_SPEC_TXT environment variable is set to true, which should only
|
||||
# UPDATE_GHFM_SPEC_MD environment variable is set to true, which should only
|
||||
# ever be done manually and locally, never in CI. This provides some security
|
||||
# protection against a possible injection attack vector, if the GitHub-hosted
|
||||
# version of the spec is ever temporarily compromised with an injection attack.
|
||||
|
@ -29,40 +29,40 @@ module Glfm
|
|||
# This also avoids doing external network access to download the file
|
||||
# in CI jobs, which can avoid potentially flaky builds if the GitHub-hosted
|
||||
# version of the file is temporarily unavailable.
|
||||
if ENV['UPDATE_GHFM_SPEC_TXT'] == 'true'
|
||||
download_and_write_ghfm_spec_txt
|
||||
if ENV['UPDATE_GHFM_SPEC_MD'] == 'true'
|
||||
update_ghfm_spec_md
|
||||
else
|
||||
read_existing_ghfm_spec_txt
|
||||
read_existing_ghfm_spec_md
|
||||
end
|
||||
end
|
||||
|
||||
def read_existing_ghfm_spec_txt
|
||||
output("Reading existing #{GHFM_SPEC_TXT_PATH}...")
|
||||
File.open(GHFM_SPEC_TXT_PATH).readlines
|
||||
def read_existing_ghfm_spec_md
|
||||
output("Reading existing #{GHFM_SPEC_MD_PATH}...")
|
||||
File.open(GHFM_SPEC_MD_PATH).readlines
|
||||
end
|
||||
|
||||
def download_and_write_ghfm_spec_txt
|
||||
def update_ghfm_spec_md
|
||||
output("Downloading #{GHFM_SPEC_TXT_URI}...")
|
||||
ghfm_spec_txt_uri_io = URI.open(GHFM_SPEC_TXT_URI)
|
||||
|
||||
# Read IO stream into an array of lines for easy processing later
|
||||
ghfm_spec_txt_lines = ghfm_spec_txt_uri_io.readlines
|
||||
raise "Unable to read lines from #{GHFM_SPEC_TXT_URI}" if ghfm_spec_txt_lines.empty?
|
||||
ghfm_spec_lines = ghfm_spec_txt_uri_io.readlines
|
||||
raise "Unable to read lines from #{GHFM_SPEC_TXT_URI}" if ghfm_spec_lines.empty?
|
||||
|
||||
# Make sure the GHFM spec version has not changed
|
||||
validate_expected_spec_version!(ghfm_spec_txt_lines[2])
|
||||
validate_expected_spec_version!(ghfm_spec_lines[2])
|
||||
|
||||
# Reset IO stream and re-read into a single string for easy writing
|
||||
# noinspection RubyNilAnalysis
|
||||
ghfm_spec_txt_uri_io.seek(0)
|
||||
ghfm_spec_txt_string = ghfm_spec_txt_uri_io.read
|
||||
raise "Unable to read string from #{GHFM_SPEC_TXT_URI}" unless ghfm_spec_txt_string
|
||||
ghfm_spec_string = ghfm_spec_txt_uri_io.read
|
||||
raise "Unable to read string from #{GHFM_SPEC_TXT_URI}" unless ghfm_spec_string
|
||||
|
||||
output("Writing #{GHFM_SPEC_TXT_PATH}...")
|
||||
GHFM_SPEC_TXT_PATH.dirname.mkpath
|
||||
write_file(GHFM_SPEC_TXT_PATH, ghfm_spec_txt_string)
|
||||
output("Writing #{GHFM_SPEC_MD_PATH}...")
|
||||
GHFM_SPEC_MD_PATH.dirname.mkpath
|
||||
write_file(GHFM_SPEC_MD_PATH, ghfm_spec_string)
|
||||
|
||||
ghfm_spec_txt_lines
|
||||
ghfm_spec_lines
|
||||
end
|
||||
|
||||
def validate_expected_spec_version!(version_line)
|
||||
|
@ -85,13 +85,13 @@ module Glfm
|
|||
end
|
||||
|
||||
def replace_intro_section(spec_txt_lines)
|
||||
glfm_intro_txt_lines = File.open(GLFM_INTRO_TXT_PATH).readlines
|
||||
raise "Unable to read lines from #{GLFM_INTRO_TXT_PATH}" if glfm_intro_txt_lines.empty?
|
||||
glfm_intro_md_lines = File.open(GLFM_INTRO_MD_PATH).readlines
|
||||
raise "Unable to read lines from #{GLFM_INTRO_MD_PATH}" if glfm_intro_md_lines.empty?
|
||||
|
||||
ghfm_intro_header_begin_index = spec_txt_lines.index do |line|
|
||||
line =~ INTRODUCTION_HEADER_LINE_TEXT
|
||||
end
|
||||
raise "Unable to locate introduction header line in #{GHFM_SPEC_TXT_PATH}" if ghfm_intro_header_begin_index.nil?
|
||||
raise "Unable to locate introduction header line in #{GHFM_SPEC_MD_PATH}" if ghfm_intro_header_begin_index.nil?
|
||||
|
||||
# Find the index of the next header after the introduction header, starting from the index
|
||||
# of the introduction header this is the length of the intro section
|
||||
|
@ -100,7 +100,7 @@ module Glfm
|
|||
end
|
||||
|
||||
# Replace the intro section with the GitLab flavored Markdown intro section
|
||||
spec_txt_lines[ghfm_intro_header_begin_index, ghfm_intro_section_length] = glfm_intro_txt_lines
|
||||
spec_txt_lines[ghfm_intro_header_begin_index, ghfm_intro_section_length] = glfm_intro_md_lines
|
||||
end
|
||||
|
||||
def insert_examples_txt(spec_txt_lines)
|
||||
|
@ -110,7 +110,7 @@ module Glfm
|
|||
ghfm_end_tests_comment_index = spec_txt_lines.index do |line|
|
||||
line =~ END_TESTS_COMMENT_LINE_TEXT
|
||||
end
|
||||
raise "Unable to locate 'END TESTS' comment line in #{GHFM_SPEC_TXT_PATH}" if ghfm_end_tests_comment_index.nil?
|
||||
raise "Unable to locate 'END TESTS' comment line in #{GHFM_SPEC_MD_PATH}" if ghfm_end_tests_comment_index.nil?
|
||||
|
||||
# Insert the GLFM examples before the 'END TESTS' comment line
|
||||
spec_txt_lines[ghfm_end_tests_comment_index - 1] = ["\n", glfm_examples_txt_lines, "\n"].flatten
|
||||
|
|
|
@ -51,11 +51,11 @@ RSpec.describe 'Profile > Password' do
|
|||
end
|
||||
|
||||
context 'Password authentication unavailable' do
|
||||
before do
|
||||
gitlab_sign_in(user)
|
||||
end
|
||||
|
||||
context 'Regular user' do
|
||||
before do
|
||||
gitlab_sign_in(user)
|
||||
end
|
||||
|
||||
let(:user) { create(:user) }
|
||||
|
||||
it 'renders 404 when password authentication is disabled for the web interface and Git' do
|
||||
|
@ -69,7 +69,22 @@ RSpec.describe 'Profile > Password' do
|
|||
end
|
||||
|
||||
context 'LDAP user' do
|
||||
include LdapHelpers
|
||||
|
||||
let(:ldap_settings) { { enabled: true } }
|
||||
let(:user) { create(:omniauth_user, provider: 'ldapmain') }
|
||||
let(:provider) { 'ldapmain' }
|
||||
let(:provider_label) { 'Main LDAP' }
|
||||
|
||||
before do
|
||||
stub_ldap_setting(ldap_settings)
|
||||
stub_ldap_access(user, provider, provider_label)
|
||||
sign_in_using_ldap!(user, provider_label, provider)
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
Rails.application.reload_routes!
|
||||
end
|
||||
|
||||
it 'renders 404' do
|
||||
visit edit_profile_password_path
|
||||
|
|
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
import { GlTable, GlAlert, GlLoadingIcon, GlDropdown, GlIcon, GlAvatar } from '@gitlab/ui';
|
||||
import { GlTable, GlAlert, GlLoadingIcon, GlDropdown, GlIcon, GlAvatar, GlLink } from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import axios from 'axios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
|
@ -31,6 +31,7 @@ describe('AlertManagementTable', () => {
|
|||
const findSearch = () => wrapper.findComponent(FilteredSearchBar);
|
||||
const findSeverityColumnHeader = () => wrapper.findByTestId('alert-management-severity-sort');
|
||||
const findFirstIDField = () => wrapper.findAllByTestId('idField').at(0);
|
||||
const findFirstIDLink = () => wrapper.findAllByTestId('idField').at(0).findComponent(GlLink);
|
||||
const findAssignees = () => wrapper.findAllByTestId('assigneesField');
|
||||
const findSeverityFields = () => wrapper.findAllByTestId('severityField');
|
||||
const findIssueFields = () => wrapper.findAllByTestId('issueField');
|
||||
|
@ -135,10 +136,11 @@ describe('AlertManagementTable', () => {
|
|||
expect(findLoader().exists()).toBe(false);
|
||||
expect(findAlertsTable().exists()).toBe(true);
|
||||
expect(findAlerts()).toHaveLength(mockAlerts.length);
|
||||
expect(findAlerts().at(0).classes()).toContain('gl-hover-bg-blue-50');
|
||||
expect(findAlerts().at(0).classes()).toContain('gl-hover-bg-gray-50');
|
||||
expect(findAlerts().at(0).classes()).not.toContain('gl-hover-border-blue-200');
|
||||
});
|
||||
|
||||
it('displays the alert ID and title formatted correctly', () => {
|
||||
it('displays the alert ID and title as a link', () => {
|
||||
mountComponent({
|
||||
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
||||
loading: false,
|
||||
|
@ -146,6 +148,8 @@ describe('AlertManagementTable', () => {
|
|||
|
||||
expect(findFirstIDField().exists()).toBe(true);
|
||||
expect(findFirstIDField().text()).toBe(`#${mockAlerts[0].iid} ${mockAlerts[0].title}`);
|
||||
expect(findFirstIDLink().text()).toBe(`#${mockAlerts[0].iid} ${mockAlerts[0].title}`);
|
||||
expect(findFirstIDLink().attributes('href')).toBe('/1527542/details');
|
||||
});
|
||||
|
||||
it('displays status dropdown', () => {
|
||||
|
@ -266,7 +270,8 @@ describe('AlertManagementTable', () => {
|
|||
alerts: {
|
||||
list: [
|
||||
{
|
||||
iid: 1,
|
||||
iid: '1',
|
||||
title: 'SyntaxError: Invalid or unexpected token',
|
||||
status: 'acknowledged',
|
||||
startedAt: '2020-03-17T23:18:14.996Z',
|
||||
severity: 'high',
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::SecureFiles::Cer do
|
||||
context 'when the supplied certificate cannot be parsed' do
|
||||
let(:invalid_certificate) { described_class.new('xyzabc') }
|
||||
let(:subject) { described_class.new('xyzabc') }
|
||||
|
||||
describe '#certificate_data' do
|
||||
it 'assigns the error message and returns nil' do
|
||||
expect(invalid_certificate.certificate_data).to be nil
|
||||
expect(invalid_certificate.error).to eq('not enough data')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#metadata' do
|
||||
it 'returns an empty hash' do
|
||||
expect(invalid_certificate.metadata).to eq({})
|
||||
end
|
||||
end
|
||||
|
||||
describe '#expires_at' do
|
||||
it 'returns nil' do
|
||||
expect(invalid_certificate.expires_at).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the supplied certificate can be parsed' do
|
||||
let(:sample_file) { fixture_file('ci_secure_files/sample.cer') }
|
||||
let(:subject) { described_class.new(sample_file) }
|
||||
|
||||
describe '#certificate_data' do
|
||||
it 'returns an OpenSSL::X509::Certificate object' do
|
||||
expect(subject.certificate_data.class).to be(OpenSSL::X509::Certificate)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#metadata' do
|
||||
it 'returns a hash with the expected keys' do
|
||||
expect(subject.metadata.keys).to match_array([:issuer, :subject, :id, :expires_at])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#id' do
|
||||
it 'returns the certificate serial number' do
|
||||
expect(subject.metadata[:id]).to eq('33669367788748363528491290218354043267')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#expires_at' do
|
||||
it 'returns the certificate expiration timestamp' do
|
||||
expect(subject.expires_at).to eq('2022-04-26 19:20:40 UTC')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#issuer' do
|
||||
it 'calls parse on X509Name' do
|
||||
expect(subject.metadata[:issuer]["O"]).to eq('Apple Inc.')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#subject' do
|
||||
it 'calls parse on X509Name' do
|
||||
expect(subject.metadata[:subject]["OU"]).to eq('N7SYAN8PX8')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::SecureFiles::X509Name do
|
||||
describe '.parse' do
|
||||
it 'parses an X509Name object into a hash format' do
|
||||
sample = OpenSSL::X509::Name.new([
|
||||
['C', 'Test Country'],
|
||||
['O', 'Test Org Name'],
|
||||
['OU', 'Test Org Unit'],
|
||||
['CN', 'Test Common Name'],
|
||||
['UID', 'Test UID']
|
||||
])
|
||||
|
||||
parsed_sample = described_class.parse(sample)
|
||||
|
||||
expect(parsed_sample["C"]).to eq('Test Country')
|
||||
expect(parsed_sample["O"]).to eq('Test Org Name')
|
||||
expect(parsed_sample["OU"]).to eq('Test Org Unit')
|
||||
expect(parsed_sample["CN"]).to eq('Test Common Name')
|
||||
expect(parsed_sample["UID"]).to eq('Test UID')
|
||||
end
|
||||
|
||||
it 'returns an empty hash when an error occurs' do
|
||||
parsed_sample = described_class.parse('unexpectedinput')
|
||||
expect(parsed_sample).to eq({})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -95,6 +95,7 @@ label_links:
|
|||
label:
|
||||
- subscriptions
|
||||
- project
|
||||
- parent_container
|
||||
- lists
|
||||
- label_links
|
||||
- issues
|
||||
|
|
|
@ -81,4 +81,60 @@ RSpec.describe Ci::SecureFile do
|
|||
expect(Base64.encode64(subject.file.read)).to eq(Base64.encode64(sample_file))
|
||||
end
|
||||
end
|
||||
|
||||
describe '#file_extension' do
|
||||
it 'returns the extension for the file name' do
|
||||
file = build(:ci_secure_file, name: 'file1.cer')
|
||||
expect(file.file_extension).to eq('cer')
|
||||
end
|
||||
|
||||
it 'returns only the last part of the extension for the file name' do
|
||||
file = build(:ci_secure_file, name: 'file1.tar.gz')
|
||||
expect(file.file_extension).to eq('gz')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#metadata_parsable?' do
|
||||
it 'returns true when the file extension has a supported parser' do
|
||||
file = build(:ci_secure_file, name: 'file1.cer')
|
||||
expect(file.metadata_parsable?).to be true
|
||||
end
|
||||
|
||||
it 'returns false when the file extension does not have a supported parser' do
|
||||
file = build(:ci_secure_file, name: 'file1.foo')
|
||||
expect(file.metadata_parsable?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#metadata_parser' do
|
||||
it 'returns an instance of Gitlab::Ci::SecureFiles::Cer when a .cer file is supplied' do
|
||||
file = build(:ci_secure_file, name: 'file1.cer')
|
||||
expect(file.metadata_parser).to be_an_instance_of(Gitlab::Ci::SecureFiles::Cer)
|
||||
end
|
||||
|
||||
it 'returns nil when the file type is not supported by any parsers' do
|
||||
file = build(:ci_secure_file, name: 'file1.foo')
|
||||
expect(file.metadata_parser).to be nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update_metadata!' do
|
||||
it 'assigns the expected metadata when a parsable file is supplied' do
|
||||
file = create(:ci_secure_file, name: 'file1.cer',
|
||||
file: CarrierWaveStringFile.new(fixture_file('ci_secure_files/sample.cer') ))
|
||||
file.update_metadata!
|
||||
|
||||
expect(file.expires_at).to eq(DateTime.parse('2022-04-26 19:20:40'))
|
||||
expect(file.metadata['id']).to eq('33669367788748363528491290218354043267')
|
||||
expect(file.metadata['issuer']['CN']).to eq('Apple Worldwide Developer Relations Certification Authority')
|
||||
expect(file.metadata['subject']['OU']).to eq('N7SYAN8PX8')
|
||||
end
|
||||
|
||||
it 'logs an error when something goes wrong with the file parsing' do
|
||||
corrupt_file = create(:ci_secure_file, name: 'file1.cer', file: CarrierWaveStringFile.new('11111111'))
|
||||
message = 'Validation failed: Metadata must be a valid json schema - not enough data.'
|
||||
expect(Gitlab::AppLogger).to receive(:error).with("Secure File Parser Failure (#{corrupt_file.id}): #{message}")
|
||||
corrupt_file.update_metadata!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6235,6 +6235,13 @@ RSpec.describe User do
|
|||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
context 'user with autogenerated_password' do
|
||||
let(:user) { build_stubbed(:user, password_automatically_set: true) }
|
||||
let(:password) { user.password }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
# These entire test section can be removed once the :pbkdf2_password_encryption feature flag is removed.
|
||||
|
|
|
@ -708,7 +708,21 @@ RSpec.describe 'getting an issue list for a project' do
|
|||
end
|
||||
|
||||
# Executes 3 extra queries to fetch participant_attrs
|
||||
include_examples 'N+1 query check', 3
|
||||
include_examples 'N+1 query check', threshold: 3
|
||||
end
|
||||
|
||||
context 'when requesting labels' do
|
||||
let(:requested_fields) { ['labels { nodes { id } }'] }
|
||||
|
||||
before do
|
||||
project_labels = create_list(:label, 2, project: project)
|
||||
group_labels = create_list(:group_label, 2, group: group)
|
||||
|
||||
issue_a.update!(labels: [project_labels.first, group_labels.first].flatten)
|
||||
issue_b.update!(labels: [project_labels, group_labels].flatten)
|
||||
end
|
||||
|
||||
include_examples 'N+1 query check', skip_cached: false
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,12 +5,14 @@ require 'spec_helper'
|
|||
RSpec.describe 'getting merge request listings nested in a project' do
|
||||
include GraphqlHelpers
|
||||
|
||||
let_it_be(:project) { create(:project, :repository, :public) }
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, :repository, :public, group: group) }
|
||||
let_it_be(:current_user) { create(:user) }
|
||||
let_it_be(:label) { create(:label, project: project) }
|
||||
let_it_be(:group_label) { create(:group_label, group: group) }
|
||||
|
||||
let_it_be_with_reload(:merge_request_a) do
|
||||
create(:labeled_merge_request, :unique_branches, source_project: project, labels: [label])
|
||||
create(:labeled_merge_request, :unique_branches, source_project: project, labels: [label, group_label])
|
||||
end
|
||||
|
||||
let_it_be(:merge_request_b) do
|
||||
|
@ -18,7 +20,7 @@ RSpec.describe 'getting merge request listings nested in a project' do
|
|||
end
|
||||
|
||||
let_it_be(:merge_request_c) do
|
||||
create(:labeled_merge_request, :closed, :unique_branches, source_project: project, labels: [label])
|
||||
create(:labeled_merge_request, :closed, :unique_branches, source_project: project, labels: [label, group_label])
|
||||
end
|
||||
|
||||
let_it_be(:merge_request_d) do
|
||||
|
@ -357,7 +359,20 @@ RSpec.describe 'getting merge request listings nested in a project' do
|
|||
end
|
||||
|
||||
# Executes 3 extra queries to fetch participant_attrs
|
||||
include_examples 'N+1 query check', 3
|
||||
include_examples 'N+1 query check', threshold: 3
|
||||
end
|
||||
|
||||
context 'when requesting labels' do
|
||||
let(:requested_fields) { ['labels { nodes { id } }'] }
|
||||
|
||||
before do
|
||||
project_labels = create_list(:label, 2, project: project)
|
||||
group_labels = create_list(:group_label, 2, group: group)
|
||||
|
||||
merge_request_c.update!(labels: [project_labels, group_labels].flatten)
|
||||
end
|
||||
|
||||
include_examples 'N+1 query check', skip_cached: false
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1759,8 +1759,7 @@ RSpec.describe 'Git HTTP requests' do
|
|||
end
|
||||
|
||||
describe "User with LDAP identity" do
|
||||
let(:user) { create(:omniauth_user, extern_uid: dn) }
|
||||
let(:dn) { 'uid=john,ou=people,dc=example,dc=com' }
|
||||
let(:user) { create(:omniauth_user, :ldap) }
|
||||
let(:path) { 'doesnt/exist.git' }
|
||||
|
||||
before do
|
||||
|
|
|
@ -7,11 +7,11 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
|
|||
|
||||
let(:ghfm_spec_txt_uri) { described_class::GHFM_SPEC_TXT_URI }
|
||||
let(:ghfm_spec_txt_uri_io) { StringIO.new(ghfm_spec_txt_contents) }
|
||||
let(:ghfm_spec_txt_path) { described_class::GHFM_SPEC_TXT_PATH }
|
||||
let(:ghfm_spec_md_path) { described_class::GHFM_SPEC_MD_PATH }
|
||||
let(:ghfm_spec_txt_local_io) { StringIO.new(ghfm_spec_txt_contents) }
|
||||
|
||||
let(:glfm_intro_txt_path) { described_class::GLFM_INTRO_TXT_PATH }
|
||||
let(:glfm_intro_txt_io) { StringIO.new(glfm_intro_txt_contents) }
|
||||
let(:glfm_intro_md_path) { described_class::GLFM_INTRO_MD_PATH }
|
||||
let(:glfm_intro_md_io) { StringIO.new(glfm_intro_md_contents) }
|
||||
let(:glfm_examples_txt_path) { described_class::GLFM_EXAMPLES_TXT_PATH }
|
||||
let(:glfm_examples_txt_io) { StringIO.new(glfm_examples_txt_contents) }
|
||||
let(:glfm_spec_txt_path) { described_class::GLFM_SPEC_TXT_PATH }
|
||||
|
@ -52,7 +52,7 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
|
|||
MARKDOWN
|
||||
end
|
||||
|
||||
let(:glfm_intro_txt_contents) do
|
||||
let(:glfm_intro_md_contents) do
|
||||
# language=Markdown
|
||||
<<~MARKDOWN
|
||||
# Introduction
|
||||
|
@ -73,15 +73,15 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
|
|||
|
||||
before do
|
||||
# Mock default ENV var values
|
||||
allow(ENV).to receive(:[]).with('UPDATE_GHFM_SPEC_TXT').and_return(nil)
|
||||
allow(ENV).to receive(:[]).with('UPDATE_GHFM_SPEC_MD').and_return(nil)
|
||||
allow(ENV).to receive(:[]).and_call_original
|
||||
|
||||
# We mock out the URI and local file IO objects with real StringIO, instead of just mock
|
||||
# objects. This gives better and more realistic coverage, while still avoiding
|
||||
# actual network and filesystem I/O during the spec run.
|
||||
allow(URI).to receive(:open).with(ghfm_spec_txt_uri) { ghfm_spec_txt_uri_io }
|
||||
allow(File).to receive(:open).with(ghfm_spec_txt_path) { ghfm_spec_txt_local_io }
|
||||
allow(File).to receive(:open).with(glfm_intro_txt_path) { glfm_intro_txt_io }
|
||||
allow(File).to receive(:open).with(ghfm_spec_md_path) { ghfm_spec_txt_local_io }
|
||||
allow(File).to receive(:open).with(glfm_intro_md_path) { glfm_intro_md_io }
|
||||
allow(File).to receive(:open).with(glfm_examples_txt_path) { glfm_examples_txt_io }
|
||||
allow(File).to receive(:open).with(glfm_spec_txt_path, 'w') { glfm_spec_txt_io }
|
||||
|
||||
|
@ -90,7 +90,7 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
|
|||
end
|
||||
|
||||
describe 'retrieving latest GHFM spec.txt' do
|
||||
context 'when UPDATE_GHFM_SPEC_TXT is not true (default)' do
|
||||
context 'when UPDATE_GHFM_SPEC_MD is not true (default)' do
|
||||
it 'does not download' do
|
||||
expect(URI).not_to receive(:open).with(ghfm_spec_txt_uri)
|
||||
|
||||
|
@ -100,12 +100,12 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when UPDATE_GHFM_SPEC_TXT is true' do
|
||||
context 'when UPDATE_GHFM_SPEC_MD is true' do
|
||||
let(:ghfm_spec_txt_local_io) { StringIO.new }
|
||||
|
||||
before do
|
||||
allow(ENV).to receive(:[]).with('UPDATE_GHFM_SPEC_TXT').and_return('true')
|
||||
allow(File).to receive(:open).with(ghfm_spec_txt_path, 'w') { ghfm_spec_txt_local_io }
|
||||
allow(ENV).to receive(:[]).with('UPDATE_GHFM_SPEC_MD').and_return('true')
|
||||
allow(File).to receive(:open).with(ghfm_spec_md_path, 'w') { ghfm_spec_txt_local_io }
|
||||
end
|
||||
|
||||
context 'with success' do
|
||||
|
@ -170,7 +170,7 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
|
|||
|
||||
it 'replaces the intro section with the GitLab version' do
|
||||
expect(glfm_contents).not_to match(/What is GitHub Flavored Markdown/m)
|
||||
expect(glfm_contents).to match(/#{Regexp.escape(glfm_intro_txt_contents)}/m)
|
||||
expect(glfm_contents).to match(/#{Regexp.escape(glfm_intro_md_contents)}/m)
|
||||
end
|
||||
|
||||
it 'inserts the GitLab examples sections before the appendix section' do
|
||||
|
|
|
@ -108,7 +108,8 @@ RSpec.describe MergeRequests::FfMergeService do
|
|||
|
||||
service.execute(merge_request)
|
||||
|
||||
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message))
|
||||
expect(Gitlab::AppLogger).to have_received(:error)
|
||||
.with(hash_including(message: a_string_matching(error_message)))
|
||||
end
|
||||
|
||||
it 'logs and saves error if there is an PreReceiveError exception' do
|
||||
|
@ -122,7 +123,8 @@ RSpec.describe MergeRequests::FfMergeService do
|
|||
service.execute(merge_request)
|
||||
|
||||
expect(merge_request.merge_error).to include(error_message)
|
||||
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message))
|
||||
expect(Gitlab::AppLogger).to have_received(:error)
|
||||
.with(hash_including(message: a_string_matching(error_message)))
|
||||
end
|
||||
|
||||
it 'does not update squash_commit_sha if squash merge is not successful' do
|
||||
|
|
|
@ -95,6 +95,42 @@ RSpec.describe MergeRequests::MergeService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'running the service once' do
|
||||
let(:ref) { merge_request.to_reference(full: true) }
|
||||
let(:jid) { SecureRandom.hex }
|
||||
|
||||
let(:messages) do
|
||||
[
|
||||
/#{ref} - Git merge started on JID #{jid}/,
|
||||
/#{ref} - Git merge finished on JID #{jid}/,
|
||||
/#{ref} - Post merge started on JID #{jid}/,
|
||||
/#{ref} - Post merge finished on JID #{jid}/,
|
||||
/#{ref} - Merge process finished on JID #{jid}/
|
||||
]
|
||||
end
|
||||
|
||||
before do
|
||||
merge_request.update!(merge_jid: jid)
|
||||
::Gitlab::ApplicationContext.push(caller_id: 'MergeWorker')
|
||||
end
|
||||
|
||||
it 'logs status messages' do
|
||||
allow(Gitlab::AppLogger).to receive(:info).and_call_original
|
||||
|
||||
messages.each do |message|
|
||||
expect(Gitlab::AppLogger).to receive(:info).with(
|
||||
hash_including(
|
||||
'meta.caller_id' => 'MergeWorker',
|
||||
message: message,
|
||||
merge_request_info: ref
|
||||
)
|
||||
).and_call_original
|
||||
end
|
||||
|
||||
service.execute(merge_request)
|
||||
end
|
||||
end
|
||||
|
||||
context 'running the service multiple time' do
|
||||
it 'is idempotent' do
|
||||
2.times { service.execute(merge_request) }
|
||||
|
@ -315,7 +351,9 @@ RSpec.describe MergeRequests::MergeService do
|
|||
service.execute(merge_request)
|
||||
|
||||
expect(merge_request.merge_error).to eq(error_message)
|
||||
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message))
|
||||
expect(Gitlab::AppLogger).to have_received(:error)
|
||||
.with(hash_including(merge_request_info: merge_request.to_reference(full: true),
|
||||
message: a_string_matching(error_message)))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -328,7 +366,9 @@ RSpec.describe MergeRequests::MergeService do
|
|||
service.execute(merge_request)
|
||||
|
||||
expect(merge_request.merge_error).to eq(described_class::GENERIC_ERROR_MESSAGE)
|
||||
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message))
|
||||
expect(Gitlab::AppLogger).to have_received(:error)
|
||||
.with(hash_including(merge_request_info: merge_request.to_reference(full: true),
|
||||
message: a_string_matching(error_message)))
|
||||
end
|
||||
|
||||
it 'logs and saves error if user is not authorized' do
|
||||
|
@ -354,7 +394,9 @@ RSpec.describe MergeRequests::MergeService do
|
|||
service.execute(merge_request)
|
||||
|
||||
expect(merge_request.merge_error).to include('Something went wrong during merge pre-receive hook')
|
||||
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message))
|
||||
expect(Gitlab::AppLogger).to have_received(:error)
|
||||
.with(hash_including(merge_request_info: merge_request.to_reference(full: true),
|
||||
message: a_string_matching(error_message)))
|
||||
end
|
||||
|
||||
it 'logs and saves error if commit is not created' do
|
||||
|
@ -366,7 +408,9 @@ RSpec.describe MergeRequests::MergeService do
|
|||
expect(merge_request).to be_open
|
||||
expect(merge_request.merge_commit_sha).to be_nil
|
||||
expect(merge_request.merge_error).to include(described_class::GENERIC_ERROR_MESSAGE)
|
||||
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(described_class::GENERIC_ERROR_MESSAGE))
|
||||
expect(Gitlab::AppLogger).to have_received(:error)
|
||||
.with(hash_including(merge_request_info: merge_request.to_reference(full: true),
|
||||
message: a_string_matching(described_class::GENERIC_ERROR_MESSAGE)))
|
||||
end
|
||||
|
||||
context 'when squashing is required' do
|
||||
|
@ -385,7 +429,9 @@ RSpec.describe MergeRequests::MergeService do
|
|||
expect(merge_request.merge_commit_sha).to be_nil
|
||||
expect(merge_request.squash_commit_sha).to be_nil
|
||||
expect(merge_request.merge_error).to include(error_message)
|
||||
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message))
|
||||
expect(Gitlab::AppLogger).to have_received(:error)
|
||||
.with(hash_including(merge_request_info: merge_request.to_reference(full: true),
|
||||
message: a_string_matching(error_message)))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -406,7 +452,9 @@ RSpec.describe MergeRequests::MergeService do
|
|||
expect(merge_request.merge_commit_sha).to be_nil
|
||||
expect(merge_request.squash_commit_sha).to be_nil
|
||||
expect(merge_request.merge_error).to include(error_message)
|
||||
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message))
|
||||
expect(Gitlab::AppLogger).to have_received(:error)
|
||||
.with(hash_including(merge_request_info: merge_request.to_reference(full: true),
|
||||
message: a_string_matching(error_message)))
|
||||
end
|
||||
|
||||
it 'logs and saves error if there is an PreReceiveError exception' do
|
||||
|
@ -422,7 +470,9 @@ RSpec.describe MergeRequests::MergeService do
|
|||
expect(merge_request.merge_commit_sha).to be_nil
|
||||
expect(merge_request.squash_commit_sha).to be_nil
|
||||
expect(merge_request.merge_error).to include('Something went wrong during merge pre-receive hook')
|
||||
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message))
|
||||
expect(Gitlab::AppLogger).to have_received(:error)
|
||||
.with(hash_including(merge_request_info: merge_request.to_reference(full: true),
|
||||
message: a_string_matching(error_message)))
|
||||
end
|
||||
|
||||
context 'when fast-forward merge is not allowed' do
|
||||
|
@ -444,7 +494,9 @@ RSpec.describe MergeRequests::MergeService do
|
|||
expect(merge_request.merge_commit_sha).to be_nil
|
||||
expect(merge_request.squash_commit_sha).to be_nil
|
||||
expect(merge_request.merge_error).to include(error_message)
|
||||
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message))
|
||||
expect(Gitlab::AppLogger).to have_received(:error)
|
||||
.with(hash_including(merge_request_info: merge_request.to_reference(full: true),
|
||||
message: a_string_matching(error_message)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -461,7 +513,9 @@ RSpec.describe MergeRequests::MergeService do
|
|||
it 'logs and saves error' do
|
||||
service.execute(merge_request)
|
||||
|
||||
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message))
|
||||
expect(Gitlab::AppLogger).to have_received(:error)
|
||||
.with(hash_including(merge_request_info: merge_request.to_reference(full: true),
|
||||
message: a_string_matching(error_message)))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -473,7 +527,9 @@ RSpec.describe MergeRequests::MergeService do
|
|||
it 'logs and saves error' do
|
||||
service.execute(merge_request)
|
||||
|
||||
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message))
|
||||
expect(Gitlab::AppLogger).to have_received(:error)
|
||||
.with(hash_including(merge_request_info: merge_request.to_reference(full: true),
|
||||
message: a_string_matching(error_message)))
|
||||
end
|
||||
|
||||
context 'when passing `skip_discussions_check: true` as `options` parameter' do
|
||||
|
|
|
@ -38,21 +38,13 @@ RSpec.describe PagesDomains::CreateAcmeOrderService do
|
|||
expect(challenge).to have_received(:request_validation).ordered
|
||||
end
|
||||
|
||||
it 'generates and saves private key: rsa' do
|
||||
stub_feature_flags(pages_lets_encrypt_ecdsa: false)
|
||||
it 'generates and saves private key' do
|
||||
service.execute
|
||||
|
||||
saved_order = PagesDomainAcmeOrder.last
|
||||
expect { OpenSSL::PKey::RSA.new(saved_order.private_key) }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'generates and saves private key: ec' do
|
||||
service.execute
|
||||
|
||||
saved_order = PagesDomainAcmeOrder.last
|
||||
expect { OpenSSL::PKey::EC.new(saved_order.private_key) }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'properly saves order attributes' do
|
||||
service.execute
|
||||
|
||||
|
|
|
@ -69,6 +69,32 @@ module LdapHelpers
|
|||
allow_any_instance_of(Gitlab::Auth::Ldap::Adapter)
|
||||
.to receive(:ldap_search).and_raise(Gitlab::Auth::Ldap::LdapConnectionError)
|
||||
end
|
||||
|
||||
def stub_ldap_access(user, provider, provider_label)
|
||||
ldap_server_config =
|
||||
{
|
||||
'label' => provider_label,
|
||||
'provider_name' => provider,
|
||||
'attributes' => {},
|
||||
'encryption' => 'plain',
|
||||
'uid' => 'uid',
|
||||
'base' => 'dc=example,dc=com'
|
||||
}
|
||||
uid = 'my-uid'
|
||||
allow(::Gitlab::Auth::Ldap::Config).to receive_messages(enabled: true, servers: [ldap_server_config])
|
||||
allow(Gitlab::Auth::OAuth::Provider).to receive_messages(providers: [provider.to_sym])
|
||||
|
||||
Ldap::OmniauthCallbacksController.define_providers!
|
||||
Rails.application.reload_routes!
|
||||
|
||||
mock_auth_hash(provider, uid, user.email)
|
||||
allow(Gitlab::Auth::Ldap::Access).to receive(:allowed?).with(user).and_return(true)
|
||||
|
||||
allow_next_instance_of(ActionDispatch::Routing::RoutesProxy) do |instance|
|
||||
allow(instance).to receive(:"user_#{provider}_omniauth_callback_path")
|
||||
.and_return("/users/auth/#{provider}/callback")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
LdapHelpers.include_mod_with('LdapHelpers')
|
||||
|
|
|
@ -119,6 +119,16 @@ module LoginHelpers
|
|||
click_button "oauth-login-#{provider}"
|
||||
end
|
||||
|
||||
def sign_in_using_ldap!(user, ldap_tab, ldap_name)
|
||||
visit new_user_session_path
|
||||
click_link ldap_tab
|
||||
fill_in 'username', with: user.username
|
||||
fill_in 'password', with: user.password
|
||||
within("##{ldap_name}") do
|
||||
click_button 'Sign in'
|
||||
end
|
||||
end
|
||||
|
||||
def register_via(provider, uid, email, additional_info: {})
|
||||
mock_auth_hash(provider, uid, email, additional_info: additional_info)
|
||||
visit new_user_registration_path
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
RSpec.shared_examples 'N+1 query check' do |threshold = 0|
|
||||
|
||||
RSpec.shared_examples 'N+1 query check' do |threshold: 0, skip_cached: true|
|
||||
it 'prevents N+1 queries' do
|
||||
execute_query # "warm up" to prevent undeterministic counts
|
||||
expect(graphql_errors).to be_blank # Sanity check - ex falso quodlibet!
|
||||
|
||||
control = ActiveRecord::QueryRecorder.new { execute_query }
|
||||
control = ActiveRecord::QueryRecorder.new(skip_cached: skip_cached) { execute_query }
|
||||
expect(control.count).to be > 0
|
||||
|
||||
search_params[:iids] << extra_iid_for_second_query
|
||||
|
||||
expect { execute_query }.not_to exceed_query_limit(control).with_threshold(threshold)
|
||||
expect { execute_query }.not_to exceed_query_count_limit(control, skip_cached: skip_cached, threshold: threshold)
|
||||
end
|
||||
|
||||
def exceed_query_count_limit(control, skip_cached: true, threshold: 0)
|
||||
if skip_cached
|
||||
exceed_query_limit(control).with_threshold(threshold)
|
||||
else
|
||||
exceed_all_query_limit(control).with_threshold(threshold)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Ci::ParseSecureFileMetadataWorker do
|
||||
describe '#perform' do
|
||||
include_examples 'an idempotent worker' do
|
||||
let(:secure_file) { create(:ci_secure_file) }
|
||||
subject { described_class.new.perform(secure_file&.id) }
|
||||
|
||||
context 'when the file is found' do
|
||||
it 'calls update_metadata!' do
|
||||
allow(::Ci::SecureFile).to receive(:find_by_id).and_return(secure_file)
|
||||
expect(secure_file).to receive(:update_metadata!)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when file is not found' do
|
||||
let(:secure_file) { nil }
|
||||
|
||||
it 'does not call update_metadata!' do
|
||||
expect(secure_file).not_to receive(:update_metadata!)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue