Content last updated 2026-01-13

Project setup

Documentation on project setup

This page guides you through the complete process of creating and configuring new GitLab projects for Customer Support Operations.

Process

The process consists of 7 steps:

  1. Create the project
  2. Adjust project settings
  3. Invite the correct groups
  4. Add initial files
  5. Setup CI/CD variables
  6. Configure additional requirements (if needed)
  7. Document the project

Step 1 - Create the project

The starting place to create the project itself. When doing so, you need to consider the following:

  • The location of the project
  • The name of the project
  • The slug of the project

Location of the project

To create a project, you need to know where the project will be. When possible, we group similar projects together. Using this, you can often find an existing subgroup for the project to be in.

As an example, if making a sync repo for Zendesk Global, it will be within the subgroup gitlab-support-readiness/zendesk-global because:

  • It is a Customer Support Operations non-public project (thus within our private namespace gitlab-support-readiness)
  • It relates to Zendesk Global (thus within the subgroup zendesk-global)

If the sync repo being created was relating to ticket attributes or metadata (such as ticket forms, ticket fields, etc.), you would then create it in the gitlab-support-readiness/zendesk-global/tickets subgroup.

If you do not see a subgroup for the project to exist within that already exists, speak to the team concerning this to determine the best place for the project to be created within.

Name of the project

We should strive for concise and self-explainable naming when creating projects. This should utilize the location in that effort.

So if making a project that handles processing of tickets for Zendesk US Government, we know from determining the location that it will be within the subgroup gitlab-support-readiness/zendesk-us-government/tickets. Knowing that, we can simply name the project Processor, since the location pathing will already dictate it is relating to Zendesk US Government tickets.

If you are ever unsure of what to name a project, speak to the team concerning this to determine the best name to use.

Slug of the project

Much like when determining the name, the slug should, with the pathing, be concise and self-explainable. Most often, the slug generated by GitLab will work for this purpose.

If you are ever unsure of what the slug for a project should be, speak to the team concerning this to determine the best slug to use.

Step 2 - Adjust the settings

With the project created, we need to manually adjust the settings to the following:

General
  • Visibility, project features, permissions
    • Project visibility: Private
    • Additional options
      • Require authentication to view media files
    • Issues: disabled
    • Repository: enabled
      • Merge requests: enabled
      • Forks: disabled
      • Git Large File Storage (LFS): disabled
      • CI/CD: enabled
    • Container registry: disabled
    • Analytics: disabled
    • Requirements: disabled
    • Security and Compliance: disabled
    • Wiki: disabled
    • Snippets: enabled
    • Package registry: disabled
    • Model experiments: disabled
    • Model registry: disabled
    • Pages: disabled
    • Monitor: disabled
    • Environments: disabled
    • Feature flags: disabled
    • Infrastructure: disabled
    • Releases: disabled
    • Email notifications
      • Enable email notifications
        • Include diff previews
      • Show default emoji reactions
      • Warn about Potentially Unwanted Characters
    • CI/CD Catalog project: disabled
  • Badges
    • None
  • GitLab Duo
    • GitLab Duo: enabled
    • Allow flow execution: enabled
  • Service Desk
    • Disabled
Integrations
  • None
Webhooks
  • None
Access tokens
  • None
Repository
  • Branch defaults
    • Default branch: master
    • Auto-close references issues on default branch
  • Branch rules
    • All branches
      • Squash commits: Require
    • master
      • Requires CODEOWNERS approval
      • Allowed to merge: Developers+Maintainers
      • Allowed to push and merge: 1 user
        • NOTE: The 1 user here is the gl-support-bot user
  • Push rules
    • Reject unverified users
    • Reject inconsistent user name
    • Reject unsigned commits
    • Reject commits that aren’t DCO certified
    • Do not allow users to remove Git tags with git push
    • Check whether the commit author is a GitLab user
    • Prevent pushing secret files
    • Require expression in commit messages: blank
    • Reject expression in commit messages: blank
    • Branch name: blank
    • Commit author’s email: blank
    • Prohibited file names: blank
    • Maximum file size (MB): 0
  • Mirroring repositories
    • None
  • Protected branches
    • master
      • Allowed to merge: Maintainers
      • Allowed to push and merge: GitLab Support Bot
      • Allowed to force push: disabled
      • Code owner approval: enabled
  • Protected tags
    • None
  • Deploy tokens
    • None
  • Deploy keys
    • None
