-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #371 from guardian/ts/best-practices
feat: add best practices markdown and generator script
- Loading branch information
Showing
8 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Best Practice Generator | ||
This is a small TypeScript script to generate the [best practices markdown file](best-practices.md). | ||
|
||
## Adding a new best practice | ||
To add a new best practice, follow these steps: | ||
1. Update [definitions.ts](src/definitions.ts). Before adding a best practice, consider: | ||
- How one can identify if it's being followed (e.g. a dashboard, or a command to run) | ||
- How to exempt from it. | ||
2. Generate the markdown file via `npm -w best-practices run generate` (this will also type-check, and format your changes). | ||
3. Raise a PR with your changes. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
|
||
|
||
# Best Practices | ||
|
||
|
||
This file is auto-generated from `definitions.ts`. Do not edit this file directly, but instead edit `definitions.ts` and then run `npm -w best-practices run generate`. | ||
|
||
This document defines a list of best practices we have defined. | ||
|
||
An Owner is someone/a team that is responsible for tracking compliance of the best practice. They can also be approached for guidance on how to adhere. Typically this will be a DevX team. | ||
|
||
<!-- contentstart --> | ||
## Repository | ||
| ID | Name | Owner | Description | How to check compliance | How to exempt | | ||
| ------------- | ------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | ||
| REPOSITORY-01 | Default Branch Name | [@guardian](https://github.com/orgs/guardian/teams/all) | The default branch name should be `main`.<br>See the [master-to-main tool](https://github.com/guardian/master-to-main/blob/main/migrating.md). | [Grafana](https://metrics.gutools.co.uk/d/2uaV8PiIz/repocop?orgId=1) | Archived repositories are exempt. | | ||
| REPOSITORY-02 | Branch Protection | [@guardian](https://github.com/orgs/guardian/teams/all) | Enable branch protection for the default branch, ensuring changes are reviewed before being deployed. | [Grafana](https://metrics.gutools.co.uk/d/2uaV8PiIz/repocop?orgId=1) | Archived repositories are exempt. | | ||
| REPOSITORY-03 | Team-based Access | [@guardian](https://github.com/orgs/guardian/teams/all) | Grant access on a team basis, rather than directly to individuals. | Manual. View the repository on https://github.com | Repositories with one of following topics are exempt: `hackday`, `learning`, `prototype`. | | ||
| REPOSITORY-04 | Admin Access | [@guardian](https://github.com/orgs/guardian/teams/all) | Grant at least one GitHub team Admin access - typically, the dev team that own the project. | [Grafana](https://metrics.gutools.co.uk/d/2uaV8PiIz/repocop?orgId=1) | Repositories with one of following topics are exempt: `hackday`, `learning`, `prototype`. Archived repositories are exempt. | | ||
| REPOSITORY-05 | Archiving | [DevX Operations](https://github.com/orgs/guardian/teams/devx-operations) | Repositories that are no longer used should be archived. | Manual. View the repository on https://github.com | N/A | | ||
| REPOSITORY-06 | Topics | [DevX Security](https://github.com/orgs/guardian/teams/devx-security) | Repositories should have one of the following topics, to help understand what is in production. `production`, `testing`, `documentation`, `hackday`, `prototype`, `learning`, `interactive` | [Grafana](https://metrics.gutools.co.uk/d/2uaV8PiIz/repocop?orgId=1) | Archived repositories are exempt. | | ||
| REPOSITORY-07 | Contents | [DevX Security](https://github.com/orgs/guardian/teams/devx-security) | Never commit secret information. Avoid private information in public repositories. | Manual. View the repository on https://github.com | N/A | | ||
## AWS | ||
| ID | Name | Owner | Description | How to check compliance | How to exempt | | ||
| ------ | ---------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------- | | ||
| AWS-01 | Resource Tagging | [DevX Operations](https://github.com/orgs/guardian/teams/devx-operations) | AWS resources should be tagged (where supported) with `Stack`, `Stage`, and `App`.<br>This aids service discovery, and cost allocation. | TBD | N/A | | ||
## GalaxiesPerson | ||
| ID | Name | Owner | Description | How to check compliance | How to exempt | | ||
| ----------------- | ---------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------- | | ||
| GALAXIESPERSON-01 | GitHub Usernames | [DevX Operations](https://github.com/orgs/guardian/teams/devx-operations) | Developers should update their [Galaxies profiles](https://forms.gle/7Yye3KfHefgYqg3c7) with their GitHub usernames | View on Galaxies | Your Galaxies role is something other than an engineer/data analyst | | ||
## GalaxiesTeam | ||
| ID | Name | Owner | Description | How to check compliance | How to exempt | | ||
| --------------- | ------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | ||
| GALAXIESTEAM-01 | Github Team | [DevX Operations](https://github.com/orgs/guardian/teams/devx-operations) | Teams should have their GitHub team names in their galaxies entry | Check in [this file](https://github.com/guardian/galaxies/blob/main/shared/data/teams.ts) in the Galaxies repo | Teams that don't use GitHub are exempt | | ||
| GALAXIESTEAM-02 | Team Emails | [DevX Operations](https://github.com/orgs/guardian/teams/devx-operations) | A team on Galaxies should have an email address entry | Check in [this file](https://github.com/guardian/galaxies/blob/main/shared/data/teams.ts) in the Galaxies repo | N/A | | ||
| GALAXIESTEAM-03 | Team Channels | [DevX Operations](https://github.com/orgs/guardian/teams/devx-operations) | A team on Galaxies should have a public chat channel key listed | Check in [this file](https://github.com/guardian/galaxies/blob/main/shared/data/teams.ts) in the Galaxies repo | It's generally good practice to do this, but teams that don't use GitHub are exempt | | ||
<!-- contentend --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"name": "best-practices", | ||
"version": "1.0.0", | ||
"description": "A markdown file of best practices generated by a script from a definitions file", | ||
"type": "module", | ||
"scripts": { | ||
"generate": "ts-node src/index.ts" | ||
}, | ||
"dependencies": { | ||
"markdown-table": "^3.0.3" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import type { IAllBestPractice, IBestPractice } from './types.ts'; | ||
|
||
const repository: readonly IBestPractice[] = [ | ||
{ | ||
name: 'Default Branch Name', | ||
owner: '[@guardian](https://github.com/orgs/guardian/teams/all)', | ||
description: | ||
'The default branch name should be `main`.<br>See the [master-to-main tool](https://github.com/guardian/master-to-main/blob/main/migrating.md).', | ||
howToCheck: | ||
'[Grafana](https://metrics.gutools.co.uk/d/2uaV8PiIz/repocop?orgId=1)', | ||
howToExempt: 'Archived repositories are exempt.', | ||
}, | ||
{ | ||
name: 'Branch Protection', | ||
owner: '[@guardian](https://github.com/orgs/guardian/teams/all)', | ||
description: | ||
'Enable branch protection for the default branch, ensuring changes are reviewed before being deployed.', | ||
howToCheck: | ||
'[Grafana](https://metrics.gutools.co.uk/d/2uaV8PiIz/repocop?orgId=1)', | ||
howToExempt: 'Archived repositories are exempt.', | ||
}, | ||
{ | ||
name: 'Team-based Access', | ||
owner: '[@guardian](https://github.com/orgs/guardian/teams/all)', | ||
description: | ||
'Grant access on a team basis, rather than directly to individuals.', | ||
howToCheck: 'Manual. View the repository on https://github.com', | ||
howToExempt: | ||
'Repositories with one of following topics are exempt: `hackday`, `learning`, `prototype`.', | ||
}, | ||
{ | ||
name: 'Admin Access', | ||
owner: '[@guardian](https://github.com/orgs/guardian/teams/all)', | ||
description: | ||
'Grant at least one GitHub team Admin access - typically, the dev team that own the project.', | ||
howToCheck: | ||
'[Grafana](https://metrics.gutools.co.uk/d/2uaV8PiIz/repocop?orgId=1)', | ||
howToExempt: | ||
'Repositories with one of following topics are exempt: `hackday`, `learning`, `prototype`. Archived repositories are exempt.', | ||
}, | ||
{ | ||
name: 'Archiving', | ||
owner: | ||
'[DevX Operations](https://github.com/orgs/guardian/teams/devx-operations)', | ||
description: 'Repositories that are no longer used should be archived.', | ||
howToCheck: 'Manual. View the repository on https://github.com', | ||
howToExempt: 'N/A', | ||
}, | ||
{ | ||
name: 'Topics', | ||
owner: | ||
'[DevX Security](https://github.com/orgs/guardian/teams/devx-security)', | ||
description: | ||
'Repositories should have one of the following topics, to help understand what is in production. `production`, `testing`, `documentation`, `hackday`, `prototype`, `learning`, `interactive`', | ||
howToCheck: | ||
'[Grafana](https://metrics.gutools.co.uk/d/2uaV8PiIz/repocop?orgId=1)', | ||
howToExempt: 'Archived repositories are exempt.', | ||
}, | ||
{ | ||
name: 'Contents', | ||
owner: | ||
'[DevX Security](https://github.com/orgs/guardian/teams/devx-security)', | ||
description: | ||
'Never commit secret information. Avoid private information in public repositories.', | ||
howToCheck: 'Manual. View the repository on https://github.com', | ||
howToExempt: 'N/A', | ||
}, | ||
] as const satisfies readonly IBestPractice[]; | ||
|
||
const aws: readonly IBestPractice[] = [ | ||
{ | ||
name: 'Resource Tagging', | ||
owner: | ||
'[DevX Operations](https://github.com/orgs/guardian/teams/devx-operations)', | ||
description: | ||
'AWS resources should be tagged (where supported) with `Stack`, `Stage`, and `App`.<br>This aids service discovery, and cost allocation.', | ||
howToCheck: 'TBD', | ||
howToExempt: 'N/A', | ||
}, | ||
] as const satisfies readonly IBestPractice[]; | ||
|
||
const galaxiesPerson: readonly IBestPractice[] = [ | ||
{ | ||
name: 'GitHub Usernames', | ||
owner: | ||
'[DevX Operations](https://github.com/orgs/guardian/teams/devx-operations)', | ||
description: | ||
'Developers should update their [Galaxies profiles](https://forms.gle/7Yye3KfHefgYqg3c7) with their GitHub usernames', | ||
howToCheck: 'View on Galaxies', | ||
howToExempt: | ||
'Your Galaxies role is something other than an engineer/data analyst', | ||
}, | ||
]; | ||
|
||
const galaxiesTeam: readonly IBestPractice[] = [ | ||
{ | ||
name: 'Github Team', | ||
owner: | ||
'[DevX Operations](https://github.com/orgs/guardian/teams/devx-operations)', | ||
description: | ||
'Teams should have their GitHub team names in their galaxies entry', | ||
howToCheck: | ||
'Check in [this file](https://github.com/guardian/galaxies/blob/main/shared/data/teams.ts) in the Galaxies repo', | ||
howToExempt: "Teams that don't use GitHub are exempt", | ||
}, | ||
{ | ||
name: 'Team Emails', | ||
owner: | ||
'[DevX Operations](https://github.com/orgs/guardian/teams/devx-operations)', | ||
description: 'A team on Galaxies should have an email address entry', | ||
howToCheck: | ||
'Check in [this file](https://github.com/guardian/galaxies/blob/main/shared/data/teams.ts) in the Galaxies repo', | ||
howToExempt: 'N/A', | ||
}, | ||
{ | ||
name: 'Team Channels', | ||
owner: | ||
'[DevX Operations](https://github.com/orgs/guardian/teams/devx-operations)', | ||
description: | ||
'A team on Galaxies should have a public chat channel key listed', | ||
howToCheck: | ||
'Check in [this file](https://github.com/guardian/galaxies/blob/main/shared/data/teams.ts) in the Galaxies repo', | ||
//We rely on this information this for repocop alerts, so only teams that have repos are relevant at this stage | ||
howToExempt: | ||
"It's generally good practice to do this, but teams that don't use GitHub are exempt", | ||
}, | ||
] as const satisfies readonly IBestPractice[]; | ||
|
||
export const AllBestPractices: IAllBestPractice = { | ||
Repository: repository, | ||
AWS: aws, | ||
GalaxiesPerson: galaxiesPerson, | ||
GalaxiesTeam: galaxiesTeam, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/** | ||
* This script generates the best-practices.md file from the definitions in the definitions.ts file. | ||
* | ||
* Add new best practices to the definitions.ts file. | ||
*/ | ||
import * as fs from 'fs'; | ||
import { markdownTable } from 'markdown-table'; | ||
import { AllBestPractices } from './definitions.ts'; | ||
|
||
const markdownFilepath = './best-practices.md'; | ||
|
||
const file = fs.readFileSync(markdownFilepath, 'utf-8'); | ||
|
||
const startMark = '<!-- contentstart -->'; | ||
const endMark = '<!-- contentend -->'; | ||
|
||
if (!file.includes(startMark) || !file.includes(endMark)) { | ||
throw new Error( | ||
`Could not find start (${startMark}) and end markers (${endMark}) in ${markdownFilepath}`, | ||
); | ||
} | ||
|
||
const tableHeaderRow = [ | ||
'ID', // This will be auto-generated | ||
'Name', | ||
'Owner', | ||
'Description', | ||
'How to check compliance', | ||
'How to exempt', | ||
]; | ||
|
||
const markdownContent = Object.entries(AllBestPractices).flatMap( | ||
([section, bestPractices]) => { | ||
const markdownH2 = `## ${section}`; | ||
|
||
const tableRows = bestPractices.map((row, index) => { | ||
const id = [section, (index + 1).toString().padStart(2, '0')] | ||
.join('-') | ||
.toUpperCase(); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-assignment -- testing | ||
return [id, ...Object.values(row)]; | ||
}); | ||
|
||
const table = markdownTable([tableHeaderRow, ...tableRows]); | ||
|
||
return [markdownH2, table]; | ||
}, | ||
); | ||
|
||
// Find the markers, and replace them, and any text in between, with the new content. | ||
const re = new RegExp(`${startMark}(.|\n)*${endMark}`, 'm'); | ||
const updatedFile = file.replace( | ||
re, | ||
[startMark, ...markdownContent, endMark].join('\n'), | ||
); | ||
fs.writeFileSync(markdownFilepath, updatedFile, 'utf-8'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
export interface IBestPractice { | ||
/** | ||
* The name of the best practice. Should be short. | ||
*/ | ||
name: string; | ||
|
||
/** | ||
* The team responsible for monitoring, and communicating this best practice. | ||
* | ||
* This field supports Markdown notation, allowing you to, for example, link to a team's GitHub page. | ||
*/ | ||
owner: string; | ||
|
||
/** | ||
* A description of the best practice, explaining why it's important. | ||
* | ||
* This field supports Markdown notation. | ||
*/ | ||
description: string; | ||
|
||
/** | ||
* How to check if the best practice is being followed. | ||
* | ||
* For example, a link to a dashboard, or a command to run. | ||
*/ | ||
howToCheck: string; | ||
|
||
/** | ||
* How to exempt from the best practice. | ||
*/ | ||
howToExempt: string; | ||
} | ||
|
||
/** | ||
* A list of best practices, grouped by section. | ||
* The section will be used as a heading in the Markdown file. | ||
*/ | ||
export type IAllBestPractice = Record<string, readonly IBestPractice[]>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"extends": "../../tsconfig.json", | ||
"compilerOptions": { | ||
"module": "es6", | ||
"allowImportingTsExtensions": true | ||
}, | ||
"ts-node": { | ||
"esm": true, | ||
} | ||
} |