{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://catalog.lintel.tools/schemas/schemastore/cloud-gov-workshop-configuration/latest.json",
  "title": "Workshop Configuration",
  "description": "Schema for the Cloud.gov Workshop configuration files. Top level keys `subgroups` and `projects` are valid for Customer configs. Top level keys `namespaces` and `users` are valid for the Workshop-controlled configurations.",
  "x-lintel": {
    "source": "https://workshop.cloud.gov/workshop/workshop-schemas/-/raw/main/cg-workshop.schema.json",
    "sourceSha256": "893619de8041202a9c960e98460c50a15fc7cd623157dbe1d17896f55f86c749",
    "fileMatch": [
      "cg-workshop.yml",
      "**/cg-workshop/*.yml"
    ],
    "parsers": [
      "yaml"
    ]
  },
  "type": "object",
  "properties": {
    "namespaces": {
      "description": "Workshop top level groups. This key is invalid in customer config files.",
      "type": "object",
      "additionalProperties": false,
      "patternProperties": {
        "^([\\w\\-\\.]+)$": {
          "$ref": "#/$defs/namespace"
        }
      }
    },
    "projects": {
      "description": "Workshop customer projects.",
      "type": "object",
      "additionalProperties": false,
      "patternProperties": {
        "^([\\w\\-\\.]+)$": {
          "$ref": "#/$defs/project"
        }
      }
    },
    "subgroups": {
      "description": "Workshop customer sub-groups.",
      "type": "object",
      "additionalProperties": false,
      "patternProperties": {
        "^([\\w\\-\\.]+)$": {
          "$ref": "#/$defs/subgroup"
        }
      }
    },
    "teams": {
      "description": "Team groups to provision. These groups are used specifically for role management.",
      "type": "object",
      "additionalProperties": false,
      "patternProperties": {
        "^([\\w\\-\\.]+)$": {
          "$ref": "#/$defs/team"
        }
      }
    },
    "users": {
      "description": "Workshop users to provision. This key is invalid in customer config files.",
      "type": "object",
      "additionalProperties": false,
      "patternProperties": {
        "^([a-z0-9_\\-\\.\\+]+@[a-z0-9\\-.]+\\.gov)$": {
          "$ref": "#/$defs/user"
        }
      }
    }
  },
  "$defs": {
    "namespace": {
      "description": "Namespace (top level group) - The key can use any sequence of letters, numbers, underscores, hypens, and dots",
      "type": "object",
      "properties": {
        "name": {
          "description": "Friendly name for the group",
          "type": "string"
        },
        "description": {
          "description": "Friendly description for the group",
          "type": "string"
        },
        "path": {
          "description": "Path (slug) for group - Defaults to the group key name",
          "type": "string",
          "pattern": "^([\\w\\-\\.]+)$"
        },
        "config_project": {
          "description": "Optional overrides for the related customer configuration project",
          "type": "object",
          "properties": {
            "approvals_required": {
              "description": "Number of approvals needed for a MR to the config project",
              "type": "number",
              "minimum": 0
            },
            "merge_method": {
              "description": "Default merge method",
              "type": "string",
              "enum": [
                "merge",
                "rebase_merge",
                "ff"
              ]
            },
            "require_owner_approval": {
              "description": "Require at least one approval from a namespace owner before merging",
              "type": "boolean"
            },
            "squash_option": {
              "description": "Squash commits on merge request merge",
              "type": "string",
              "enum": [
                "always",
                "default_off",
                "default_on",
                "never"
              ]
            }
          },
          "additionalProperties": false
        },
        "custom_attributes": {
          "description": "[Optional] Key/value pairs to set as custom attributes (Requires admin permission)",
          "type": "object",
          "patternProperties": {
            "^([\\w\\-]+)$": {
              "description": "Value for the given custom_attribute",
              "type": "string"
            }
          }
        },
        "dr_group": {
          "description": "Group is required to bootstrap Workshop",
          "type": "boolean"
        },
        "visibility": {
          "description": "Namespace visibility - private (members only), public (including anonymous), or internal (visible to other Workshop users)",
          "type": "string",
          "enum": [
            "internal",
            "private",
            "public"
          ]
        },
        "use_custom_template": {
          "description": "Use a custom project template",
          "type": "boolean"
        },
        "owners": {
          "description": "List of owners for the group",
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^([a-z0-9_\\-\\.\\+]+@[a-z0-9\\-.]+\\.gov)$"
          },
          "minItems": 1,
          "uniqueItems": true
        },
        "wiki_access_level": {
          "description": "Whether the namespace group wiki is enabled, disabled, or private",
          "type": "string",
          "enum": [
            "disabled",
            "enabled",
            "private"
          ]
        },
        "runner": {
          "description": "Runner pool configuration",
          "type": "object",
          "properties": {
            "allow_ssh": {
              "description": "Allow SSH access to manager and egress spaces. Defaults to false",
              "type": "boolean"
            },
            "cg_emails": {
              "description": "List of Cloug.gov operators allowed to interact with the group runner spaces",
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^([a-z0-9_\\-\\.\\+]+@gsa\\.gov)$"
              },
              "minItems": 1,
              "uniqueItems": true
            },
            "concurrency": {
              "description": "Maximum concurrent jobs to run per-worker manager",
              "type": "integer",
              "exclusiveMinimum": 0,
              "exclusiveMaximum": 100
            },
            "docker_hub_user": {
              "description": "Docker Hub username for runner workers to pull images",
              "type": "string"
            },
            "docker_hub_token_env_var": {
              "description": "Name of the environment variable holding the token for the Docker Hub user",
              "type": "string"
            },
            "egress_https_mode": {
              "description": "Egress HTTPS proxy mode for runner workers and services",
              "type": "string",
              "enum": [
                "http",
                "https",
                "both"
              ]
            },
            "grant_workers_developer_role": {
              "description": "Allow runner workers to SSH to runner services",
              "type": "boolean"
            },
            "instances": {
              "description": "Number of worker managers to run",
              "type": "integer",
              "exclusiveMinimum": 0,
              "exclusiveMaximum": 2
            },
            "pool_size": {
              "description": "Size of the runner worker pool",
              "type": "string",
              "enum": [
                "small",
                "medium",
                "large",
                "extra_large"
              ]
            },
            "register": {
              "description": "Register the runner pool to the group",
              "type": "boolean"
            },
            "service_egress_ports": {
              "description": "List of TCP ports the egress proxy will allow outbound connection to for job services",
              "type": "array",
              "items": {
                "type": "number",
                "exclusiveMinimum": 1,
                "exclusiveMaxiumum": 65536
              },
              "minItems": 1,
              "uniqueItems": true
            },
            "service_egress_allowlist": {
              "description": "List of additional fully qualified domain names to allow outbound to the Internet by runner job services over HTTPS",
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^([\\w\\-\\.\\*]+)$"
              },
              "minItems": 1,
              "uniqueItems": true
            },
            "service_egress_denylist": {
              "description": "List of fully qualified domain names to block outbound to the Internet by runner job services over HTTPS",
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^([\\w\\-\\.\\*]+)$"
              },
              "minItems": 1,
              "uniqueItems": true
            },
            "technologies": {
              "description": "List of technologies used under the group requiring egress allowance over HTTPS byrunner workers",
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^([a-z0-9_\\-]+)$"
              },
              "minItems": 1,
              "uniqueItems": true
            },
            "worker_allowlist": {
              "description": "List of additional fully qualified domain names to allow outbound to the Internet by runner workers over HTTPS",
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^([\\w\\-\\.\\*]+)$"
              },
              "minItems": 1,
              "uniqueItems": true
            },
            "worker_denylist": {
              "description": "List of fully qualified domain names to block outbound to the Internet by runner workers over HTTPS",
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^([\\w\\-\\.\\*]+)$"
              },
              "minItems": 1,
              "uniqueItems": true
            },
            "worker_egress_ports": {
              "description": "List of TCP ports the egress proxy will allow outbound connection to for runner workers",
              "type": "array",
              "items": {
                "type": "number",
                "exclusiveMinimum": 1,
                "exclusiveMaxiumum": 65536
              },
              "minItems": 1,
              "uniqueItems": true
            },
            "unsafe_egress": {
              "description": "Allow unfettered outbound Internet access [DANGER!]",
              "type": "boolean"
            }
          },
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },
    "project": {
      "description": "Project - The key can use any sequence of letters, numbers, underscores, hypens, and dots",
      "type": "object",
      "properties": {
        "name": {
          "description": "Friendly name for the project",
          "type": "string"
        },
        "description": {
          "description": "Friendly description for the project",
          "type": "string"
        },
        "visibility": {
          "description": "Project visibility - private (members only), public (including anonymous), or internal (visible to other Workshop users)",
          "type": "string",
          "enum": [
            "internal",
            "private",
            "public"
          ]
        },
        "subgroup_key": {
          "description": "Subgroup project is under - Defaults to the namespace",
          "type": "string",
          "pattern": "^([\\w\\-\\.\\/]+)$"
        },
        "archived": {
          "description": "Archive - When true sets repository to read-only state",
          "type": "boolean"
        },
        "approvals_required": {
          "description": "Number of approvals needed for a MR",
          "type": "number",
          "minimum": 0
        },
        "allow_mr_committers_to_approve_merge_requests": {
          "description": "Allow merge request committers to approve their own merge requests. Defaults to false. A merge request committer is a user who has added commits to the merge request's source branch.",
          "type": "boolean"
        },
        "auto_cancel_pending_pipelines": {
          "description": "",
          "type": "boolean"
        },
        "auto_devops_enabled": {
          "description": "",
          "type": "boolean"
        },
        "ci_pipeline_variables_minimum_override_role": {
          "description": "",
          "type": "string",
          "enum": [
            "developer",
            "maintainer",
            "owner"
          ]
        },
        "ci_separated_caches": {
          "description": "",
          "type": "boolean"
        },
        "compliance_frameworks": {
          "description": "List of compliance framework names to apply to the project. Names must exist in the namespace already. See <https://workshop.cloud.gov/help/user/compliance/compliance_center/compliance_frameworks_report/#create-a-new-compliance-framework> for more details.",
          "type": "array",
          "items": {
            "type": "string"
          },
          "minItems": 1,
          "uniqueItems": true
        },
        "container_expiration_policy": {
          "description": "",
          "type": "object",
          "properties": {
            "cadence": {
              "description": "",
              "type": "string"
            },
            "enabled": {
              "description": "",
              "type": "boolean"
            },
            "older_than": {
              "description": "",
              "type": "string"
            }
          }
        },
        "container_registry_access_level": {
          "description": "",
          "type": "string",
          "enum": [
            "disabled",
            "enabled"
          ]
        },
        "default_branch": {
          "description": "",
          "type": "string",
          "pattern": "^([\\w\\-\\.\\/]+)$"
        },
        "import_id": {
          "description": "Existing project ID to import as a new IaC managed resource",
          "type": "number"
        },
        "initialize_with_readme": {
          "description": "",
          "type": "boolean"
        },
        "lfs_enabled": {
          "description": "",
          "type": "boolean"
        },
        "model_registry_access_level": {
          "description": "",
          "type": "string",
          "enum": [
            "disabled",
            "enabled"
          ]
        },
        "namespace": {
          "description": "[DEPRECATING] Namespace path project is under - Defaults to the namespace / subgroup_key",
          "type": "string",
          "pattern": "^([\\w\\-\\.\\/]+)$"
        },
        "allow_merge_on_skipped_pipeline": {
          "description": "Whether to treat skipped pipelines as successful when merging. Defaults to false",
          "type": "boolean"
        },
        "only_allow_merge_if_all_discussions_are_resolved": {
          "description": "Whether merge requests can be merged only after all discussions are resolved. Defaults to true",
          "type": "boolean"
        },
        "only_allow_merge_if_pipeline_succeeds": {
          "description": "Whether merge requests can be merged only if the pipeline succeeds. Defaults to true",
          "type": "boolean"
        },
        "path": {
          "description": "Project path (slug name) - Overrides the default path derived from the key name",
          "type": "string",
          "pattern": "^([\\w\\-\\.]+)$"
        },
        "packages_enabled": {
          "description": "",
          "type": "boolean"
        },
        "protected_tags": {
          "description": "Project tags that are protected",
          "type": "array",
          "items": {
            "description": "List of protected tags for the project",
            "type": "object",
            "properties": {
              "tag": {
                "description": "Wildcard pattern or tag name",
                "type": "string"
              },
              "create_access_level": {
                "description": "Access levels allowed to create. Default is set to maintainer",
                "type": "string",
                "enum": [
                  "no one",
                  "developer",
                  "maintainer"
                ]
              }
            },
            "required": [
              "tag"
            ],
            "additionalProperties": false
          }
        },
        "public_jobs": {
          "description": "",
          "type": "boolean"
        },
        "remove_source_branch_after_merge": {
          "description": "",
          "type": "boolean"
        },
        "resolve_outdated_diff_discussions": {
          "description": "",
          "type": "boolean"
        },
        "group_roles": {
          "description": "Additional groups outside of the inheritence structure to share the project with, giving the group's members access to the project - Supports default roles docs.gitlab.com/user/permissions/#default-roles with a key of lower cased plural role name (e.g. The key developers will share the project with the group and give members the developer role)",
          "type": "object",
          "additionalProperties": false,
          "patternProperties": {
            "^\\w+s$": {
              "description": "List of group paths to assign as role project members, e.g. 'subgroup/roles/developers'",
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^([\\w\\-\\.\\/]+)$"
              }
            }
          }
        },
        "shared_runners_enabled": {
          "description": "",
          "type": "boolean"
        },
        "snippets_enabled": {
          "description": "Whether project code snippets are enabled. Deprecated in favor of `snippets_access_level`",
          "deprecated": true,
          "type": "boolean"
        },
        "snippets_access_level": {
          "description": "Whether project code snippets are enabled, disabled, or private.",
          "type": "string",
          "enum": [
            "disabled",
            "enabled",
            "private"
          ]
        },
        "wiki_access_level": {
          "description": "Whether the project wiki is enabled, disabled, or private",
          "type": "string",
          "enum": [
            "disabled",
            "enabled",
            "private"
          ]
        },
        "avatar": {
          "description": "",
          "type": "string"
        },
        "avatar_hash": {
          "description": "",
          "type": "string"
        },
        "forked_from_project_id": {
          "description": "",
          "type": "number"
        },
        "import_url": {
          "description": "https url to import repository from. Use with `mirror: true` to set up a pull mirror to keep the repository up to date.",
          "type": "string"
        },
        "import_url_password_env_var": {
          "description": "Name of the environment variable holding the `import_url_password` value. Used with `import_url_username`.",
          "type": "string"
        },
        "import_url_username": {
          "description": "Username that can access `import_url`. Required for private repositories. Optional for public repositories.",
          "type": "string"
        },
        "mirror": {
          "description": "",
          "type": "boolean"
        },
        "push_rules": {
          "description": "Rules for pushing to the repository",
          "type": "object",
          "properties": {
            "commit_committer_check": {
              "description": "Users can only push commits to this repository that were committed with one of their own verified emails.",
              "type": "boolean"
            },
            "member_check": {
              "description": "Restrict commits by author (email) to existing GitLab users.",
              "type": "boolean"
            },
            "deny_delete_tag": {
              "description": "Do not allow deleting tags with a push",
              "type": "boolean"
            },
            "max_file_size": {
              "description": "",
              "type": "number"
            },
            "prevent_secrets": {
              "description": "Reject any files that are likely to contain secrets",
              "type": "boolean"
            },
            "reject_unsigned_commits": {
              "description": "",
              "type": "boolean"
            },
            "reject_non_dco_commits": {
              "description": "Reject commits that do not have a valid DCO sign-off",
              "type": "boolean"
            },
            "author_email_regex": {
              "description": "",
              "type": "string"
            },
            "branch_name_regex": {
              "description": "",
              "type": "string"
            },
            "commit_message_negative_regex": {
              "description": "",
              "type": "string"
            },
            "commit_message_regex": {
              "description": "",
              "type": "string"
            },
            "file_name_regex": {
              "description": "",
              "type": "string"
            }
          },
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },
    "subgroup": {
      "description": "Subgroup (any group other than a top level namespace) - The key can use any sequence of letters, numbers, underscores, hypens, and dots",
      "type": "object",
      "required": [
        "name",
        "visibility"
      ],
      "properties": {
        "name": {
          "description": "Friendly name for the group",
          "type": "string"
        },
        "path": {
          "description": "Path (slug) for group - Defaults to the group key name",
          "type": "string",
          "pattern": "^([\\w\\-\\.]+)$"
        },
        "parent_path": {
          "description": "Parent group full path for the subgroup. Defaults to the top level namespace",
          "type": "string",
          "pattern": "^([\\w\\-\\.\\/]+)$"
        },
        "description": {
          "description": "Friendly description of the group",
          "type": "string"
        },
        "import_id": {
          "description": "Existing subgroup ID to import as a new IaC managed resource",
          "type": "number"
        },
        "members": {
          "description": "Roles and their memberships under the subgroup - Supports both default roles docs.gitlab.com/user/permissions/#default-roles and custom roles <https://docs.gitlab.com/user/custom_roles/> with a key of lower cased plural role name (e.g. The key developers will create a group Developers that give members the developer role)",
          "type": "object",
          "patternProperties": {
            "^[\\w\\-\\.\\/]+$": {
              "description": "List of users to assign as role group members",
              "type": "array"
            }
          }
        },
        "group_roles": {
          "description": "Additional groups outside of the inheritance structure to grant a role on the subgroup. This gives the group's members access to the subgroup and all of its projects. Supports default roles <https://docs.gitlab.com/user/permissions/#default-roles> by matching the key to lowercased and pluralized role names (e.g. The key 'developers' will share the project with the group and give members the `developer` role)",
          "type": "object",
          "additionalProperties": false,
          "patternProperties": {
            "^\\w+s$": {
              "description": "List of group paths to assign as role project members, e.g. 'teams/platform-developers'",
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^([\\w\\-\\.\\/]+)$"
              }
            }
          }
        },
        "visibility": {
          "description": "Subgroup visibility - private (members only), public (including anonymous), or internal (visible to other Workshop users)",
          "type": "string",
          "enum": [
            "internal",
            "private",
            "public"
          ]
        },
        "wiki_access_level": {
          "description": "Whether the group wiki is enabled, disabled, or private",
          "type": "string",
          "enum": [
            "disabled",
            "enabled",
            "private"
          ]
        }
      },
      "additionalProperties": false
    },
    "team": {
      "description": "Team group - used to create groups for easy @mentions and group-based assignments — not necessarily for role-based access control. The key can use any sequence of letters, numbers, underscores, hyphens, and dots.",
      "type": "object",
      "required": [
        "name",
        "members"
      ],
      "properties": {
        "name": {
          "description": "Friendly name for the team",
          "type": "string"
        },
        "description": {
          "description": "Friendly description of the team",
          "type": "string"
        },
        "namespace_role": {
          "description": "Role to assign the team on the full namespace. Defaults to none (no access)",
          "type": "string"
        },
        "managed_projects_role": {
          "description": "Role to assign the team for Workshop's configuration projects — i.e., the configuration project this schema applies to, the templates project, and other Workshop-generated config projects. Defaults to `developer`, set to `none` to remove access",
          "type": "string"
        },
        "members": {
          "description": "List of users to assign as team members.",
          "type": "array",
          "items": {
            "description": "Each item is a Workshop username",
            "type": "string",
            "pattern": "^([a-z0-9_\\-\\.\\+]+)$"
          },
          "minItems": 1,
          "uniqueItems": true
        }
      },
      "additionalProperties": false
    },
    "user": {
      "description": "User object - The key must be the user's US government email address under a .gov domain",
      "type": "object",
      "properties": {
        "custom_attributes": {
          "description": "[Optional] Key/value pairs to set as custom attributes (Requires admin permission)",
          "type": "object",
          "patternProperties": {
            "^([\\w\\-]+)$": {
              "description": "Value for the given custom_attribute",
              "type": "string"
            }
          }
        },
        "import_id": {
          "description": "[Optional] Existing GitLab user ID on the system to import",
          "type": "integer"
        },
        "name": {
          "description": "Full name of user",
          "type": "string",
          "pattern": "^([\\w'\\- ]+)$"
        },
        "note": {
          "description": "[Optional] Additional notes about the user",
          "type": "string"
        },
        "projects_limit": {
          "description": "[Optional] Personal project limit",
          "type": "integer"
        },
        "state": {
          "description": "[Optional] Forced state of the user account",
          "type": "string",
          "enum": [
            "active",
            "blocked",
            "deactivated"
          ]
        },
        "username": {
          "description": "[Optional] Custom username - Overrides the default username from the user email address",
          "type": "string",
          "pattern": "^([a-zA-Z0-9][\\w\\-\\.]{1,254})$"
        }
      },
      "required": [
        "name"
      ],
      "additionalProperties": false
    }
  },
  "additionalProperties": false
}
