Skip to content

Commit

Permalink
Merge pull request #5 from mona-actions/amenocal/write-to-issue
Browse files Browse the repository at this point in the history
Adds ability to pass in repository list
  • Loading branch information
amenocal authored Jul 25, 2024
2 parents d4fe322 + 3ae9e64 commit b7e840d
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 28 deletions.
40 changes: 30 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ Creates a JSON file of the releases tied to a repository
```bash
gh migrate-releases export --hostname github.example.com -o <org-name> --repository <repo-name> --token <token>
```
```

```txt
Usage:
migrate-releases export [flags]
Expand All @@ -42,19 +43,38 @@ Recreates releases,from a source repository to a target repository
gh migrate-releases sync --source-hostname github.example.com --source-organization <source-org> --source-token <source-token> --repository <repo-name> --target-organization <target-org> --target-token <target-token> --mapping-file "path/to/user-mappings.csv"
```

```bash
```txt
Usage:
migrate-releases sync [flags]
Flags:
-h, --help help for sync
-m, --mapping-file string Mapping file path to use for mapping members handles
-r, --repository string repository to export/import releases from/to
-u, --source-hostname string GitHub Enterprise source hostname url (optional) Ex. github.example.com
-s, --source-organization string Source Organization to sync releases from
-a, --source-token string Source Organization GitHub token. Scopes: read:org, read:user, user:email
-t, --target-organization string Target Organization to sync releases from
-b, --target-token string Target Organization GitHub token. Scopes: admin:org
-h, --help help for sync
-m, --mapping-file string Mapping file path to use for mapping members handles
-r, --repository string repository to export/import releases from/to; can't be used with --repository-list
-l, --repository-list-file string file path that contains list of repositories to export/import releases from/to; can't be used with --repository
-u, --source-hostname string GitHub Enterprise source hostname url (optional) Ex. github.example.com
-s, --source-organization string Source Organization to sync releases from
-a, --source-token string Source Organization GitHub token. Scopes: read:org, read:user, user:email
-t, --target-organization string Target Organization to sync releases from
-b, --target-token string Target Organization GitHub token. Scopes: admin:org
```

### Repository List Example

A list of repositories can be provided to sync releases from multiple repositories to many repositories in a single target.

Example:

```txt
https://github.example.com/owner/repo-name
https://github.example.com/owner/repo-name2
```

or

```txt
owner/repo-name
owner/repo-name2
```

### Mapping File Example
Expand Down
9 changes: 6 additions & 3 deletions cmd/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var syncCmd = &cobra.Command{
ghHostname := cmd.Flag("source-hostname").Value.String()
repository := cmd.Flag("repository").Value.String()
mappingFile := cmd.Flag("mapping-file").Value.String()
repositoryList := cmd.Flag("repository-list-file").Value.String()

// Set ENV variables
os.Setenv("GHMT_SOURCE_ORGANIZATION", sourceOrganization)
Expand All @@ -34,6 +35,7 @@ var syncCmd = &cobra.Command{
os.Setenv("GHMT_SOURCE_HOSTNAME", ghHostname)
os.Setenv("GHMT_REPOSITORY", repository)
os.Setenv("GHMT_MAPPING_FILE", mappingFile)
os.Setenv("GHMT_REPOSITORY_LIST", repositoryList)

// Bind ENV variables in Viper
viper.BindEnv("SOURCE_ORGANIZATION")
Expand All @@ -43,6 +45,7 @@ var syncCmd = &cobra.Command{
viper.BindEnv("SOURCE_HOSTNAME")
viper.BindEnv("REPOSITORY")
viper.BindEnv("MAPPING_FILE")
viper.BindEnv("REPOSITORY_LIST")

// Call syncreleases
sync.SyncReleases()
Expand All @@ -54,7 +57,6 @@ func init() {

// Flags
syncCmd.Flags().StringP("source-organization", "s", "", "Source Organization to sync releases from")
syncCmd.MarkFlagRequired("source-organization")

syncCmd.Flags().StringP("target-organization", "t", "", "Target Organization to sync releases from")
syncCmd.MarkFlagRequired("target-organization")
Expand All @@ -65,8 +67,9 @@ func init() {
syncCmd.Flags().StringP("target-token", "b", "", "Target Organization GitHub token. Scopes: admin:org")
syncCmd.MarkFlagRequired("target-token")

syncCmd.Flags().StringP("repository", "r", "", "repository to export/import releases from/to")
syncCmd.MarkFlagRequired("repository")
syncCmd.Flags().StringP("repository", "r", "", "repository to export/import releases from/to; can't be used with --repository-list")

syncCmd.Flags().StringP("repository-list-file", "l", "", "file path that contains list of repositories to export/import releases from/to; can't be used with --repository")

syncCmd.Flags().StringP("mapping-file", "m", "", "Mapping file path to use for mapping members handles")

Expand Down
43 changes: 38 additions & 5 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -51,7 +52,7 @@ func newGHRestClient(token string, hostname string) *github.Client {
return client
}

func GetSourceRepositoryReleases() ([]*github.RepositoryRelease, error) {
func GetSourceRepositoryReleases(owner string, repository string) ([]*github.RepositoryRelease, error) {
client := newGHRestClient(viper.GetString("source_token"), viper.GetString("source_hostname"))

ctx := context.WithValue(context.Background(), github.SleepUntilPrimaryRateLimitResetWhenRateLimited, true)
Expand All @@ -60,9 +61,9 @@ func GetSourceRepositoryReleases() ([]*github.RepositoryRelease, error) {
opts := &github.ListOptions{PerPage: 100}

for {
releases, resp, err := client.Repositories.ListReleases(ctx, viper.Get("SOURCE_ORGANIZATION").(string), viper.Get("REPOSITORY").(string), opts)
releases, resp, err := client.Repositories.ListReleases(ctx, owner, repository, opts)
if err != nil {
return allReleases, fmt.Errorf("error getting releases: %v", err)
return allReleases, fmt.Errorf("unable to get releases: %v", err)
}
allReleases = append(allReleases, releases...)
if resp.NextPage == 0 {
Expand Down Expand Up @@ -182,11 +183,11 @@ func DownloadFileFromURL(url, fileName, token string) error {
return err
}

func CreateRelease(release *github.RepositoryRelease) (*github.RepositoryRelease, error) {
func CreateRelease(repository string, release *github.RepositoryRelease) (*github.RepositoryRelease, error) {
client := newGHRestClient(viper.GetString("TARGET_TOKEN"), "")

ctx := context.WithValue(context.Background(), github.SleepUntilPrimaryRateLimitResetWhenRateLimited, true)
newRelease, _, err := client.Repositories.CreateRelease(ctx, viper.Get("TARGET_ORGANIZATION").(string), viper.Get("REPOSITORY").(string), release)
newRelease, _, err := client.Repositories.CreateRelease(ctx, viper.Get("TARGET_ORGANIZATION").(string), repository, release)
if err != nil {
if strings.Contains(err.Error(), "already_exists") {
return nil, fmt.Errorf("release already exists: %v", release.GetName())
Expand Down Expand Up @@ -261,3 +262,35 @@ func UploadAssetViaURL(uploadURL string, asset *github.ReleaseAsset) error {

return nil
}

func WriteToIssue(owner string, repository string, issueNumber int, comment string) error {

client := newGHRestClient(viper.GetString("TARGET_TOKEN"), "")

ctx := context.WithValue(context.Background(), github.SleepUntilPrimaryRateLimitResetWhenRateLimited, true)
_, _, err := client.Issues.CreateComment(ctx, owner, repository, issueNumber, &github.IssueComment{Body: &comment})
if err != nil {
return err
}

return nil
}

func GetDatafromGitHubContext() (string, string, int, error) {
githubContext := os.Getenv("GITHUB_CONTEXT")
if githubContext == "" {
return "", "", 0, fmt.Errorf("GITHUB_CONTEXT is not set or empty")
}

var issueEvent github.IssueEvent

err := json.Unmarshal([]byte(githubContext), &issueEvent)
if err != nil {
return "", "", 0, fmt.Errorf("error unmarshalling GITHUB_CONTEXT: %v", err)
}
organization := *issueEvent.Repository.Owner.Login
repository := *issueEvent.Repository.Name
issueNumber := *issueEvent.Issue.Number

return organization, repository, issueNumber, nil
}
30 changes: 30 additions & 0 deletions internal/files/files.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package files

import (
"bufio"
"encoding/json"
"net/url"
"os"
"strings"
)

func OpenFile(fileName string) (*os.File, error) {
Expand Down Expand Up @@ -41,3 +44,30 @@ func CreateJSON(data interface{}, filename string) error {

return nil
}

// read repository list from file assuming each line is a repository
func ReadRepositoryListFromFile(fileName string) ([]string, error) {
file, err := os.Open(fileName)
if err != nil {
return nil, err
}
defer file.Close()

var repositories []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
repo := scanner.Text()
parsedURL, err := url.Parse(repo)
if err != nil {
return nil, err
}
path := strings.TrimPrefix(parsedURL.Path, "/")
repositories = append(repositories, path)
}

if err := scanner.Err(); err != nil {
return nil, err
}

return repositories, nil
}
7 changes: 5 additions & 2 deletions pkg/export/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import (
"github.com/mona-actions/gh-migrate-releases/internal/api"
"github.com/mona-actions/gh-migrate-releases/internal/files"
"github.com/pterm/pterm"
"github.com/spf13/viper"
)

func CreateJSONs() {
// Get all teams from source organization
fetchReleasesSpinner, _ := pterm.DefaultSpinner.Start("Fetching teams from organization...")
releases, err := api.GetSourceRepositoryReleases()
fetchReleasesSpinner, _ := pterm.DefaultSpinner.Start("Fetching releases from repository...")
repository := viper.GetString("REPOSITORY")
owner := viper.GetString("SOURCE_ORGANIZATION")
releases, err := api.GetSourceRepositoryReleases(owner, repository)
if err != nil {
pterm.Fatal.Printf("Error getting releases: %v", err)
}
Expand Down
Loading

0 comments on commit b7e840d

Please sign in to comment.