Merge Requests
  • Merge requests
    • Merge method: Merge commit

    • Merge options

      • Enable merged results pipelines
        • NOTE: In rare situations this may be enabled, but never by default
      • Automatically resolve merge request diff threads when they become outdated
      • Show link to create or view a merge request when pushing from the command line
      • Enable “Delete source branch” option by default
    • Squash commits when merging: Require

    • Merge checks

      • Pipelines must succeed
        • NOTE: This will vary based on project template type.
      • All threads must be resolved
      • Status checks must succeed
    • Status checks

      • None
    • Merge suggestions: blank

    • Merge commit message template

      Merge branch '%{source_branch}' into '%{target_branch}'
      
      %{title}
      
      %{issues}
      
      See merge request %{reference}
      
    • Squash commit message template

      %{title}
      
    • Default description template for merge requests: blank

  • Merge request approvals
    • Coverage-Check: disabled
    • Minimum required approvals: 0
    • Approval settings
      • Prevent approval by author
      • Prevent approvals by users who add commits
      • Prevent editing approval rules in merge requests
      • Require user re-authentication (password or SAML) to approve
    • When a commit is added: Removal all approvals
  • Merge request branch workflow
    • None
CI/CD
  • General pipelines
    • Project-based pipeline visibility
    • Auto-cancel redundant pipelines
    • Prevent outdated deployment jobs
      • Allow job retries even if the deployment job is outdated.
    • Use separate caches for protected branches
    • Minimum role required to cancel a pipeline or job: Developer
    • CI/CD configuration file: blank
    • Git strategy: git fetch
    • Git shallow clone: 20
    • Timeout: 15m
      • NOTE: You might need to adjust this depending on what your project is doing exactly
    • Automatic pipeline cleanup: 5d
  • Auto DevOps
    • Default to Auto DevOps pipeline
  • Protected environments
    • None
  • Pipeline trigger tokens
    • None
  • Automatic deployment rollbacks
    • Enable automatic rollbacks
  • Deploy freezes
    • None
  • Job token permissions
    • Authorized groups and projects: Only this project and any groups and projects in the allowlist
    • CI/CD job token allowlist
      • Only the project itself should be present in the list
    • Limit access from this project (Deprecated): disabled
    • Add an existing project to the scope
      • Only the project itself should be present in the list
  • Secure files
    • None
  • Pipeline subscriptions
    • Subscriptions
      • None
    • Subscribed to this project
      • None
Monitor
  • Error tracking
    • Enable error tracking
      • Active
    • Error tracking backend: GitLab
  • Alerts
    • None
  • Incidents
    • Active: disabled
  • Status page
    • Active
    • Status page URL: blank
    • S3 Bucket name: blank
    • AWS region: blank
    • AWS access key ID: blank
    • AWS Secret access key: blank

Step 3 - Invite the correct groups

After adjusting the settings for your new project, you need to invite the correct groups as members to it. Due to our inheritance setup, project access is handled automatically through group membership. So this is really about what the CODEOWNERS file will use (for merge request approvals).

If you are ever unsure of what group(s) to invite, speak to the team for assistance.

Step 4 - Add your files

