Simplifies init process and adds github issue publishing
This commit is contained in:
parent
21c18b3095
commit
be543e2cc4
23 changed files with 581 additions and 390 deletions
|
@ -18,7 +18,12 @@ func GrepAddCmd() *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "grep",
|
Use: "grep",
|
||||||
Short: "Generates the tickets reading grep's output from stdin",
|
Short: "Generates the tickets reading grep's output from stdin",
|
||||||
Long: "Generates tickets for the campaign reading from the standard input the output grep. The grep command must be run with the -n flag",
|
Long: `Generates tickets for the campaign reading from the standard input the output grep. The grep command must be run with the -n flag. The generated ticket will contain three fields:
|
||||||
|
|
||||||
|
- filename: the filename yield by grep
|
||||||
|
- lineNo: the line number yield by grep
|
||||||
|
- text: the trimmed line that grep captured for the expression
|
||||||
|
`,
|
||||||
Example: ` grep -nriIF --include \*.go cobra.Command | campaigner add grep`,
|
Example: ` grep -nriIF --include \*.go cobra.Command | campaigner add grep`,
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
Run: grepAddCmdF,
|
Run: grepAddCmdF,
|
||||||
|
@ -104,7 +109,7 @@ func parseGrepLine(line string) (*model.Ticket, error) {
|
||||||
Data: map[string]interface{}{
|
Data: map[string]interface{}{
|
||||||
"filename": filename,
|
"filename": filename,
|
||||||
"lineNo": lineNo,
|
"lineNo": lineNo,
|
||||||
"text": text,
|
"text": strings.TrimSpace(text),
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
63
cmd/init.go
63
cmd/init.go
|
@ -1,6 +1,9 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.ctrlz.es/mgdelacroix/campaigner/campaign"
|
"git.ctrlz.es/mgdelacroix/campaigner/campaign"
|
||||||
|
@ -14,42 +17,76 @@ func InitCmd() *cobra.Command {
|
||||||
Use: "init",
|
Use: "init",
|
||||||
Short: "Creates a new campaign in the current directory",
|
Short: "Creates a new campaign in the current directory",
|
||||||
Example: ` campaigner init \
|
Example: ` campaigner init \
|
||||||
|
--jira-username johndoe \
|
||||||
|
--jira-token secret \
|
||||||
|
--github-token TOKEN \
|
||||||
--url http://my-jira-instance.com \
|
--url http://my-jira-instance.com \
|
||||||
--epic ASD-27 \
|
--epic ASD-27 \
|
||||||
--issue-type Story \
|
--issue-type Story \
|
||||||
|
--repository johndoe/awesomeproject \
|
||||||
|
-l 'Area/API' -l 'Tech/Go' \
|
||||||
--summary 'Refactor {{.function}} to inject the configuration service' \
|
--summary 'Refactor {{.function}} to inject the configuration service' \
|
||||||
--template ./refactor-config.tmpl`,
|
--template ./refactor-config.tmpl`,
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
Run: initCmdF,
|
Run: initCmdF,
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().StringP("url", "u", "", "The jira server URL")
|
cmd.Flags().String("jira-username", "", "the jira username")
|
||||||
_ = cmd.MarkFlagRequired("url")
|
cmd.Flags().String("jira-token", "", "the jira token or password")
|
||||||
cmd.Flags().StringP("epic", "e", "", "The epic id to associate this campaign with")
|
cmd.Flags().String("github-token", "", "the github token")
|
||||||
_ = cmd.MarkFlagRequired("epic")
|
cmd.Flags().StringP("url", "u", "", "the jira server URL")
|
||||||
cmd.Flags().StringP("summary", "s", "", "The summary of the tickets")
|
cmd.Flags().StringP("epic", "e", "", "the epic id to associate this campaign with")
|
||||||
_ = cmd.MarkFlagRequired("summary")
|
cmd.Flags().StringP("repository", "r", "", "the github repository")
|
||||||
cmd.Flags().StringP("template", "t", "", "The template path for the description of the tickets")
|
cmd.Flags().StringSliceP("label", "l", []string{}, "the labels to add to the Github issues")
|
||||||
_ = cmd.MarkFlagRequired("template")
|
cmd.Flags().StringP("summary", "s", "", "the summary of the tickets")
|
||||||
cmd.Flags().StringP("issue-type", "i", "Story", "The issue type to create the tickets as")
|
cmd.Flags().StringP("template", "t", "", "the template path for the description of the tickets")
|
||||||
|
cmd.Flags().StringP("issue-type", "i", "Story", "the issue type to create the tickets as")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func initCmdF(cmd *cobra.Command, _ []string) {
|
func initCmdF(cmd *cobra.Command, _ []string) {
|
||||||
url, _ := cmd.Flags().GetString("url")
|
getStringFlagOrAskIfEmpty := func(name string, question string) string {
|
||||||
epic, _ := cmd.Flags().GetString("epic")
|
val, _ := cmd.Flags().GetString(name)
|
||||||
summary, _ := cmd.Flags().GetString("summary")
|
if val == "" {
|
||||||
template, _ := cmd.Flags().GetString("template")
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
fmt.Printf("%s ", question)
|
||||||
|
answer, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
ErrorAndExit(cmd, err)
|
||||||
|
}
|
||||||
|
val = strings.TrimSpace(answer)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
jiraUsername := getStringFlagOrAskIfEmpty("jira-username", "JIRA username:")
|
||||||
|
jiraToken := getStringFlagOrAskIfEmpty("jira-token", "JIRA password or token:")
|
||||||
|
githubToken := getStringFlagOrAskIfEmpty("github-token", "GitHub token:")
|
||||||
|
url := getStringFlagOrAskIfEmpty("url", "JIRA server URL:")
|
||||||
|
epic := getStringFlagOrAskIfEmpty("epic", "JIRA epic:")
|
||||||
|
repo := getStringFlagOrAskIfEmpty("repository", "GitHub repository:")
|
||||||
|
summary := getStringFlagOrAskIfEmpty("summary", "Ticket summary template:")
|
||||||
|
template := getStringFlagOrAskIfEmpty("template", "Ticket description template path:")
|
||||||
issueType, _ := cmd.Flags().GetString("issue-type")
|
issueType, _ := cmd.Flags().GetString("issue-type")
|
||||||
|
labels, _ := cmd.Flags().GetStringSlice("label")
|
||||||
|
|
||||||
project := strings.Split(epic, "-")[0]
|
project := strings.Split(epic, "-")[0]
|
||||||
|
|
||||||
cmp := &model.Campaign{
|
cmp := &model.Campaign{
|
||||||
|
Jira: model.ConfigJira{
|
||||||
Url: url,
|
Url: url,
|
||||||
|
Username: jiraUsername,
|
||||||
|
Token: jiraToken,
|
||||||
Project: project,
|
Project: project,
|
||||||
Epic: epic,
|
Epic: epic,
|
||||||
IssueType: issueType,
|
IssueType: issueType,
|
||||||
|
},
|
||||||
|
Github: model.ConfigGithub{
|
||||||
|
Token: githubToken,
|
||||||
|
Repo: repo,
|
||||||
|
Labels: labels,
|
||||||
|
},
|
||||||
Summary: summary,
|
Summary: summary,
|
||||||
Template: template,
|
Template: template,
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.ctrlz.es/mgdelacroix/campaigner/campaign"
|
"git.ctrlz.es/mgdelacroix/campaigner/campaign"
|
||||||
"git.ctrlz.es/mgdelacroix/campaigner/config"
|
|
||||||
"git.ctrlz.es/mgdelacroix/campaigner/github"
|
"git.ctrlz.es/mgdelacroix/campaigner/github"
|
||||||
"git.ctrlz.es/mgdelacroix/campaigner/jira"
|
"git.ctrlz.es/mgdelacroix/campaigner/jira"
|
||||||
|
|
||||||
|
@ -64,17 +63,12 @@ func jiraPublishCmdF(cmd *cobra.Command, _ []string) error {
|
||||||
return fmt.Errorf("One of --all or --batch flags is required")
|
return fmt.Errorf("One of --all or --batch flags is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.ReadConfig()
|
|
||||||
if err != nil {
|
|
||||||
ErrorAndExit(cmd, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmp, err := campaign.Read()
|
cmp, err := campaign.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorAndExit(cmd, err)
|
ErrorAndExit(cmd, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
jiraClient, err := jira.NewClient(cmp.Url, cfg.JiraUsername, cfg.JiraToken)
|
jiraClient, err := jira.NewClient(cmp.Jira.Url, cmp.Jira.Username, cmp.Jira.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorAndExit(cmd, err)
|
ErrorAndExit(cmd, err)
|
||||||
}
|
}
|
||||||
|
@ -104,17 +98,12 @@ func githubPublishCmdF(cmd *cobra.Command, _ []string) error {
|
||||||
return fmt.Errorf("One of --all or --batch flags is required")
|
return fmt.Errorf("One of --all or --batch flags is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.ReadConfig()
|
|
||||||
if err != nil {
|
|
||||||
ErrorAndExit(cmd, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmp, err := campaign.Read()
|
cmp, err := campaign.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorAndExit(cmd, err)
|
ErrorAndExit(cmd, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
githubClient := github.NewClient("my/repo", cfg.GithubToken)
|
githubClient := github.NewClient(cmp.Github.Repo, cmp.Github.Token)
|
||||||
|
|
||||||
if all {
|
if all {
|
||||||
count, err := githubClient.PublishAll(cmp, dryRun)
|
count, err := githubClient.PublishAll(cmp, dryRun)
|
||||||
|
|
|
@ -17,9 +17,7 @@ func RootCmd() *cobra.Command {
|
||||||
AddCmd(),
|
AddCmd(),
|
||||||
FilterCmd(),
|
FilterCmd(),
|
||||||
InitCmd(),
|
InitCmd(),
|
||||||
StandaloneCmd(),
|
|
||||||
StatusCmd(),
|
StatusCmd(),
|
||||||
TokenCmd(),
|
|
||||||
PublishCmd(),
|
PublishCmd(),
|
||||||
SyncCmd(),
|
SyncCmd(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,163 +0,0 @@
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.ctrlz.es/mgdelacroix/campaigner/config"
|
|
||||||
"git.ctrlz.es/mgdelacroix/campaigner/jira"
|
|
||||||
"git.ctrlz.es/mgdelacroix/campaigner/model"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
func StandaloneCmd() *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "standalone",
|
|
||||||
Short: "Standalone fire-and-forget commands",
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.AddCommand(
|
|
||||||
CreateJiraTicketStandaloneCmd(),
|
|
||||||
GetJiraTicketStandaloneCmd(),
|
|
||||||
)
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateJiraTicketStandaloneCmd() *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "create-jira-ticket",
|
|
||||||
Short: "Creates a jira ticket from a template",
|
|
||||||
Args: cobra.NoArgs,
|
|
||||||
RunE: createJiraTicketStandaloneCmdF,
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Flags().String("url", "", "The jira server URL")
|
|
||||||
_ = cmd.MarkFlagRequired("url")
|
|
||||||
cmd.Flags().String("epic", "", "The jira epic id to associate the ticket with")
|
|
||||||
_ = cmd.MarkFlagRequired("epic")
|
|
||||||
cmd.Flags().String("summary", "", "The summary of the ticket")
|
|
||||||
_ = cmd.MarkFlagRequired("summary")
|
|
||||||
cmd.Flags().String("template", "", "The template to render the description of the ticket")
|
|
||||||
_ = cmd.MarkFlagRequired("template")
|
|
||||||
cmd.Flags().String("username", "", "The jira username")
|
|
||||||
cmd.Flags().String("token", "", "The jira token")
|
|
||||||
cmd.Flags().StringSliceP("vars", "v", []string{}, "The variables to use in the template")
|
|
||||||
cmd.Flags().Bool("dry-run", false, "Print the ticket information instead of creating it")
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetJiraTicketStandaloneCmd() *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "get-jira-ticket",
|
|
||||||
Short: "Gets the ticket from jira",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
Run: getJiraTicketStandaloneCmdF,
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Flags().String("url", "", "The jira server URL")
|
|
||||||
_ = cmd.MarkFlagRequired("url")
|
|
||||||
cmd.Flags().String("username", "", "The jira username")
|
|
||||||
cmd.Flags().String("token", "", "The jira token")
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func getVarMap(vars []string) (map[string]interface{}, error) {
|
|
||||||
varMap := map[string]interface{}{}
|
|
||||||
for _, v := range vars {
|
|
||||||
parts := strings.Split(v, "=")
|
|
||||||
if len(parts) < 2 {
|
|
||||||
return nil, fmt.Errorf("cannot parse var %s", v)
|
|
||||||
}
|
|
||||||
varMap[parts[0]] = strings.Join(parts[1:], "=")
|
|
||||||
}
|
|
||||||
return varMap, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createJiraTicketStandaloneCmdF(cmd *cobra.Command, _ []string) error {
|
|
||||||
url, _ := cmd.Flags().GetString("url")
|
|
||||||
epic, _ := cmd.Flags().GetString("epic")
|
|
||||||
username, _ := cmd.Flags().GetString("username")
|
|
||||||
token, _ := cmd.Flags().GetString("token")
|
|
||||||
summary, _ := cmd.Flags().GetString("summary")
|
|
||||||
template, _ := cmd.Flags().GetString("template")
|
|
||||||
vars, _ := cmd.Flags().GetStringSlice("vars")
|
|
||||||
dryRun, _ := cmd.Flags().GetBool("dry-run")
|
|
||||||
|
|
||||||
project := strings.Split(epic, "-")[0]
|
|
||||||
|
|
||||||
if username == "" || token == "" {
|
|
||||||
cfg, err := config.ReadConfig()
|
|
||||||
if err != nil {
|
|
||||||
ErrorAndExit(cmd, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if username == "" {
|
|
||||||
username = cfg.JiraUsername
|
|
||||||
}
|
|
||||||
if token == "" {
|
|
||||||
token = cfg.JiraToken
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
varMap, err := getVarMap(vars)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error processing vars: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
jiraClient, err := jira.NewClient(url, username, token)
|
|
||||||
if err != nil {
|
|
||||||
ErrorAndExit(cmd, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
campaign := &model.Campaign{
|
|
||||||
Epic: epic,
|
|
||||||
Project: project,
|
|
||||||
Summary: summary,
|
|
||||||
Template: template,
|
|
||||||
}
|
|
||||||
ticket := &model.Ticket{Data: varMap}
|
|
||||||
|
|
||||||
issue, err := jiraClient.PublishTicket(ticket, campaign, dryRun)
|
|
||||||
if err != nil {
|
|
||||||
ErrorAndExit(cmd, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Printf("Ticket %s successfully created in JIRA", issue.Key)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getJiraTicketStandaloneCmdF(cmd *cobra.Command, args []string) {
|
|
||||||
url, _ := cmd.Flags().GetString("url")
|
|
||||||
username, _ := cmd.Flags().GetString("username")
|
|
||||||
token, _ := cmd.Flags().GetString("token")
|
|
||||||
|
|
||||||
if username == "" || token == "" {
|
|
||||||
cfg, err := config.ReadConfig()
|
|
||||||
if err != nil {
|
|
||||||
ErrorAndExit(cmd, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if username == "" {
|
|
||||||
username = cfg.JiraUsername
|
|
||||||
}
|
|
||||||
if token == "" {
|
|
||||||
token = cfg.JiraToken
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jiraClient, err := jira.NewClient(url, username, token)
|
|
||||||
if err != nil {
|
|
||||||
ErrorAndExit(cmd, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
issue, err := jiraClient.GetIssue(args[0])
|
|
||||||
if err != nil {
|
|
||||||
ErrorAndExit(cmd, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Summary: %s\nKey: %s\nStatus: %s\nAsignee: %s\n", issue.Fields.Summary, issue.Key, issue.Fields.Status.Name, issue.Fields.Assignee.DisplayName)
|
|
||||||
}
|
|
79
cmd/token.go
79
cmd/token.go
|
@ -1,79 +0,0 @@
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.ctrlz.es/mgdelacroix/campaigner/config"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TokenSetJiraCmd() *cobra.Command {
|
|
||||||
return &cobra.Command{
|
|
||||||
Use: "jira USERNAME TOKEN",
|
|
||||||
Short: "Sets the value of the jira token",
|
|
||||||
Args: cobra.ExactArgs(2),
|
|
||||||
RunE: tokenSetJiraCmdF,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TokenSetGithubCmd() *cobra.Command {
|
|
||||||
return &cobra.Command{
|
|
||||||
Use: "github TOKEN",
|
|
||||||
Short: "Sets the value of the github token",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
RunE: tokenSetGithubCmdF,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TokenSetCmd() *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "set",
|
|
||||||
Short: "Sets the value of the platform tokens",
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.AddCommand(
|
|
||||||
TokenSetJiraCmd(),
|
|
||||||
TokenSetGithubCmd(),
|
|
||||||
)
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func TokenCmd() *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "token",
|
|
||||||
Short: "Subcommands related to tokens",
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.AddCommand(
|
|
||||||
TokenSetCmd(),
|
|
||||||
)
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func tokenSetJiraCmdF(cmd *cobra.Command, args []string) error {
|
|
||||||
cfg, err := config.ReadConfig()
|
|
||||||
if err != nil {
|
|
||||||
ErrorAndExit(cmd, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.JiraUsername = args[0]
|
|
||||||
cfg.JiraToken = args[1]
|
|
||||||
if err := config.SaveConfig(cfg); err != nil {
|
|
||||||
ErrorAndExit(cmd, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func tokenSetGithubCmdF(cmd *cobra.Command, args []string) error {
|
|
||||||
cfg, err := config.ReadConfig()
|
|
||||||
if err != nil {
|
|
||||||
ErrorAndExit(cmd, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.GithubToken = args[0]
|
|
||||||
if err := config.SaveConfig(cfg); err != nil {
|
|
||||||
ErrorAndExit(cmd, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/user"
|
|
||||||
|
|
||||||
"git.ctrlz.es/mgdelacroix/campaigner/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getConfigPath() (string, error) {
|
|
||||||
user, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return user.HomeDir + "/.campaigner", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadConfig() (*model.Config, error) {
|
|
||||||
configPath, err := getConfigPath()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Stat(configPath); err != nil {
|
|
||||||
return &model.Config{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fileContents, err := ioutil.ReadFile(configPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("there was a problem reading the config file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var config model.Config
|
|
||||||
if err := json.Unmarshal(fileContents, &config); err != nil {
|
|
||||||
return nil, fmt.Errorf("there was a problem parsing the config file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveConfig(config *model.Config) error {
|
|
||||||
configPath, err := getConfigPath()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
marshaledConfig, err := json.MarshalIndent(config, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ioutil.WriteFile(configPath, marshaledConfig, 0600); err != nil {
|
|
||||||
return fmt.Errorf("cannot save the config: %w", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -2,10 +2,13 @@ package github
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"git.ctrlz.es/mgdelacroix/campaigner/campaign"
|
"git.ctrlz.es/mgdelacroix/campaigner/campaign"
|
||||||
"git.ctrlz.es/mgdelacroix/campaigner/model"
|
"git.ctrlz.es/mgdelacroix/campaigner/model"
|
||||||
|
|
||||||
|
"github.com/StevenACoffman/j2m"
|
||||||
"github.com/google/go-github/v29/github"
|
"github.com/google/go-github/v29/github"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
@ -28,7 +31,28 @@ func NewClient(repo, token string) *GithubClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GithubClient) PublishTicket(ticket *model.Ticket, cmp *model.Campaign, dryRun bool) (*github.Issue, error) {
|
func (c *GithubClient) PublishTicket(ticket *model.Ticket, cmp *model.Campaign, dryRun bool) (*github.Issue, error) {
|
||||||
return nil, nil
|
mdDescription := j2m.JiraToMD(ticket.Description)
|
||||||
|
issueRequest := &github.IssueRequest{
|
||||||
|
Title: &ticket.Summary,
|
||||||
|
Body: &mdDescription,
|
||||||
|
Labels: &cmp.Github.Labels,
|
||||||
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
b, _ := json.MarshalIndent(issueRequest, "", " ")
|
||||||
|
fmt.Println(string(b))
|
||||||
|
return &github.Issue{
|
||||||
|
Title: issueRequest.Title,
|
||||||
|
Body: issueRequest.Body,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
owner, repo := cmp.RepoComponents()
|
||||||
|
newIssue, _, err := c.Issues.Create(context.Background(), owner, repo, issueRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newIssue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GithubClient) PublishNextTicket(cmp *model.Campaign, dryRun bool) (bool, error) {
|
func (c *GithubClient) PublishNextTicket(cmp *model.Campaign, dryRun bool) (bool, error) {
|
||||||
|
@ -47,9 +71,6 @@ func (c *GithubClient) PublishNextTicket(cmp *model.Campaign, dryRun bool) (bool
|
||||||
}
|
}
|
||||||
|
|
||||||
ticket.GithubLink = *issue.ID
|
ticket.GithubLink = *issue.ID
|
||||||
// move this to a publish service that can do both github and
|
|
||||||
// jira, as we need to update a jira issue field with the github
|
|
||||||
// link
|
|
||||||
if err := campaign.Save(cmp); err != nil {
|
if err := campaign.Save(cmp); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -72,7 +93,7 @@ func (c *GithubClient) PublishAll(cmp *model.Campaign, dryRun bool) (int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GithubClient) PublishBatch(cmp *model.Campaign, batch int, dryRun bool) error {
|
func (c *GithubClient) PublishBatch(cmp *model.Campaign, batch int, dryRun bool) error {
|
||||||
for i := 0; i <= batch; i++ {
|
for i := 1; i <= batch; i++ {
|
||||||
next, err := c.PublishNextTicket(cmp, dryRun)
|
next, err := c.PublishNextTicket(cmp, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -3,7 +3,7 @@ module git.ctrlz.es/mgdelacroix/campaigner
|
||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.9.0 // indirect
|
github.com/StevenACoffman/j2m v0.0.0-20190826163711-7d8d00c99217
|
||||||
github.com/fatih/structs v1.1.0 // indirect
|
github.com/fatih/structs v1.1.0 // indirect
|
||||||
github.com/google/go-github/v29 v29.0.3
|
github.com/google/go-github/v29 v29.0.3
|
||||||
github.com/spf13/cobra v0.0.6
|
github.com/spf13/cobra v0.0.6
|
||||||
|
|
13
go.sum
13
go.sum
|
@ -1,6 +1,8 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/StevenACoffman/j2m v0.0.0-20190826163711-7d8d00c99217 h1:y2QXqPIec+mEDDToinjdFymzQZmLNRIKtuOkdomqyA4=
|
||||||
|
github.com/StevenACoffman/j2m v0.0.0-20190826163711-7d8d00c99217/go.mod h1:y1vzL6Jab7oLzLLE2CtItTyEI6hKQnNMmqDrO+2a7Pk=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
|
@ -17,8 +19,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
|
||||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
@ -38,7 +38,6 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
|
||||||
github.com/google/go-github/v29 v29.0.3 h1:IktKCTwU//aFHnpA+2SLIi7Oo9uhAzgsdZNbcAqhgdc=
|
github.com/google/go-github/v29 v29.0.3 h1:IktKCTwU//aFHnpA+2SLIi7Oo9uhAzgsdZNbcAqhgdc=
|
||||||
github.com/google/go-github/v29 v29.0.3/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
|
github.com/google/go-github/v29 v29.0.3/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
|
||||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
|
@ -60,11 +59,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
@ -130,9 +124,6 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
|
17
jira/jira.go
17
jira/jira.go
|
@ -56,22 +56,22 @@ func (c *JiraClient) GetIssueFromTicket(ticket *model.Ticket, cmp *model.Campaig
|
||||||
data := map[string]string{
|
data := map[string]string{
|
||||||
"Description": description,
|
"Description": description,
|
||||||
"Summary": summary,
|
"Summary": summary,
|
||||||
"Project": cmp.Project,
|
"Project": cmp.Jira.Project,
|
||||||
"Issue Type": cmp.IssueType,
|
"Issue Type": cmp.Jira.IssueType,
|
||||||
"Epic Link": cmp.Epic,
|
"Epic Link": cmp.Jira.Epic,
|
||||||
}
|
}
|
||||||
|
|
||||||
createMetaInfo, _, err := c.Issue.GetCreateMeta(cmp.Project)
|
createMetaInfo, _, err := c.Issue.GetCreateMeta(cmp.Jira.Project)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
project := createMetaInfo.GetProjectWithKey(cmp.Project)
|
project := createMetaInfo.GetProjectWithKey(cmp.Jira.Project)
|
||||||
if project == nil {
|
if project == nil {
|
||||||
return nil, fmt.Errorf("Error retrieving project with key %s", cmp.Project)
|
return nil, fmt.Errorf("Error retrieving project with key %s", cmp.Jira.Project)
|
||||||
}
|
}
|
||||||
|
|
||||||
issueType := project.GetIssueTypeWithName(cmp.IssueType)
|
issueType := project.GetIssueTypeWithName(cmp.Jira.IssueType)
|
||||||
if issueType == nil {
|
if issueType == nil {
|
||||||
return nil, fmt.Errorf("Error retrieving issue type with name Story")
|
return nil, fmt.Errorf("Error retrieving issue type with name Story")
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,7 @@ func (c *JiraClient) PublishNextTicket(cmp *model.Campaign, dryRun bool) (bool,
|
||||||
|
|
||||||
ticket.JiraLink = issue.Key
|
ticket.JiraLink = issue.Key
|
||||||
ticket.Summary = issue.Fields.Summary
|
ticket.Summary = issue.Fields.Summary
|
||||||
|
ticket.Description = issue.Fields.Description
|
||||||
if err := campaign.Save(cmp); err != nil {
|
if err := campaign.Save(cmp); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -156,7 +157,7 @@ func (c *JiraClient) PublishAll(cmp *model.Campaign, dryRun bool) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *JiraClient) PublishBatch(cmp *model.Campaign, batch int, dryRun bool) error {
|
func (c *JiraClient) PublishBatch(cmp *model.Campaign, batch int, dryRun bool) error {
|
||||||
for i := 0; i <= batch; i++ {
|
for i := 1; i <= batch; i++ {
|
||||||
next, err := c.PublishNextTicket(cmp, dryRun)
|
next, err := c.PublishNextTicket(cmp, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,16 +1,32 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToDo: add key-value extra params as a map to allow for customfield_whatever = team
|
type ConfigJira struct {
|
||||||
type Campaign struct {
|
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Token string `json:"token"`
|
||||||
Project string `json:"project"`
|
Project string `json:"project"`
|
||||||
Epic string `json:"epic"`
|
Epic string `json:"epic"`
|
||||||
IssueType string `json:"issue_type"`
|
IssueType string `json:"issue_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigGithub struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
Repo string `json:"repo"`
|
||||||
|
Labels []string `json:"labels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDo: add key-value extra params as a map to allow for customfield_whatever = team
|
||||||
|
type Campaign struct {
|
||||||
|
Jira ConfigJira `json:"jira"`
|
||||||
|
Github ConfigGithub `json:"github"`
|
||||||
Summary string `json:"summary"`
|
Summary string `json:"summary"`
|
||||||
Template string `json:"template"`
|
Template string `json:"template"`
|
||||||
Tickets []*Ticket `json:"tickets,omitempty"`
|
Tickets []*Ticket `json:"tickets,omitempty"`
|
||||||
|
@ -27,7 +43,7 @@ func (c *Campaign) NextJiraUnpublishedTicket() *Ticket {
|
||||||
|
|
||||||
func (c *Campaign) NextGithubUnpublishedTicket() *Ticket {
|
func (c *Campaign) NextGithubUnpublishedTicket() *Ticket {
|
||||||
for _, ticket := range c.Tickets {
|
for _, ticket := range c.Tickets {
|
||||||
if ticket.JiraLink != "" && ticket.GithubLink != 0 {
|
if ticket.JiraLink != "" && ticket.GithubLink == 0 {
|
||||||
return ticket
|
return ticket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,14 +51,50 @@ func (c *Campaign) NextGithubUnpublishedTicket() *Ticket {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Campaign) PrintStatus(w io.Writer) {
|
func (c *Campaign) PrintStatus(w io.Writer) {
|
||||||
fmt.Fprintf(w, "Url: %s\n", c.Url)
|
fmt.Fprintf(w, "JIRA URL: %s\n", c.Jira.Url)
|
||||||
fmt.Fprintf(w, "Project: %s\n", c.Project)
|
fmt.Fprintf(w, "JIRA Project: %s\n", c.Jira.Project)
|
||||||
fmt.Fprintf(w, "Epic: %s\n", c.Epic)
|
fmt.Fprintf(w, "JIRA Epic: %s\n", c.Jira.Epic)
|
||||||
fmt.Fprintf(w, "Issue Type: %s\n", c.IssueType)
|
fmt.Fprintf(w, "JIRA Issue Type: %s\n", c.Jira.IssueType)
|
||||||
|
fmt.Fprintf(w, "GitHub Repo: %s\n", c.Github.Repo)
|
||||||
|
fmt.Fprintf(w, "GitHub Labels: %s\n", c.Github.Labels)
|
||||||
fmt.Fprintf(w, "Summary: %s\n", c.Summary)
|
fmt.Fprintf(w, "Summary: %s\n", c.Summary)
|
||||||
fmt.Fprintf(w, "Template: %s\n", c.Template)
|
fmt.Fprintf(w, "Template: %s\n", c.Template)
|
||||||
|
fmt.Fprintln(w, "")
|
||||||
|
|
||||||
for _, ticket := range c.Tickets {
|
for _, ticket := range c.Tickets {
|
||||||
ticket.PrintStatus(w)
|
ticket.PrintStatus(w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Campaign) FillTicket(t *Ticket) error {
|
||||||
|
summaryTmpl, err := template.New("").Parse(c.Summary)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var summaryBytes bytes.Buffer
|
||||||
|
if err := summaryTmpl.Execute(&summaryBytes, t.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Summary = summaryBytes.String()
|
||||||
|
|
||||||
|
descriptionTemplate, err := template.ParseFiles(c.Template)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var descriptionBytes bytes.Buffer
|
||||||
|
if err := descriptionTemplate.Execute(&descriptionBytes, t.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Description = descriptionBytes.String()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Campaign) RepoComponents() (string, string) {
|
||||||
|
parts := strings.Split(c.Github.Repo, "/")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
return parts[0], parts[1]
|
||||||
|
}
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
GithubToken string `json:"github_token"`
|
|
||||||
JiraUsername string `json:"jira_username"`
|
|
||||||
JiraToken string `json:"jira_token"`
|
|
||||||
}
|
|
|
@ -6,9 +6,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Ticket struct {
|
type Ticket struct {
|
||||||
GithubLink int64 `json:"githubLink,omitempty"`
|
GithubLink int64 `json:"github_link,omitempty"`
|
||||||
JiraLink string `json:"jiraLink,omitempty"`
|
GithubStatus string `json:"github_status,omitempty"`
|
||||||
|
JiraLink string `json:"jira_link,omitempty"`
|
||||||
|
JiraStatus string `json:"jira_status,omitempty"`
|
||||||
Summary string `json:"summary,omitempty"`
|
Summary string `json:"summary,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
Data map[string]interface{} `json:"data,omitempty"`
|
Data map[string]interface{} `json:"data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,5 +36,7 @@ func RemoveDuplicateTickets(tickets []*Ticket, fileOnly bool) []*Ticket {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Ticket) PrintStatus(w io.Writer) {
|
func (t *Ticket) PrintStatus(w io.Writer) {
|
||||||
fmt.Fprintf(w, " [%s] %s\n", t.JiraLink, t.Summary)
|
if t.Summary != "" {
|
||||||
|
fmt.Fprintf(w, "[%s] %s\n", t.JiraLink, t.Summary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
12
vendor/github.com/StevenACoffman/j2m/.gitignore
generated
vendored
Normal file
12
vendor/github.com/StevenACoffman/j2m/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, build with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
21
vendor/github.com/StevenACoffman/j2m/LICENSE
generated
vendored
Normal file
21
vendor/github.com/StevenACoffman/j2m/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Steve Coffman
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
15
vendor/github.com/StevenACoffman/j2m/Makefile
generated
vendored
Normal file
15
vendor/github.com/StevenACoffman/j2m/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
.DEFAULT_GOAL := easy
|
||||||
|
.PHONY: install clean all easy
|
||||||
|
|
||||||
|
bin/j2m:
|
||||||
|
go build -o bin/j2m cmd/j2m.go
|
||||||
|
|
||||||
|
all: bin/j2m
|
||||||
|
|
||||||
|
install: bin/j2m
|
||||||
|
cp bin/* ~/bin
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f bin/*
|
||||||
|
|
||||||
|
easy: all
|
61
vendor/github.com/StevenACoffman/j2m/README.md
generated
vendored
Normal file
61
vendor/github.com/StevenACoffman/j2m/README.md
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
[![GoDoc](https://godoc.org/github.com/StevenACoffman/j2m?status.svg)](https://godoc.org/github.com/StevenACoffman/j2m)
|
||||||
|
[![GoReportcard](https://goreportcard.com/badge/github.com/StevenACoffman/j2m?status.svg)](https://goreportcard.com/report/github.com/StevenACoffman/j2m)
|
||||||
|
|
||||||
|
# jira-to-md
|
||||||
|
|
||||||
|
## JIRA to MarkDown text format converter
|
||||||
|
Golang tool to convert from JIRA Markdown text formatting to GitHub Flavored MarkDown.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
This fun toy was heavily inspired by the J2M project by Fokke Zandbergen (http://j2m.fokkezb.nl/). Major credit to Fokke, kylefarris (and other contributors) for establishing the RexExp patterns for this to work. The maintained JavaScript fork I based this on is [here](https://github.com/kylefarris/J2M)
|
||||||
|
|
||||||
|
## Supported Conversions
|
||||||
|
|
||||||
|
* Headers (H1-H6)
|
||||||
|
* Bold
|
||||||
|
* Italic
|
||||||
|
* Bold + Italic
|
||||||
|
* Un-ordered lists
|
||||||
|
* Ordered lists
|
||||||
|
* Programming Language-specific code blocks (with help from herbert-venancio)
|
||||||
|
* Inline preformatted text spans
|
||||||
|
* Un-named links
|
||||||
|
* Named links
|
||||||
|
* Monospaced Text
|
||||||
|
* ~~Citations~~ (currently kinda buggy)
|
||||||
|
* Strikethroughs
|
||||||
|
* Inserts
|
||||||
|
* Superscripts
|
||||||
|
* Subscripts
|
||||||
|
* Single-paragraph blockquotes
|
||||||
|
* Tables
|
||||||
|
* Panels
|
||||||
|
|
||||||
|
|
||||||
|
## How to Use
|
||||||
|
|
||||||
|
### Markdown String
|
||||||
|
|
||||||
|
```
|
||||||
|
**Some bold things**
|
||||||
|
*Some italic stuff*
|
||||||
|
## H2
|
||||||
|
<http://google.com>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Atlassian Wiki MarkUp Syntax (JIRA)
|
||||||
|
|
||||||
|
We'll refer to this as the `jira` variable in the examples below.
|
||||||
|
|
||||||
|
```
|
||||||
|
*Some bold things**
|
||||||
|
_Some italic stuff_
|
||||||
|
h2. H2
|
||||||
|
[http://google.com]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
cat j2m.jira | j2m
|
||||||
|
```
|
3
vendor/github.com/StevenACoffman/j2m/go.mod
generated
vendored
Normal file
3
vendor/github.com/StevenACoffman/j2m/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/StevenACoffman/j2m
|
||||||
|
|
||||||
|
go 1.12
|
153
vendor/github.com/StevenACoffman/j2m/j2m.go
generated
vendored
Normal file
153
vendor/github.com/StevenACoffman/j2m/j2m.go
generated
vendored
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
package j2m
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type jiration struct {
|
||||||
|
re *regexp.Regexp
|
||||||
|
repl interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JiraToMD takes a string in Jira Markdown, and outputs Github Markdown
|
||||||
|
func JiraToMD(str string) string {
|
||||||
|
jirations := []jiration{
|
||||||
|
{ // UnOrdered Lists
|
||||||
|
re: regexp.MustCompile(`(?m)^[ \t]*(\*+)\s+`),
|
||||||
|
repl: func(groups []string) string {
|
||||||
|
_, stars := groups[0], groups[1]
|
||||||
|
return strings.Repeat(" ", len(stars)-1) + "* "
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ //Ordered Lists
|
||||||
|
re: regexp.MustCompile(`(?m)^[ \t]*(#+)\s+`),
|
||||||
|
repl: func(groups []string) string {
|
||||||
|
_, nums := groups[0], groups[1]
|
||||||
|
return strings.Repeat(" ", len(nums)-1) + "1. "
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ //Headers 1-6
|
||||||
|
re: regexp.MustCompile(`(?m)^h([0-6])\.(.*)$`),
|
||||||
|
repl: func(groups []string) string {
|
||||||
|
_, level, content := groups[0], groups[1], groups[2]
|
||||||
|
i, _ := strconv.Atoi(level)
|
||||||
|
return strings.Repeat("#", i) + content
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // Bold
|
||||||
|
re: regexp.MustCompile(`\*(\S.*)\*`),
|
||||||
|
repl: "**$1**",
|
||||||
|
},
|
||||||
|
{ // Italic
|
||||||
|
re: regexp.MustCompile(`\_(\S.*)\_`),
|
||||||
|
repl: "*$1*",
|
||||||
|
},
|
||||||
|
{ // Monospaced text
|
||||||
|
re: regexp.MustCompile(`\{\{([^}]+)\}\}`),
|
||||||
|
repl: "`$1`",
|
||||||
|
},
|
||||||
|
{ // Citations (buggy)
|
||||||
|
re: regexp.MustCompile(`\?\?((?:.[^?]|[^?].)+)\?\?`),
|
||||||
|
repl: "<cite>$1</cite>",
|
||||||
|
},
|
||||||
|
{ // Inserts
|
||||||
|
re: regexp.MustCompile(`\+([^+]*)\+`),
|
||||||
|
repl: "<ins>$1</ins>",
|
||||||
|
},
|
||||||
|
{ // Superscript
|
||||||
|
re: regexp.MustCompile(`\^([^^]*)\^`),
|
||||||
|
repl: "<sup>$1</sup>",
|
||||||
|
},
|
||||||
|
{ // Subscript
|
||||||
|
re: regexp.MustCompile(`~([^~]*)~`),
|
||||||
|
repl: "<sub>$1</sub>",
|
||||||
|
},
|
||||||
|
{ // Strikethrough
|
||||||
|
re: regexp.MustCompile(`(\s+)-(\S+.*?\S)-(\s+)`),
|
||||||
|
repl: "$1~~$2~~$3",
|
||||||
|
},
|
||||||
|
{ // Code Block
|
||||||
|
re: regexp.MustCompile(`\{code(:([a-z]+))?([:|]?(title|borderStyle|borderColor|borderWidth|bgColor|titleBGColor)=.+?)*\}`),
|
||||||
|
repl: "```$2",
|
||||||
|
},
|
||||||
|
{ // Code Block End
|
||||||
|
re: regexp.MustCompile(`{code}`),
|
||||||
|
repl: "```",
|
||||||
|
},
|
||||||
|
{ // Pre-formatted text
|
||||||
|
re: regexp.MustCompile(`{noformat}`),
|
||||||
|
repl: "```",
|
||||||
|
},
|
||||||
|
{ // Un-named Links
|
||||||
|
re: regexp.MustCompile(`(?U)\[([^|]+)\]`),
|
||||||
|
repl: "<$1>",
|
||||||
|
},
|
||||||
|
{ // Images
|
||||||
|
re: regexp.MustCompile(`!(.+)!`),
|
||||||
|
repl: "![]($1)",
|
||||||
|
},
|
||||||
|
{ // Named Links
|
||||||
|
re: regexp.MustCompile(`\[(.+?)\|(.+)\]`),
|
||||||
|
repl: "[$1]($2)",
|
||||||
|
},
|
||||||
|
{ // Single Paragraph Blockquote
|
||||||
|
re: regexp.MustCompile(`(?m)^bq\.\s+`),
|
||||||
|
repl: "> ",
|
||||||
|
},
|
||||||
|
{ // Remove color: unsupported in md
|
||||||
|
re: regexp.MustCompile(`(?m)\{color:[^}]+\}(.*)\{color\}`),
|
||||||
|
repl: "$1",
|
||||||
|
},
|
||||||
|
{ // panel into table
|
||||||
|
re: regexp.MustCompile(`(?m)\{panel:title=([^}]*)\}\n?(.*?)\n?\{panel\}`),
|
||||||
|
repl: "\n| $1 |\n| --- |\n| $2 |",
|
||||||
|
},
|
||||||
|
{ //table header
|
||||||
|
re: regexp.MustCompile(`(?m)^[ \t]*((?:\|\|.*?)+\|\|)[ \t]*$`),
|
||||||
|
repl: func(groups []string) string {
|
||||||
|
_, headers := groups[0], groups[1]
|
||||||
|
reBarred := regexp.MustCompile(`\|\|`)
|
||||||
|
|
||||||
|
singleBarred := reBarred.ReplaceAllString(headers, "|")
|
||||||
|
fillerRe := regexp.MustCompile(`\|[^|]+`)
|
||||||
|
return "\n" + singleBarred + "\n" + fillerRe.ReplaceAllString(singleBarred, "| --- ")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // remove leading-space of table headers and rows
|
||||||
|
re: regexp.MustCompile(`(?m)^[ \t]*\|`),
|
||||||
|
repl: "|",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, jiration := range jirations {
|
||||||
|
switch v := jiration.repl.(type) {
|
||||||
|
case string:
|
||||||
|
str = jiration.re.ReplaceAllString(str, v)
|
||||||
|
case func([]string) string:
|
||||||
|
str = replaceAllStringSubmatchFunc(jiration.re, str, v)
|
||||||
|
default:
|
||||||
|
fmt.Printf("I don't know about type %T!\n", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://gist.github.com/elliotchance/d419395aa776d632d897
|
||||||
|
func replaceAllStringSubmatchFunc(re *regexp.Regexp, str string, repl func([]string) string) string {
|
||||||
|
result := ""
|
||||||
|
lastIndex := 0
|
||||||
|
|
||||||
|
for _, v := range re.FindAllSubmatchIndex([]byte(str), -1) {
|
||||||
|
groups := []string{}
|
||||||
|
for i := 0; i < len(v); i += 2 {
|
||||||
|
groups = append(groups, str[v[i]:v[i+1]])
|
||||||
|
}
|
||||||
|
|
||||||
|
result += str[lastIndex:v[0]] + repl(groups)
|
||||||
|
lastIndex = v[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return result + str[lastIndex:]
|
||||||
|
}
|
65
vendor/github.com/StevenACoffman/j2m/j2m.jira
generated
vendored
Normal file
65
vendor/github.com/StevenACoffman/j2m/j2m.jira
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
h1. Biggest heading
|
||||||
|
|
||||||
|
h2. Bigger heading
|
||||||
|
|
||||||
|
h1. Biggest heading
|
||||||
|
h2. Bigger heading
|
||||||
|
h3. Big heading
|
||||||
|
h4. Normal heading
|
||||||
|
h5. Small heading
|
||||||
|
h6. Smallest heading
|
||||||
|
|
||||||
|
*strong*
|
||||||
|
_emphasis_
|
||||||
|
{{monospaced}}
|
||||||
|
-deleted-
|
||||||
|
+inserted+
|
||||||
|
^superscript^
|
||||||
|
~subscript~
|
||||||
|
|
||||||
|
{code:javascript}
|
||||||
|
var hello = 'world';
|
||||||
|
{code}
|
||||||
|
|
||||||
|
!http://google.com/image!
|
||||||
|
[!http://google.com/image!|http://google.com/link]
|
||||||
|
|
||||||
|
[http://google.com]
|
||||||
|
[Google|http://google.com]
|
||||||
|
|
||||||
|
GitHub Flavor
|
||||||
|
-deleted-
|
||||||
|
|
||||||
|
{code}
|
||||||
|
preformatted piece of text
|
||||||
|
so _no_ further _formatting_ is done here
|
||||||
|
{code}
|
||||||
|
|
||||||
|
_*Should be bold AND italic*_
|
||||||
|
|
||||||
|
* First li
|
||||||
|
* Second li
|
||||||
|
** Indented li
|
||||||
|
*** Three columns in li
|
||||||
|
* Back to first level li
|
||||||
|
|
||||||
|
# First li
|
||||||
|
# Second li
|
||||||
|
## Indented li
|
||||||
|
### Three columns in li
|
||||||
|
# Back to first level li
|
||||||
|
|
||||||
|
* Here's _italic_ inside li
|
||||||
|
* here's *bold* inside li
|
||||||
|
* Here's _*bold + italic*_ inside li
|
||||||
|
** Here they are in one line indented: _italic_ *bold*
|
||||||
|
|
||||||
|
bq. Here's a long single-paragraph block quote. It should look pretty and stuff.
|
||||||
|
|
||||||
|
{panel:title=A title}
|
||||||
|
Panel text
|
||||||
|
{panel}
|
||||||
|
|
||||||
|
||Heading 1||Heading 2||
|
||||||
|
|Col A1|Col A2|
|
||||||
|
|Col B1|Col B2|
|
68
vendor/github.com/StevenACoffman/j2m/j2m.md
generated
vendored
Normal file
68
vendor/github.com/StevenACoffman/j2m/j2m.md
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
# Biggest heading
|
||||||
|
|
||||||
|
## Bigger heading
|
||||||
|
|
||||||
|
# Biggest heading
|
||||||
|
## Bigger heading
|
||||||
|
### Big heading
|
||||||
|
#### Normal heading
|
||||||
|
##### Small heading
|
||||||
|
###### Smallest heading
|
||||||
|
|
||||||
|
**strong**
|
||||||
|
*emphasis*
|
||||||
|
`monospaced`
|
||||||
|
~~deleted~~
|
||||||
|
<ins>inserted</ins>
|
||||||
|
<sup>superscript</sup>
|
||||||
|
<sub>subscript</sub>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var hello = 'world';
|
||||||
|
```
|
||||||
|
|
||||||
|
![](http://google.com/image)
|
||||||
|
[![](http://google.com/image)](http://google.com/link)
|
||||||
|
|
||||||
|
<http://google.com>
|
||||||
|
[Google](http://google.com)
|
||||||
|
|
||||||
|
GitHub Flavor
|
||||||
|
~~deleted~~
|
||||||
|
|
||||||
|
```
|
||||||
|
preformatted piece of text
|
||||||
|
so *no_ further _formatting* is done here
|
||||||
|
```
|
||||||
|
|
||||||
|
***Should be bold AND italic***
|
||||||
|
|
||||||
|
* First li
|
||||||
|
* Second li
|
||||||
|
* Indented li
|
||||||
|
* Three columns in li
|
||||||
|
* Back to first level li
|
||||||
|
|
||||||
|
1. First li
|
||||||
|
1. Second li
|
||||||
|
1. Indented li
|
||||||
|
1. Three columns in li
|
||||||
|
1. Back to first level li
|
||||||
|
|
||||||
|
* Here's *italic* inside li
|
||||||
|
* here's **bold** inside li
|
||||||
|
* Here's ***bold + italic*** inside li
|
||||||
|
* Here they are in one line indented: *italic* **bold**
|
||||||
|
|
||||||
|
> Here's a long single-paragraph block quote. It should look pretty and stuff.
|
||||||
|
|
||||||
|
|
||||||
|
| A title |
|
||||||
|
| --- |
|
||||||
|
| Panel text |
|
||||||
|
|
||||||
|
|
||||||
|
|Heading 1|Heading 2|
|
||||||
|
| --- | --- |
|
||||||
|
|Col A1|Col A2|
|
||||||
|
|Col B1|Col B2|
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
|
@ -1,3 +1,5 @@
|
||||||
|
# github.com/StevenACoffman/j2m v0.0.0-20190826163711-7d8d00c99217
|
||||||
|
github.com/StevenACoffman/j2m
|
||||||
# github.com/fatih/structs v1.1.0
|
# github.com/fatih/structs v1.1.0
|
||||||
github.com/fatih/structs
|
github.com/fatih/structs
|
||||||
# github.com/golang/protobuf v1.3.2
|
# github.com/golang/protobuf v1.3.2
|
||||||
|
|
Loading…
Reference in a new issue