Skip to content

Commit

Permalink
Add github_tree table (#198)
Browse files Browse the repository at this point in the history
Co-authored-by: Pavel Savchenko <asfaltboy@gmail.com>
  • Loading branch information
cbruno10 and asfaltboy authored Sep 20, 2022
1 parent a5ae211 commit 9304475
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 1 deletion.
88 changes: 88 additions & 0 deletions docs/tables/github_tree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Table: github_tree

A Git tree object creates the hierarchy between files in a Git repository. You
can use the Git tree object to create the relationship between directories and
the files they contain.

A single tree object contains one or more entries, each of which is the SHA-1
hash of a blob or subtree with its associated mode, type, and filename.

The `github_tree` table can be used to query information about any tree, and
**you must specify which repository and tree SHA** in the where or join clause
using the `repository_full_name` and `tree_sha` columns. By default, recursive
entries are not returned, but can be with the `recursive` column.

## Examples

### List tree entries non-recursively

```sql
select
tree_sha,
truncated,
path,
mode,
type,
sha
from
github_tree
where
repository_full_name = 'turbot/steampipe'
and tree_sha = '0f200416c44b8b85277d973bff933efa8ef7803a';
```

### List tree entries for a subtree recursively

```sql
select
tree_sha,
truncated,
path,
mode,
type,
sha
from
github_tree
where
repository_full_name = 'turbot/steampipe'
and tree_sha = '5622172b528cd38438c52ecfa3c20ac3f71dd2df'
and recursive = true;
```

### List executable files

```sql
select
tree_sha,
truncated,
path,
mode,
size,
sha
from
github_tree
where
repository_full_name = 'turbot/steampipe'
and tree_sha = '0f200416c44b8b85277d973bff933efa8ef7803a'
and recursive = true
and mode = '100755';
```

### List JSON files

```sql
select
tree_sha,
truncated,
path,
mode,
size,
sha
from
github_tree
where
repository_full_name = 'turbot/steampipe'
and tree_sha = '0f200416c44b8b85277d973bff933efa8ef7803a'
and recursive = true
and path like '%.json';
```
3 changes: 2 additions & 1 deletion github/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,10 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"github_team": tableGitHubTeam(),
"github_traffic_view_daily": tableGitHubTrafficViewDaily(ctx),
"github_traffic_view_weekly": tableGitHubTrafficViewWeekly(ctx),
"github_tree": tableGitHubTree(ctx),
"github_user": tableGitHubUser(),
"github_workflow": tableGitHubWorkflow(ctx),
},
}
return p
}
}
97 changes: 97 additions & 0 deletions github/table_github_tree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package github

import (
"context"

"github.com/google/go-github/v45/github"

"github.com/turbot/steampipe-plugin-sdk/v4/grpc/proto"
"github.com/turbot/steampipe-plugin-sdk/v4/plugin"
"github.com/turbot/steampipe-plugin-sdk/v4/plugin/transform"
)

//// TABLE DEFINITION

func tableGitHubTree(ctx context.Context) *plugin.Table {
return &plugin.Table{
Name: "github_tree",
Description: "Lists directories and files in the given repository's git tree.",
List: &plugin.ListConfig{
Hydrate: tableGitHubTreeList,
ShouldIgnoreError: isNotFoundError([]string{"404"}),
KeyColumns: []*plugin.KeyColumn{
{Name: "repository_full_name", Require: plugin.Required},
{Name: "tree_sha", Require: plugin.Required},
{Name: "recursive", Require: plugin.Optional, CacheMatch: "exact"},
},
},
Columns: []*plugin.Column{
// Top columns
{Name: "repository_full_name", Type: proto.ColumnType_STRING, Transform: transform.FromQual("repository_full_name"), Description: "Full name of the repository that contains the tree."},
{Name: "tree_sha", Type: proto.ColumnType_STRING, Transform: transform.FromQual("tree_sha"), Description: "SHA1 of the tree."},
// Other columns
{Name: "recursive", Type: proto.ColumnType_BOOL, Description: "If set to true, return objects or subtrees referenced by the tree. Defaults to false."},
{Name: "truncated", Type: proto.ColumnType_BOOL, Description: "True if the entires were truncated because the number of items in the tree exceeded Github's maximum limit."},
{Name: "mode", Type: proto.ColumnType_STRING, Transform: transform.FromField("TreeEntry.Mode"), Description: "File mode. Valid values are 100644 (blob file), 100755 (blob executable), 040000 (tree subdirectory), 160000 (commit submodule), 120000 (blob that specifies path of a symlink)."},
{Name: "path", Type: proto.ColumnType_STRING, Transform: transform.FromField("TreeEntry.Path"), Description: "The file referenced in the tree."},
{Name: "sha", Type: proto.ColumnType_STRING, Transform: transform.FromField("TreeEntry.SHA"), Description: "SHA1 checksum ID of the object in the tree."},
{Name: "size", Type: proto.ColumnType_STRING, Transform: transform.FromField("TreeEntry.Size"), Description: "Size of the blob."},
{Name: "type", Type: proto.ColumnType_STRING, Transform: transform.FromField("TreeEntry.Type"), Description: "Either blob, tree, or commit."},
{Name: "url", Type: proto.ColumnType_STRING, Transform: transform.FromField("TreeEntry.URL"), Description: "URL to the file referenced in the tree."},
},
}
}

type treeEntry struct {
TreeEntry *github.TreeEntry
Recursive bool
Truncated *bool
}

//// GET FUNCTION

func tableGitHubTreeList(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
logger := plugin.Logger(ctx)
client := connect(ctx, d)

quals := d.KeyColumnQuals
fullName := quals["repository_full_name"].GetStringValue()
sha := quals["tree_sha"].GetStringValue()
recursive := quals["recursive"].GetBoolValue()

owner, repo := parseRepoFullName(fullName)

type GetResponse struct {
tree *github.Tree
resp *github.Response
}

getTree := func(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
tree, resp, err := client.Git.GetTree(ctx, owner, repo, sha, recursive)
return GetResponse{
tree: tree,
resp: resp,
}, err
}
getResponse, err := plugin.RetryHydrate(ctx, d, h, getTree, &plugin.RetryConfig{ShouldRetryError: shouldRetryError})

if err != nil {
logger.Error("github_tree.tableGitHubTreeList", "api_error", err)
return nil, err
}

getResp := getResponse.(GetResponse)
tree := getResp.tree
entries := tree.Entries

for _, entry := range entries {
entryRow := treeEntry{
TreeEntry: entry,
Recursive: recursive,
Truncated: tree.Truncated,
}
d.StreamListItem(ctx, entryRow)
}

return nil, nil
}

0 comments on commit 9304475

Please sign in to comment.