Skip to content

Commit 4d5f719

Browse files
ashwin-antwilliammartin
authored andcommitted
switch to approach that does commit signing
1 parent 9845c73 commit 4d5f719

File tree

2 files changed

+143
-74
lines changed

2 files changed

+143
-74
lines changed

pkg/github/repositories.go

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -585,10 +585,6 @@ func DeleteFile(getClient GetClientFn, t translations.TranslationHelperFunc) (to
585585
mcp.Required(),
586586
mcp.Description("Branch to delete the file from"),
587587
),
588-
mcp.WithString("sha",
589-
mcp.Required(),
590-
mcp.Description("SHA of the file being deleted"),
591-
),
592588
),
593589
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
594590
owner, err := requiredParam[string](request, "owner")
@@ -611,38 +607,70 @@ func DeleteFile(getClient GetClientFn, t translations.TranslationHelperFunc) (to
611607
if err != nil {
612608
return mcp.NewToolResultError(err.Error()), nil
613609
}
614-
sha, err := requiredParam[string](request, "sha")
610+
611+
client, err := getClient(ctx)
615612
if err != nil {
616-
return mcp.NewToolResultError(err.Error()), nil
613+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
617614
}
618615

619-
// Create the file options
620-
opts := &github.RepositoryContentFileOptions{
621-
Message: github.Ptr(message),
622-
Branch: github.Ptr(branch),
623-
SHA: github.Ptr(sha),
616+
// Get the reference for the branch
617+
ref, resp, err := client.Git.GetRef(ctx, owner, repo, "refs/heads/"+branch)
618+
if err != nil {
619+
return nil, fmt.Errorf("failed to get branch reference: %w", err)
624620
}
621+
defer func() { _ = resp.Body.Close() }()
625622

626-
// Delete the file
627-
client, err := getClient(ctx)
623+
// Get the commit object that the branch points to
624+
baseCommit, resp, err := client.Git.GetCommit(ctx, owner, repo, *ref.Object.SHA)
628625
if err != nil {
629-
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
626+
return nil, fmt.Errorf("failed to get base commit: %w", err)
630627
}
631-
deleteResponse, resp, err := client.Repositories.DeleteFile(ctx, owner, repo, path, opts)
628+
defer func() { _ = resp.Body.Close() }()
629+
630+
// Create a tree entry for the file deletion by setting SHA to nil
631+
treeEntries := []*github.TreeEntry{
632+
{
633+
Path: github.Ptr(path),
634+
Mode: github.Ptr("100644"), // Regular file mode
635+
Type: github.Ptr("blob"),
636+
SHA: nil, // Setting SHA to nil deletes the file
637+
},
638+
}
639+
640+
// Create a new tree with the deletion
641+
newTree, resp, err := client.Git.CreateTree(ctx, owner, repo, *baseCommit.Tree.SHA, treeEntries)
632642
if err != nil {
633-
return nil, fmt.Errorf("failed to delete file: %w", err)
643+
return nil, fmt.Errorf("failed to create tree: %w", err)
634644
}
635645
defer func() { _ = resp.Body.Close() }()
636646

637-
if resp.StatusCode != 200 && resp.StatusCode != 204 {
638-
body, err := io.ReadAll(resp.Body)
639-
if err != nil {
640-
return nil, fmt.Errorf("failed to read response body: %w", err)
641-
}
642-
return mcp.NewToolResultError(fmt.Sprintf("failed to delete file: %s", string(body))), nil
647+
// Create a new commit with the new tree
648+
commit := &github.Commit{
649+
Message: github.Ptr(message),
650+
Tree: newTree,
651+
Parents: []*github.Commit{{SHA: baseCommit.SHA}},
652+
}
653+
newCommit, resp, err := client.Git.CreateCommit(ctx, owner, repo, commit, nil)
654+
if err != nil {
655+
return nil, fmt.Errorf("failed to create commit: %w", err)
656+
}
657+
defer func() { _ = resp.Body.Close() }()
658+
659+
// Update the branch reference to point to the new commit
660+
ref.Object.SHA = newCommit.SHA
661+
_, resp, err = client.Git.UpdateRef(ctx, owner, repo, ref, false)
662+
if err != nil {
663+
return nil, fmt.Errorf("failed to update reference: %w", err)
664+
}
665+
defer func() { _ = resp.Body.Close() }()
666+
667+
// Create a response similar to what the DeleteFile API would return
668+
response := map[string]interface{}{
669+
"commit": newCommit,
670+
"content": nil,
643671
}
644672

645-
r, err := json.Marshal(deleteResponse)
673+
r, err := json.Marshal(response)
646674
if err != nil {
647675
return nil, fmt.Errorf("failed to marshal response: %w", err)
648676
}

pkg/github/repositories_test.go

Lines changed: 92 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,49 +1541,96 @@ func Test_DeleteFile(t *testing.T) {
15411541
assert.Contains(t, tool.InputSchema.Properties, "path")
15421542
assert.Contains(t, tool.InputSchema.Properties, "message")
15431543
assert.Contains(t, tool.InputSchema.Properties, "branch")
1544-
assert.Contains(t, tool.InputSchema.Properties, "sha")
1545-
assert.ElementsMatch(t, tool.InputSchema.Required, []string{"owner", "repo", "path", "message", "branch", "sha"})
1544+
// SHA is no longer required since we're using Git Data API
1545+
assert.ElementsMatch(t, tool.InputSchema.Required, []string{"owner", "repo", "path", "message", "branch"})
15461546

1547-
// Setup mock delete response
1548-
mockDeleteResponse := &github.RepositoryContentResponse{
1549-
Content: &github.RepositoryContent{
1550-
Name: github.Ptr("example.md"),
1551-
Path: github.Ptr("docs/example.md"),
1552-
SHA: github.Ptr("abc123def456"),
1553-
HTMLURL: github.Ptr("https://github.com/owner/repo/blob/main/docs/example.md"),
1547+
// Setup mock objects for Git Data API
1548+
mockRef := &github.Reference{
1549+
Ref: github.Ptr("refs/heads/main"),
1550+
Object: &github.GitObject{
1551+
SHA: github.Ptr("abc123"),
15541552
},
1555-
Commit: github.Commit{
1556-
SHA: github.Ptr("def456abc789"),
1557-
Message: github.Ptr("Delete example file"),
1558-
Author: &github.CommitAuthor{
1559-
Name: github.Ptr("Test User"),
1560-
Email: github.Ptr("test@example.com"),
1561-
Date: &github.Timestamp{Time: time.Now()},
1562-
},
1563-
HTMLURL: github.Ptr("https://github.com/owner/repo/commit/def456abc789"),
1553+
}
1554+
1555+
mockCommit := &github.Commit{
1556+
SHA: github.Ptr("abc123"),
1557+
Tree: &github.Tree{
1558+
SHA: github.Ptr("def456"),
15641559
},
15651560
}
15661561

1562+
mockTree := &github.Tree{
1563+
SHA: github.Ptr("ghi789"),
1564+
}
1565+
1566+
mockNewCommit := &github.Commit{
1567+
SHA: github.Ptr("jkl012"),
1568+
Message: github.Ptr("Delete example file"),
1569+
HTMLURL: github.Ptr("https://github.com/owner/repo/commit/jkl012"),
1570+
}
1571+
15671572
tests := []struct {
1568-
name string
1569-
mockedClient *http.Client
1570-
requestArgs map[string]interface{}
1571-
expectError bool
1572-
expectedResult *github.RepositoryContentResponse
1573-
expectedErrMsg string
1573+
name string
1574+
mockedClient *http.Client
1575+
requestArgs map[string]interface{}
1576+
expectError bool
1577+
expectedCommitSHA string
1578+
expectedErrMsg string
15741579
}{
15751580
{
1576-
name: "successful file deletion",
1581+
name: "successful file deletion using Git Data API",
15771582
mockedClient: mock.NewMockedHTTPClient(
1583+
// Get branch reference
1584+
mock.WithRequestMatch(
1585+
mock.GetReposGitRefByOwnerByRepoByRef,
1586+
mockRef,
1587+
),
1588+
// Get commit
1589+
mock.WithRequestMatch(
1590+
mock.GetReposGitCommitsByOwnerByRepoByCommitSha,
1591+
mockCommit,
1592+
),
1593+
// Create tree
1594+
mock.WithRequestMatchHandler(
1595+
mock.PostReposGitTreesByOwnerByRepo,
1596+
expectRequestBody(t, map[string]interface{}{
1597+
"base_tree": "def456",
1598+
"tree": []interface{}{
1599+
map[string]interface{}{
1600+
"path": "docs/example.md",
1601+
"mode": "100644",
1602+
"type": "blob",
1603+
"sha": nil,
1604+
},
1605+
},
1606+
}).andThen(
1607+
mockResponse(t, http.StatusCreated, mockTree),
1608+
),
1609+
),
1610+
// Create commit
15781611
mock.WithRequestMatchHandler(
1579-
mock.DeleteReposContentsByOwnerByRepoByPath,
1612+
mock.PostReposGitCommitsByOwnerByRepo,
15801613
expectRequestBody(t, map[string]interface{}{
15811614
"message": "Delete example file",
1582-
"branch": "main",
1583-
"sha": "abc123def456",
1584-
"content": interface{}(nil),
1615+
"tree": "ghi789",
1616+
"parents": []interface{}{"abc123"},
1617+
}).andThen(
1618+
mockResponse(t, http.StatusCreated, mockNewCommit),
1619+
),
1620+
),
1621+
// Update reference
1622+
mock.WithRequestMatchHandler(
1623+
mock.PatchReposGitRefsByOwnerByRepoByRef,
1624+
expectRequestBody(t, map[string]interface{}{
1625+
"sha": "jkl012",
1626+
"force": false,
15851627
}).andThen(
1586-
mockResponse(t, http.StatusOK, mockDeleteResponse),
1628+
mockResponse(t, http.StatusOK, &github.Reference{
1629+
Ref: github.Ptr("refs/heads/main"),
1630+
Object: &github.GitObject{
1631+
SHA: github.Ptr("jkl012"),
1632+
},
1633+
}),
15871634
),
15881635
),
15891636
),
@@ -1593,19 +1640,18 @@ func Test_DeleteFile(t *testing.T) {
15931640
"path": "docs/example.md",
15941641
"message": "Delete example file",
15951642
"branch": "main",
1596-
"sha": "abc123def456",
15971643
},
1598-
expectError: false,
1599-
expectedResult: mockDeleteResponse,
1644+
expectError: false,
1645+
expectedCommitSHA: "jkl012",
16001646
},
16011647
{
1602-
name: "file deletion fails",
1648+
name: "file deletion fails - branch not found",
16031649
mockedClient: mock.NewMockedHTTPClient(
16041650
mock.WithRequestMatchHandler(
1605-
mock.DeleteReposContentsByOwnerByRepoByPath,
1651+
mock.GetReposGitRefByOwnerByRepoByRef,
16061652
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
16071653
w.WriteHeader(http.StatusNotFound)
1608-
_, _ = w.Write([]byte(`{"message": "Not Found"}`))
1654+
_, _ = w.Write([]byte(`{"message": "Reference not found"}`))
16091655
}),
16101656
),
16111657
),
@@ -1614,11 +1660,10 @@ func Test_DeleteFile(t *testing.T) {
16141660
"repo": "repo",
16151661
"path": "docs/nonexistent.md",
16161662
"message": "Delete nonexistent file",
1617-
"branch": "main",
1618-
"sha": "abc123def456",
1663+
"branch": "nonexistent-branch",
16191664
},
16201665
expectError: true,
1621-
expectedErrMsg: "failed to delete file",
1666+
expectedErrMsg: "failed to get branch reference",
16221667
},
16231668
}
16241669

@@ -1647,20 +1692,16 @@ func Test_DeleteFile(t *testing.T) {
16471692
textContent := getTextResult(t, result)
16481693

16491694
// Unmarshal and verify the result
1650-
var returnedContent github.RepositoryContentResponse
1651-
err = json.Unmarshal([]byte(textContent.Text), &returnedContent)
1695+
var response map[string]interface{}
1696+
err = json.Unmarshal([]byte(textContent.Text), &response)
16521697
require.NoError(t, err)
16531698

1654-
// Verify content
1655-
if tc.expectedResult.Content != nil {
1656-
assert.Equal(t, *tc.expectedResult.Content.Name, *returnedContent.Content.Name)
1657-
assert.Equal(t, *tc.expectedResult.Content.Path, *returnedContent.Content.Path)
1658-
assert.Equal(t, *tc.expectedResult.Content.SHA, *returnedContent.Content.SHA)
1659-
}
1660-
1661-
// Verify commit
1662-
assert.Equal(t, *tc.expectedResult.Commit.SHA, *returnedContent.Commit.SHA)
1663-
assert.Equal(t, *tc.expectedResult.Commit.Message, *returnedContent.Commit.Message)
1699+
// Verify the response contains the expected commit
1700+
commit, ok := response["commit"].(map[string]interface{})
1701+
require.True(t, ok)
1702+
commitSHA, ok := commit["sha"].(string)
1703+
require.True(t, ok)
1704+
assert.Equal(t, tc.expectedCommitSHA, commitSHA)
16641705
})
16651706
}
16661707
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy