-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DOP-3911]: Check project path for monorepo #894
Changes from 30 commits
3a1f0d1
f406287
a790a75
28451bb
e47d9f5
665cce6
7c02379
1bcfe51
9081419
d825499
5461d09
b441922
9e26f66
0a7640d
e3f20c2
e296ff5
3874381
a6728f4
f546e8c
66bee93
d829955
3859aca
daa10eb
172d6e2
35bd5c0
a089ede
80cab7e
ee4691b
8fe0d9b
46918e7
15a3642
7cad533
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Octokit } from '@octokit/rest'; | ||
|
||
let client: Octokit; | ||
|
||
export function getOctokitClient(): Octokit { | ||
if (client) return client; | ||
|
||
try { | ||
const { GITHUB_BOT_PASSWORD } = process.env; | ||
|
||
if (!GITHUB_BOT_PASSWORD) throw new Error('GITHUB_BOT_PASSWORD is not defined'); | ||
|
||
client = new Octokit({ auth: GITHUB_BOT_PASSWORD }); | ||
return client; | ||
} catch (error) { | ||
console.error('ERROR! Failed to create Octokit client. Is GITHUB_BOT_PASSWORD defined?', error); | ||
throw error; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { getSnootyDirSet } from './utils/path-utils'; | ||
import { GitCommitInfo } from './types/github-types'; | ||
import { getProjectDirFromPath } from './services/get-paths'; | ||
|
||
interface FileUpdatePayload { | ||
ownerName: string; | ||
repoName: string; | ||
commitSha: string; | ||
updatedFilePaths: string[]; | ||
} | ||
|
||
/** | ||
* Retrieves the path of project directories. This is determined | ||
* by finding the nearest parent directory that has a snooty.toml file | ||
* for a given updated file path from a commit. | ||
* @param repoName Name of the repository to check. | ||
* @param ownerName Name of the owner of the repository. | ||
* @param commitSha The Git commit SHA that contains the changed files. | ||
* @param updatedFilePaths An array of all of the changed files (added, removed, modified) | ||
* from the commit. The method `getUpdatedFilePaths` in the `src/monorepo/utils/path-utils.ts | ||
* can be used to parse these paths from a GitHub `Commit` object. | ||
* @returns An array of all the project paths that need to be built. | ||
*/ | ||
export async function getMonorepoPaths({ | ||
repoName, | ||
ownerName, | ||
commitSha, | ||
updatedFilePaths, | ||
}: FileUpdatePayload): Promise<string[]> { | ||
const commitInfo: GitCommitInfo = { | ||
ownerName, | ||
repoName, | ||
commitSha, | ||
}; | ||
|
||
const snootyDirSet = await getSnootyDirSet(commitInfo); | ||
|
||
const projects = updatedFilePaths.map((path) => getProjectDirFromPath(path, snootyDirSet)); | ||
|
||
// remove empty strings and remove duplicated values | ||
return Array.from(new Set(projects.filter((dir) => !!dir))); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { SNOOTY_TOML_FILENAME } from '../utils/monorepo-constants'; | ||
|
||
/** | ||
* This function returns the project path for a given file change from a docs repository | ||
* within the monorepo. This function supports nested projects. | ||
* @param path An added/modified/removed file path from a commit e.g. server-docs/source/index.rst | ||
* @param commitInfo Contains information | ||
* @returns The closest file path that contains a snooty.toml, relative to the path parameter. | ||
*/ | ||
export function getProjectDirFromPath(path: string, snootyDirSet: Set<string>): string { | ||
const pathArray = path.split('/'); | ||
if (pathArray.length === 0) { | ||
console.warn('WARNING! Empty path found: ', path); | ||
return ''; | ||
} | ||
|
||
/** | ||
* If the changed file is the snooty.toml file, we know that we | ||
* are in the project's root directory. We can join the original | ||
* pathArray to get the project path since the snooty.toml has been removed. | ||
*/ | ||
const changedFile = pathArray.pop(); | ||
|
||
if (changedFile === SNOOTY_TOML_FILENAME) return pathArray.join('/'); | ||
|
||
while (pathArray.length > 0) { | ||
const currDir = pathArray.join('/'); | ||
|
||
if (snootyDirSet.has(currDir)) return currDir; | ||
|
||
pathArray.pop(); | ||
} | ||
|
||
console.warn(`WARNING! No snooty.toml found for the given path: ${path}`); | ||
return ''; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
interface DirectoryConfig { | ||
snooty_toml?: string; | ||
source?: string; | ||
} | ||
|
||
interface RepoConfig { | ||
repoName: string; | ||
deployable: boolean; | ||
branches: BranchConfig[]; | ||
} | ||
|
||
interface BranchConfig { | ||
gitBranchName: string; | ||
} | ||
|
||
// TODO: Populate these more. For DOP-3911, they are | ||
// being added for testing purposes. | ||
export interface DocSetEntry { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently incomplete, but this contains some of the type information that will be needed for the |
||
project: string; | ||
prefix: string; | ||
bucket: string; | ||
url: string; | ||
directories?: DirectoryConfig; | ||
repos?: RepoConfig[]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export interface GitCommitInfo { | ||
commitSha: string; | ||
ownerName: string; | ||
repoName: string; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export const SNOOTY_TOML_FILENAME = 'snooty.toml'; | ||
export const MONOREPO_NAME = 'docs-monorepo'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { Commit } from '@octokit/webhooks-types'; | ||
import { getOctokitClient } from '../../clients/githubClient'; | ||
import { GitCommitInfo } from '../types/github-types'; | ||
import { SNOOTY_TOML_FILENAME } from './monorepo-constants'; | ||
|
||
/** | ||
* Creates a `Set` of all `snooty.toml` paths within the monorepo. | ||
* The function retrieves the monorepo's tree structure from GitHub. | ||
*/ | ||
export async function getSnootyDirSet({ commitSha, ownerName, repoName }: GitCommitInfo): Promise<Set<string>> { | ||
try { | ||
const client = getOctokitClient(); | ||
|
||
// getting the repository tree for a given commit SHA. This returns an object | ||
// with the property `tree` that is a flat array of all files in the repository. | ||
// The tree array contains objects that hold the file path. | ||
// Unlike the contents API for repositories, the actual file content is not returned. | ||
const { data } = await client.request('GET /repos/{owner}/{repo}/git/trees/{tree_sha}', { | ||
owner: ownerName, | ||
repo: repoName, | ||
tree_sha: commitSha, | ||
recursive: 'true', | ||
}); | ||
|
||
const snootyTomlDirs = data.tree | ||
.filter((treeNode) => !!treeNode.path?.includes(SNOOTY_TOML_FILENAME)) | ||
.map((treeNode) => { | ||
// casting the `treeNode.path` from `(string | undefined)` to `string` since the filter will ensure that the result | ||
// only includes treeNode.path values that are defined and include snooty.toml | ||
// in the path i.e. we will not have `undefined` as a value in the resulting array. | ||
const path = treeNode.path as string; | ||
|
||
// the - 1 is to remove the trailing slash | ||
return path.slice(0, path.length - SNOOTY_TOML_FILENAME.length - 1); | ||
}); | ||
|
||
const snootyDirSet = new Set(snootyTomlDirs); | ||
|
||
return snootyDirSet; | ||
} catch (error) { | ||
console.error( | ||
`ERROR! Unable to retrieve tree for SHA: ${commitSha} owner name: ${ownerName} repo name: ${repoName}`, | ||
error | ||
); | ||
throw error; | ||
} | ||
} | ||
|
||
export const getUpdatedFilePaths = (commit: Commit): string[] => | ||
commit.modified.concat(commit.added).concat(commit.removed); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { IConfig } from 'config'; | ||
import { Db } from 'mongodb'; | ||
import { ILogger } from '../services/logger'; | ||
import { BaseRepository } from './baseRepository'; | ||
|
||
const docSetCollectionName = process.env.DOCS_SET_COLLECTION_NAME || 'docset'; | ||
|
||
export class DocSetRepository extends BaseRepository { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently not used since the |
||
constructor(db: Db, config: IConfig, logger: ILogger) { | ||
super(config, logger, 'DocSetRepository', db.collection(docSetCollectionName)); | ||
} | ||
|
||
/** | ||
* Compares the project path from a monorepo push event, and compares it with | ||
* what is configured in the docset entry in Atlas. | ||
* @param path The project path where the snooty.toml file exists from the monorepo. | ||
* This path will reflect the current project path from a given commit. | ||
* @param projectName The project name for the docset entry. | ||
* @returns A boolean representing whether or not the configured docset entry snooty_toml path | ||
* matches the path found in GitHub. | ||
*/ | ||
async checkSnootyTomlPath(path: string, projectName: string) { | ||
const query = { project: projectName }; | ||
try { | ||
const docSetObject = await this.findOne( | ||
query, | ||
`Mongo Timeout Error: Timedout while retrieving repos entry for ${path}` | ||
); | ||
|
||
if (!docSetObject) { | ||
console.warn(`WARNING: The docset does not exist for the following project: ${projectName} \n path: ${path}`); | ||
|
||
return false; | ||
} | ||
|
||
return docSetObject.directories.snooty_toml === path; | ||
} catch (error) { | ||
console.warn( | ||
`WARNING: Error occurred when retrieving project path for ${projectName}. The following path was provided: ${path}`, | ||
error | ||
); | ||
return false; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixes bug with feature branch update.