Skip to main content

Connect GitHub CODEOWNERS with Service, Team & User

This guide shows you how to map CODEOWNERS file patterns in GitHub repositories (Service) to their respective Service, Team and User in port.

Prerequisites

This guide assumes:

  • You have a Port account
  • You have installed Port's GitHub app in your organisation or in repositories you are interested in.

GitHub configuration

To ingest GitHub objects, use one of the following methods:

To manage your GitHub integration configuration using Port:

  1. Go to the data sources page of your portal.
  2. Under Exporters, click on your desired GitHub organization.
  3. A window will open containing the default YAML configuration of your GitHub integration.
  4. Here you can modify the configuration to suit your needs, by adding/removing entries.
  5. When finished, click resync to apply any changes.

Using this method applies the configuration to all repositories that the GitHub app has permissions to.

When configuring the integration using Port, the YAML configuration is global, allowing you to specify mappings for multiple Port blueprints.

Important

When using Port's UI, the specified configuration will override any port-app-config.yml file in your GitHub repository/ies.

Setting up the blueprint and mapping configuration

Blueprints creation

If you already have the githubUser, githubTeam and service blueprints created, you do not need to recreate them. Ensure to adjust the relations' targets as necessary.

GitHub User Blueprint (Click to expand)
{
"identifier": "githubUser",
"title": "Github User",
"icon": "Microservice",
"schema": {
"properties": {},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"aggregationProperties": {},
"relations": {
"user": {
"title": "User",
"target": "user",
"required": false,
"many": false
}
}
}
GitHub User mapping configuration (Click to expand)
resources:
- kind: user
selector:
query: "true"
port:
entity:
mappings:
identifier: .login
title: .login
blueprint: '"githubUser"'
relations:
GitHub Team Blueprint (Click to expand)
{
"identifier": "githubTeam",
"title": "GitHub Team",
"icon": "Github",
"schema": {
"properties": {
"slug": {
"title": "Slug",
"type": "string"
},
"description": {
"title": "Description",
"type": "string"
},
"link": {
"title": "Link",
"icon": "Link",
"type": "string",
"format": "url"
},
"permission": {
"title": "Permission",
"type": "string"
},
"notification_setting": {
"title": "Notification Setting",
"type": "string"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}
GitHub Team mapping configuration (Click to expand)
resources:
- kind: team
selector:
query: "true" # JQ boolean query. If evaluated to false - skip syncing the object.
port:
entity:
mappings:
identifier: ".id | tostring"
title: .name
blueprint: '"githubTeam"'
properties:
name: .name
slug: .slug
description: .description
link: .html_url
permission: .permission
notification_setting: .notification_setting
Service (GitHub Repository) Blueprint (Click to expand)
{
"identifier": "service",
"title": "Service",
"icon": "Microservice",
"schema": {
"properties": {
"readme": {
"title": "README",
"type": "string",
"format": "markdown"
},
"url": {
"title": "Service URL",
"type": "string",
"format": "url"
},
"defaultBranch": {
"title": "Default branch",
"type": "string"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}
GitHub Service (Repository) mapping configuration (Click to expand)
resources:
- kind: repository
selector:
query: "true" # JQ boolean query. If evaluated to false - skip syncing the object.
port:
entity:
mappings:
identifier: ".name" # The Entity identifier will be the repository name.
title: ".name"
blueprint: '"service"'
properties:
readme: file://README.md # fetching the README.md file that is within the root folder of the repository and ingesting its contents as a markdown property
url: .html_url
defaultBranch: .default_branch
CODEOWNERS blueprint (Click to expand)
{
"identifier": "githubCodeowners",
"description": "This blueprint represents a CODEOWNERS file in a service",
"title": "Github Codeowners",
"icon": "Github",
"schema": {
"properties": {
"location": {
"type": "string",
"title": "File location",
"description": "File path to CODEOWNERS file"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"aggregationProperties": {},
"relations": {
"service": {
"title": "Service",
"target": "service",
"required": false,
"many": false
}
}
}
CODEOWNERS Pattern mapping configuration (Click to expand)
resources:
- kind: file
selector:
query: .repo.archived == false
files:
- path: '**/.github/CODEOWNERS'
port:
itemsToParse: >-
[. as $root | .file.content | split("\n\n") | map( if (startswith("# ")
| not) then { component: $root.repo.name, patterns: [], teams: [. |
split(" ")[] | select(startswith("@")) | rtrimstr("\n") |
ltrimstr("\n")] } else split("\n") as $lines | { component: ($lines[0] |
ltrimstr("# ") | ltrimstr(" ")), patterns: ($lines[1:] | map(split("
")[0])), teams: [($lines[1:] | map(split(" ")[1:] | join(" ")) | [.[] |
split(" ") | .[]] | unique)[] | select(startswith("@"))] } end
)[]]
entity:
mappings:
identifier: .item.component | gsub(" "; "_") | gsub("&"; "and") | gsub("-"; "")
title: .item.component
blueprint: '"githubCodeowners"'
properties:
codeowners_file_patterns: .item.patterns
relations:
service: .repo.name
owning_teams:
combinator: '''and'''
rules:
- property: '''github_team'''
value: .item.teams
operator: '"in"'
CODEOWNERS Pattern blueprint (Click to expand)
{
"identifier": "githubCodeownersPattern",
"description": "This blueprint represents a pattern in a CODEOWNERS file from a service",
"title": "Github Codeowners Pattern",
"icon": "Github",
"schema": {
"properties": {
"pattern": {
"type": "string",
"title": "File & Folder pattern",
"description": "Regex pattern depicting the folder or file the teams and users have access to"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"aggregationProperties": {},
"relations": {
"service": {
"title": "Service",
"target": "githubRepository",
"required": false,
"many": false
},
"user": {
"title": "Users",
"target": "githubUser",
"required": false,
"many": true
},
"codeownersFile": {
"title": "Codeowners File",
"target": "githubCodeowners",
"required": true,
"many": false
},
"team": {
"title": "Teams",
"target": "githubTeam",
"required": false,
"many": true
}
}
}

Add content to your CODEOWNERS file, then click on Resync and wait for the entities to be ingested in your Port environment.