In this stage, you are going to make the initial commit to the project to add the files. The exact files you will need to add are going to vary depending on what the project is for (and what it does). Please review the below guidelines for various files to ensure you align with how we setup projects:

  • .gitlab/CODEOWNERS

    • The exact contents of this are going to vary depending on the code you use, but the starting place for this should always be:
    [Customer Support Operations]
    * @GROUP_TO_INVITE
    
    • See Step 3 for more information for the value of GROUP_TO_INVITE
  • bin/post_in_slack.sh

    • This file should always have executable permissions
    • The contents for this file should always be:
    curl -ss $SLACK_URL_PIPELINE_FAIL \
      -X POST \
      -H 'Content-type: application/json' \
      --data "{\"text\": \"<$CI_PROJECT_URL|$CI_PROJECT_PATH> pipeline <$CI_PIPELINE_URL|#$CI_PIPELINE_ID> failed $DATE\", \"mrkdwn\": true }"
    
  • .gitlab-ci.yml

    • The exact contents of this are going to vary depending on the code you use, but you can use the following start templates for common project types:

      Sync repos
      image: "registry.gitlab.com/gitlab-support-readiness/images/ruby:3.2.2"
      
      stages:
      - compare
      - sync
      - notify
      
      before_script:
      - ruby -v
      - gem install bundler
      - bundle
      
      compare:
        stage: compare
        script:
        - ./bin/compare
        rules:
        - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
        retry:
          max: 2
          when:
          - runner_system_failure
          - stuck_or_timeout_failure
      
      sync:
        stage: sync
        script:
        - ./bin/sync
        rules:
        - if: $CI_PIPELINE_SOURCE == 'trigger'
        retry:
          max: 2
          when:
          - runner_system_failure
          - stuck_or_timeout_failure
      
      post_in_slack_on_failure:
        before_script: ''
        stage: notify
        when: on_failure
        image: registry.gitlab.com/gitlab-support-readiness/images/curl:latest
        script:
        - ./bin/post_in_slack.sh
      
      Zendesk apps
      image: "registry.gitlab.com/gitlab-support-readiness/images/ruby:3.2.2"
      
      stages:
      - compare
      - sync
      - notify
      
      before_script:
      - ruby -v
      - gem install bundler
      - bundle
      
      compare_instances:
        stage: compare
        script:
        - ./bin/compare_sandbox
        - ./bin/compare_production
        rules:
        - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
        retry:
          max: 2
          when:
          - runner_system_failure
          - stuck_or_timeout_failure
      
      force_sandbox_sync:
        stage: sync
        script:
        - ./bin/sync_sandbox force
        needs: ['compare_instances']
        when: manual
        rules:
        - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
        retry:
          max: 2
          when:
          - runner_system_failure
          - stuck_or_timeout_failure
      
      perform_sandbox_sync:
        stage: sync
        script:
        - ./bin/sync_sandbox
        rules:
        - if: $CI_PIPELINE_SOURCE == 'schedule' && $SANDBOX == 'true'
        retry:
          max: 2
          when:
          - runner_system_failure
          - stuck_or_timeout_failure
      
      perform_production_sync:
        stage: sync
        script:
        - ./bin/sync_production
        rules:
        - if: $CI_PIPELINE_SOURCE == 'schedule' && $SANDBOX == 'false'
        retry:
          max: 2
          when:
          - runner_system_failure
          - stuck_or_timeout_failure
      
      post_in_slack_on_failure:
        before_script: ''
        stage: notify
        when: on_failure
        image: registry.gitlab.com/gitlab-support-readiness/images/curl:latest
        script:
        - ./bin/post_in_slack.sh
      
    • If you are unsure of what to have here, reach out to the rest of the team for assistance

  • .rubocop.yml

    • The contents of this file should always align with the current ruby version we are working with. That means it should be:
    AllCops:
      NewCops: enable
      TargetRubyVersion: 3.2.2
    
  • .ruby-version

    • The contents of this file should always be the current ruby version we are working with. That means it should be:
    3.2.2
    
  • Gemfile

    • The list of gems should be in alphabetic order
    • The exact gems required are going to vary depending on the code you use, but the starting place for this should always be:
    # frozen_string_literal: true
    
    source 'https://rubygems.org'
    
  • README.md

    • The exact content of this is going to vary, but you should ensure you have the following documented in your README.md file:
      • Name of project
      • Brief description
      • Filetree layout
      • Requirements
      • CI/CD pipeline information
    • If you are unsure of what to have here, reach out to the rest of the team for assistance

Step 5 - Setup CI/CD variables

Configure the CI/CD variables required for your project. Many variables are inherited from the parent namespace, so focus on project-specific values:

Common variables to add:

  • API token values (e.g. ZD_TOKEN, GL_TOKEN, CALENDLY_API_TOKEN, etc.)
  • Slack webhooks (except SLACK_URL_PIPELINE_FAIL which is inherited)

To check inherited variables:

  1. Navigate to Settings > CI/CD > Variables in your project
  2. Review the “Inherited from group” section to see what’s already available
  3. Add only the variables not already provided by the parent namespace

Variable protection:

  • Mark sensitive tokens as “Masked”

Step 6 - Additional configuration (if needed)

For most projects, Steps 1-5 are sufficient. However, some projects may require additional configuration such as:

If you’re unsure whether additional configuration is needed, consult with the team.

Step 7 - Document it

The final step in your project creation is to update our handbook pages with information about the project. This should include:

  • What it is
  • What it does
  • How it works
  • The type of deployment strategy it uses
  • How to make changes
  • Any troubleshooting information

After Project Creation

Once your project is created and documented:

  • The project will be accessible to the appropriate team members based on group membership
  • CI/CD pipelines will run according to the configured rules
  • Changes will require merge requests with CODEOWNERS approval
  • Monitor the project’s initial pipelines to ensure everything is configured correctly
Last modified January 21, 2026: Revamp CustSuppOps handbook (7d49549f)