I'm trying to use octokit/rest
to remove a directory programaticcaly. I have the following code
import {Octokit as Github} from '@octokit/rest';
const githubToken = "read from vault"
const getCurrentCommit = async (
github: Github,
org: string,
repo: string,
branch: string = 'main'
) => {
const {data: refData} = await github.git.getRef({
owner: org,
repo,
ref: `heads/${branch}`,
})
const commitSha = refData.object.sha
const {data: commitData} = await github.git.getCommit({
owner: org,
repo,
commit_sha: commitSha,
})
return {
commitSha,
treeSha: commitData.tree.sha,
}
}
const createNewCommit = async (
github: Github,
org: string,
repo: string,
message: string,
currentTreeSha: string,
currentCommitSha: string
) => {
const {data} = await github.git.createCommit({
owner: org,
repo,
message,
tree: currentTreeSha,
parents: [currentCommitSha],
});
return data;
}
const setBranchToCommit = (
github: Github,
org: string,
repo: string,
commitSha: string,
branch: string = `main`
) => github.git.updateRef({
owner: org,
repo,
ref: `heads/${branch}`,
sha: commitSha,
});
const getdirectorySha = async (
tree: { path?: string, sha?: string }[],
directoryName: string
) => {
return tree
.filter(({path: directoryPath}) => directoryPath ? directoryPath.includes(directoryName) : false)
.map(({sha}) => sha)
.filter(sha => sha !== undefined).values().next().value
};
const deleteFromRepo = async (
github: Github,
org: string,
repo: string,
directoryName: string,
branch: string = `main`
) => {
const currentCommit = await getCurrentCommit(github, org, repo, branch);
const {data: treeData} = await github.git.getTree({
owner: org,
repo,
tree_sha: currentCommit.treeSha
})
const directorySha = await getdirectorySha(treeData.tree, directoryName);
if (!directorySha) {
throw new Error(`Could not find an directory '${directoryName}'`)
}
// Skip the files in the directory I need to remove
treeData.tree = treeData.tree
.filter(({path: directoryPath}) => directoryPath && !(directoryPath.includes(directoryName)))
.map(({path: directoryPath, sha}) => {
return {path: directoryPath, sha}
});
// For some reason, this repopulates the files I just skipped
const commitMessage = `Deleting '${directoryName}' files.`
const newCommit = await createNewCommit(
github,
org,
repo,
commitMessage,
treeData.sha,
currentCommit.commitSha
)
await setBranchToCommit(github, org, repo, newCommit.sha, branch)
}
const checkRepositoryExists = (
organisationRepos: any,
repoName: string) => {
if (!organisationRepos.data.map((repo: { name: any }) => repo.name).includes(repoName)) {
let errorMessage = `Repo '${repoName}' does not exist`;
console.log(errorMessage);
throw new Error(errorMessage);
}
};
export const deletedirectoryFromGithubRepo = async (
directoryName: string
) => {
const github = new Github({auth: githubToken})
const organization = `myOrg`
const repoName = `myRepo`
const organisationRepos = await github.repos.listForOrg({org: organization})
checkRepositoryExists(organisationRepos, repoName);
await deleteFromRepo(github, organization, repoName, directoryName)
}
When I call deleteDirectoryFromGithubRepo
, it commits an empty commit to github. That is, it created a commit but without any changes in it.
I read that passing the current commit might be repopulating the deleted files. But removing it results in an error Update is not a fast forward
Full reponse from github
{
"name": "HttpError",
"status": 422,
"response": {
"url": "https://api.github.com/repos/myOrg/myRepo/git/refs/heads%2Fmain",
"status": 422,
"headers": {
"header1": "*",
"header2": "*"
},
"data": {
"message": "Update is not a fast forward",
"documentation_url": "https://docs.github.com/rest/reference/git#update-a-reference"
}
},
"request": {
"method": "PATCH",
"url": "https://api.github.com/repos/myOrg/myRepo/git/refs/heads%2Fmain",
"headers": {
"accept": "application/vnd.github.v3+json",
"user-agent": "octokit-rest.js/18.12.0 octokit-core.js/3.6.0 Node.js/14.19.3 (linux; x64)",
"authorization": "token [REDACTED]",
"content-type": "application/json; charset=utf-8"
},
"body": "{\"sha\":\"75dd3**************ee3da188d4\"}",
"request": {}
}
}
I'm using octokit-rest.js/18.12.0
Any idea how to fix this?
An answer was provided here here and a working example of deleting a directory can be found below
const deleteFromRepo = async (
github: Github,
org: string,
repo: string,
directoryName: string
) => {
const currentCommit = await getCurrentCommit(github, org, repo);
const {data: repoTree} = await github.git.getTree({
owner: org,
repo,
tree_sha: currentCommit.treeSha
})
const directorySha = await getDirectorySha(repoTree.tree, directoryName);
if (!directorySha) {
throw new Error(`Could not find an directory '${directoryName}'`)
}
const {data: directoryTree} = await github.git.getTree({
owner: org,
repo,
tree_sha: directorySha
})
const blobs: Blob[] = directoryTree.tree.map((blob) => {
return {'url': `${directoryName}/${blob.path}`, 'sha': null}
});
const newTree = await createNewTree(
github,
org,
repo,
blobs,
currentCommit.treeSha
)
const commitMessage = `Deleting '${directoryName}' files.`
const newCommit = await createNewCommit(
github,
org,
repo,
commitMessage,
newTree.sha,
currentCommit.commitSha
)
await setBranchToCommit(github, org, repo, newCommit.sha)
}