~emersion/public-inbox

gh2srht: Add support for exporting from organization v2 PROPOSED

I've tried implementing your suggestion, minimizing code duplication.
i'm not entirely satisfied with the result but I could not come up with a better
solution (I am also not very conversant with golang).

What do you think?

Thanks

jman (1):
  Add support for exporting from organization

 README.md |  10 ++++-
 main.go   | 121 +++++++++++++++++++++++++++++++++++++-----------------
 2 files changed, 93 insertions(+), 38 deletions(-)

-- 
2.35.1
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~emersion/public-inbox/patches/34641/mbox | git am -3
Learn more about email & git

[PATCH gh2srht v2 1/1] Add support for exporting from organization Export this patch

Signed-off-by: jman <srht@city17.xyz>
---
 README.md |  10 ++++-
 main.go   | 121 +++++++++++++++++++++++++++++++++++++-----------------
 2 files changed, 93 insertions(+), 38 deletions(-)

diff --git a/README.md b/README.md
index 4a5043b..f79027f 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,15 @@ A tool to migrate from GitHub to SourceHut.
Only todo.sr.ht is supported for now.

    export GITHUB_TOKEN=<token>
    go run . <repo> | gzip >todo.json.gz
    go run . [<repo_owner>] <repo> | gzip >todo.json.gz

Add `<repo_owner>` to export a repository that does not belong to your Github user or from an organization your Github user can access.

For example to export all issues from the repository `https://github.com/emersion/kanshi`, use:

```
go run . emersion kanshi | gzip >todo.json.gz
```

Then import the tracker dump to SourceHut.

diff --git a/main.go b/main.go
index e341e20..278531e 100644
--- a/main.go
+++ b/main.go
@@ -12,32 +12,60 @@ import (

const upstream = "https://github.com"

func populateLabels(client *githubv4.Client, repoName string, tracker *Tracker) {
	var query struct {
type repo_labels struct {
	Labels struct {
		Nodes      []GHLabel
		TotalCount int64
	} `graphql:"labels(first: 100)"`
}

type repo_issues struct {
	Issues struct {
		Nodes    []GHIssue
		PageInfo struct {
			EndCursor   githubv4.String
			HasNextPage bool
		}
		TotalCount int64
	} `graphql:"issues(first: 100, after: $issuesCursor)"`
}

func populateLabels(client *githubv4.Client, owner, repoName string, tracker *Tracker) {

	var query_no_owner struct {
		Viewer struct {
			Repository *struct {
				Labels struct {
					Nodes      []GHLabel
					TotalCount int64
				} `graphql:"labels(first: 100)"`
			} `graphql:"repository(name: $name)"`
			Repository repo_labels `graphql:"repository(name: $name)"`
		}
	}

	var query_with_owner struct {
		Repository repo_labels `graphql:"repository(owner: $owner, name: $name)"`
	}

	variables := map[string]interface{}{
		"name": githubv4.String(repoName),
	}
	if owner != "" {
		variables["owner"] = githubv4.String(owner)
	}

	log.Print("Fetching labels...")
	if err := client.Query(context.Background(), &query, variables); err != nil {
		log.Fatal(err)
	var repo = repo_labels{}
	if owner == "" {
		if err := client.Query(context.Background(), &query_no_owner, variables); err != nil {
			log.Fatal(err)
		}
		repo = query_no_owner.Viewer.Repository
	} else {
		if err := client.Query(context.Background(), &query_with_owner, variables); err != nil {
			log.Fatal(err)
		}
		repo = query_with_owner.Repository
	}

	repo := query.Viewer.Repository
	if repo == nil {
		log.Fatalf("Repository %q not found", repoName)
	}

	if repo.Labels.TotalCount > 100 {
		log.Fatalf("Too many labels (%v)", repo.Labels.TotalCount)
	}
@@ -47,22 +75,18 @@ func populateLabels(client *githubv4.Client, repoName string, tracker *Tracker)
	}
}

func populateTickets(client *githubv4.Client, repoName string, tracker *Tracker) {
	var query struct {
func populateTickets(client *githubv4.Client, owner, repoName string, tracker *Tracker) {

	var query_no_owner struct {
		Viewer struct {
			Repository struct {
				Issues struct {
					Nodes    []GHIssue
					PageInfo struct {
						EndCursor   githubv4.String
						HasNextPage bool
					}
					TotalCount int64
				} `graphql:"issues(first: 100, after: $issuesCursor)"`
			} `graphql:"repository(name: $name)"`
			Repository repo_issues `graphql:"repository(name: $name)"`
		}
	}

	var query_with_owner struct {
		Repository repo_issues `graphql:"repository(owner:$owner,name: $name)"`
	}

	variables := map[string]interface{}{
		"name":         githubv4.String(repoName),
		"issuesCursor": (*githubv4.String)(nil),
@@ -70,7 +94,11 @@ func populateTickets(client *githubv4.Client, repoName string, tracker *Tracker)
			githubv4.IssueTimelineItemsItemTypeIssueComment,
		},
	}
	if owner != "" {
		variables["owner"] = githubv4.String(owner)
	}

	var repo = repo_issues{}
	total := int64(0)
	for {
		pct := 0
@@ -79,31 +107,50 @@ func populateTickets(client *githubv4.Client, repoName string, tracker *Tracker)
		}

		log.Printf("Fetching issues (%v%% completed)...", pct)
		if err := client.Query(context.Background(), &query, variables); err != nil {
			log.Fatal(err)
		if owner == "" {
			if err := client.Query(context.Background(), &query_no_owner, variables); err != nil {
				log.Fatal(err)
			}
			repo = query_no_owner.Viewer.Repository
		} else {
			if err := client.Query(context.Background(), &query_with_owner, variables); err != nil {
				log.Fatal(err)
			}
			repo = query_with_owner.Repository
		}

		issues := query.Viewer.Repository.Issues

		total = issues.TotalCount
		total = repo.Issues.TotalCount

		for _, issue := range issues.Nodes {
		for _, issue := range repo.Issues.Nodes {
			tracker.Tickets = append(tracker.Tickets, *issue.ToSrHt())
		}

		if !issues.PageInfo.HasNextPage {
		if !repo.Issues.PageInfo.HasNextPage {
			break
		}

		variables["issuesCursor"] = githubv4.NewString(issues.PageInfo.EndCursor)
		variables["issuesCursor"] = githubv4.NewString(repo.Issues.PageInfo.EndCursor)
	}
}

func main() {
	if len(os.Args) < 1 {
		log.Fatal("usage: gh2srht <repo>")
	if len(os.Args) < 2 || len(os.Args) > 3 {
		log.Println("usage:   gh2srht <user-or-org> <repo>")
		log.Println("or:      gh2srht <repo>")
		log.Println("")
		log.Println("examples: gh2srht emersion kanshi")
		log.Println("          gh2srht kanshi")
		os.Exit(1)
	}

	owner, repoName := "", ""
	if len(os.Args) == 2 {
		repoName = os.Args[1]
	}
	if len(os.Args) == 3 {
		owner = os.Args[1]
		repoName = os.Args[2]
	}
	repoName := os.Args[1]

	token := os.Getenv("GITHUB_TOKEN")
	if token == "" {
@@ -125,8 +172,8 @@ func main() {
		Tickets: make([]Ticket, 0),
	}

	populateLabels(client, repoName, &tracker)
	populateTickets(client, repoName, &tracker)
	populateLabels(client, owner, repoName, &tracker)
	populateTickets(client, owner, repoName, &tracker)

	log.Print("Writing tracker dump...")

-- 
2.35.1