Copy to clipboard for `create`, `issue`, `pull-request`, `release`

Also add `--browse` functionality to `create` and `release` for parity
with `issue` and `pull-request`.
This commit is contained in:
Mislav Marohnić 2016-09-11 18:21:23 +02:00
Родитель 6fdc4c1a88
Коммит f7c1105df3
13 изменённых файлов: 411 добавлений и 11 удалений

Просмотреть файл

@ -13,7 +13,7 @@ import (
var cmdCreate = &Command{
Run: create,
Usage: "create [-p] [-d <DESCRIPTION>] [-h <HOMEPAGE>] [[<ORGANIZATION>/]<NAME>]",
Usage: "create [-poc] [-d <DESCRIPTION>] [-h <HOMEPAGE>] [[<ORGANIZATION>/]<NAME>]",
Long: `Create a new repository on GitHub and add a git remote for it.
## Options:
@ -26,6 +26,12 @@ var cmdCreate = &Command{
-h <HOMEPAGE>
Use this text as the URL of the GitHub repository.
-o, --browse
Open the new repository in a web browser.
-c, --copy
Put the URL of the new repository to clipboard instead of printing it.
[<ORGANIZATION>/]<NAME>
The name for the repository on GitHub (default: name of the current working
directory).
@ -48,12 +54,18 @@ hub-init(1), hub(1)
}
var (
flagCreatePrivate bool
flagCreateDescription, flagCreateHomepage string
flagCreatePrivate,
flagCreateBrowse,
flagCreateCopy bool
flagCreateDescription,
flagCreateHomepage string
)
func init() {
cmdCreate.Flag.BoolVarP(&flagCreatePrivate, "private", "p", false, "PRIVATE")
cmdCreate.Flag.BoolVarP(&flagCreateBrowse, "browse", "o", false, "BROWSE")
cmdCreate.Flag.BoolVarP(&flagCreateCopy, "copy", "c", false, "COPY")
cmdCreate.Flag.StringVarP(&flagCreateDescription, "description", "d", "", "DESCRIPTION")
cmdCreate.Flag.StringVarP(&flagCreateHomepage, "homepage", "h", "", "HOMEPAGE")
@ -118,5 +130,5 @@ func create(command *Command, args *Args) {
webUrl := project.WebURL("", "", "")
args.NoForward()
printBrowseOrCopy(args, webUrl, false, false)
printBrowseOrCopy(args, webUrl, flagCreateBrowse, flagCreateCopy)
}

Просмотреть файл

@ -18,7 +18,7 @@ var (
Run: listIssues,
Usage: `
issue [-a <ASSIGNEE>] [-c <CREATOR>] [-@ <USER] [-s <STATE>] [-f <FORMAT>] [-M <MILESTONE>] [-l <LABELS>] [-t <TIME>]
issue create [-o] [-m <MESSAGE>|-F <FILE>] [-a <USERS>] [-M <MILESTONE>] [-l <LABELS>]
issue create [-oc] [-m <MESSAGE>|-F <FILE>] [-a <USERS>] [-M <MILESTONE>] [-l <LABELS>]
`,
Long: `Manage GitHub issues for the current project.
@ -109,6 +109,9 @@ With no arguments, show a list of open issues.
-o, --browse
Open the new issue in a web browser.
-c, --copy
Put the URL of the new issue to clipboard instead of printing it.
-M, --milestone <ID>
Display only issues for a GitHub milestone with id <ID>.
@ -143,6 +146,7 @@ With no arguments, show a list of open issues.
flagIssueFile string
flagIssueEdit,
flagIssueCopy,
flagIssueBrowse bool
flagIssueMilestone uint64
@ -158,6 +162,7 @@ func init() {
cmdCreateIssue.Flag.VarP(&flagIssueLabels, "label", "l", "LABEL")
cmdCreateIssue.Flag.VarP(&flagIssueAssignees, "assign", "a", "ASSIGNEE")
cmdCreateIssue.Flag.BoolVarP(&flagIssueBrowse, "browse", "o", false, "BROWSE")
cmdCreateIssue.Flag.BoolVarP(&flagIssueCopy, "copy", "c", false, "COPY")
cmdCreateIssue.Flag.BoolVarP(&flagIssueEdit, "edit", "e", false, "EDIT")
cmdIssue.Flag.StringVarP(&flagIssueAssignee, "assignee", "a", "", "ASSIGNEE")
@ -393,6 +398,6 @@ func createIssue(cmd *Command, args *Args) {
issue, err := gh.CreateIssue(project, params)
utils.Check(err)
printBrowseOrCopy(args, issue.HtmlUrl, flagIssueBrowse, false)
printBrowseOrCopy(args, issue.HtmlUrl, flagIssueBrowse, flagIssueCopy)
}
}

Просмотреть файл

@ -15,7 +15,7 @@ import (
var cmdPullRequest = &Command{
Run: pullRequest,
Usage: `
pull-request [-fo] [-b <BASE>] [-h <HEAD>] [-a <USERS>] [-M <MILESTONE>] [-l <LABELS>]
pull-request [-foc] [-b <BASE>] [-h <HEAD>] [-a <USERS>] [-M <MILESTONE>] [-l <LABELS>]
pull-request -m <MESSAGE>
pull-request -F <FILE> [--edit]
pull-request -i <ISSUE>
@ -42,6 +42,9 @@ pull-request -i <ISSUE>
-o, --browse
Open the new pull request in a web browser.
-c, --copy
Put the URL of the new pull request to clipboard instead of printing it.
-b, --base <BASE>
The base branch in "[OWNER:]BRANCH" format. Defaults to the default branch
(usually "master").
@ -72,6 +75,7 @@ var (
flagPullRequestFile string
flagPullRequestBrowse,
flagPullRequestCopy,
flagPullRequestEdit,
flagPullRequestForce bool
@ -86,6 +90,7 @@ func init() {
cmdPullRequest.Flag.StringVarP(&flagPullRequestHead, "head", "h", "", "HEAD")
cmdPullRequest.Flag.StringVarP(&flagPullRequestIssue, "issue", "i", "", "ISSUE")
cmdPullRequest.Flag.BoolVarP(&flagPullRequestBrowse, "browse", "o", false, "BROWSE")
cmdPullRequest.Flag.BoolVarP(&flagPullRequestCopy, "copy", "c", false, "COPY")
cmdPullRequest.Flag.StringVarP(&flagPullRequestMessage, "message", "m", "", "MESSAGE")
cmdPullRequest.Flag.BoolVarP(&flagPullRequestEdit, "edit", "e", false, "EDIT")
cmdPullRequest.Flag.BoolVarP(&flagPullRequestForce, "force", "f", false, "FORCE")
@ -268,7 +273,7 @@ func pullRequest(cmd *Command, args *Args) {
}
args.NoForward()
printBrowseOrCopy(args, pullRequestURL, flagPullRequestBrowse, false)
printBrowseOrCopy(args, pullRequestURL, flagPullRequestBrowse, flagPullRequestCopy)
}
func createPullRequestMessage(base, head, fullBase, fullHead string) (string, error) {

Просмотреть файл

@ -19,7 +19,7 @@ var (
Usage: `
release [--include-drafts]
release show <TAG>
release create [-dp] [-a <FILE>] [-m <MESSAGE>|-F <FILE>] [-t <TARGET>] <TAG>
release create [-dpoc] [-a <FILE>] [-m <MESSAGE>|-F <FILE>] [-t <TARGET>] <TAG>
release edit [<options>] <TAG>
`,
Long: `Manage GitHub releases.
@ -72,6 +72,12 @@ With '--include-drafts', include draft releases in the listing.
-e, --edit
Further edit the contents of <FILE> in a text editor before submitting.
-o, --browse
Open the new release in a web browser.
-c, --copy
Put the URL of the new release to clipboard instead of printing it.
-t, --commitish <TARGET>
A commit SHA or branch name to attach the release to, only used if <TAG>
doesn't already exist (default: main branch).
@ -109,6 +115,8 @@ hub(1), git-tag(1)
flagReleaseShowDownloads,
flagReleaseDraft,
flagReleaseEdit,
flagReleaseBrowse,
flagReleaseCopy,
flagReleasePrerelease bool
flagReleaseMessage,
@ -126,6 +134,8 @@ func init() {
cmdCreateRelease.Flag.BoolVarP(&flagReleaseEdit, "edit", "e", false, "EDIT")
cmdCreateRelease.Flag.BoolVarP(&flagReleaseDraft, "draft", "d", false, "DRAFT")
cmdCreateRelease.Flag.BoolVarP(&flagReleasePrerelease, "prerelease", "p", false, "PRERELEASE")
cmdCreateRelease.Flag.BoolVarP(&flagReleaseBrowse, "browse", "o", false, "BROWSE")
cmdCreateRelease.Flag.BoolVarP(&flagReleaseCopy, "copy", "c", false, "COPY")
cmdCreateRelease.Flag.VarP(&flagReleaseAssets, "attach", "a", "ATTACH_ASSETS")
cmdCreateRelease.Flag.StringVarP(&flagReleaseMessage, "message", "m", "", "MESSAGE")
cmdCreateRelease.Flag.StringVarP(&flagReleaseFile, "file", "F", "", "FILE")
@ -316,7 +326,7 @@ func createRelease(cmd *Command, args *Args) {
release, err = gh.CreateRelease(project, params)
utils.Check(err)
printBrowseOrCopy(args, release.HtmlUrl, false, false)
printBrowseOrCopy(args, release.HtmlUrl, flagReleaseBrowse, flagReleaseCopy)
}
if editor != nil {

Просмотреть файл

@ -7,6 +7,7 @@ import (
"path/filepath"
"strings"
"github.com/atotto/clipboard"
"github.com/github/hub/git"
"github.com/github/hub/github"
"github.com/github/hub/ui"
@ -137,7 +138,9 @@ func runInLocalRepo(fn func(localRepo *github.GitHubRepo, project *github.Projec
func printBrowseOrCopy(args *Args, msg string, openBrowser bool, performCopy bool) {
if performCopy {
// TODO
if err := clipboard.WriteAll(msg); err != nil {
ui.Errorf("Error copying %s to clipboard:\n%s\n", msg, err.Error())
}
}
if openBrowser {

Просмотреть файл

@ -129,6 +129,17 @@ Feature: hub create
Then the url for "origin" should be "git@github.com:Mooslav/myconfig.git"
And the output should contain exactly "https://github.com/Mooslav/myconfig\n"
Scenario: Open new repository in web browser
Given the GitHub API server:
"""
post('/user/repos') {
json :full_name => 'Mooslav/myconfig'
}
"""
When I successfully run `hub create -o`
Then the output should contain exactly ""
And "open https://github.com/Mooslav/myconfig" should be run
Scenario: Current directory contains spaces
Given I am in "my dot files" git repo
Given the GitHub API server:

Просмотреть файл

@ -237,6 +237,18 @@ MARKDOWN
Attaching release asset `./hello-1.2.0.tar.gz'...\n
"""
Scenario: Open new release in web browser
Given the GitHub API server:
"""
post('/repos/mislav/will_paginate/releases') {
status 201
json :html_url => "https://github.com/mislav/will_paginate/releases/v1.2.0"
}
"""
When I successfully run `hub release create -o -m hello v1.2.0`
Then the output should contain exactly ""
And "open https://github.com/mislav/will_paginate/releases/v1.2.0" should be run
Scenario: Edit existing release
Given the GitHub API server:
"""

27
vendor/github.com/atotto/clipboard/LICENSE сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,27 @@
Copyright (c) 2013 Ato Araki. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of @atotto. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

50
vendor/github.com/atotto/clipboard/README.md сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,50 @@
[![Build Status](https://travis-ci.org/atotto/clipboard.svg?branch=master)](https://travis-ci.org/atotto/clipboard) [![Build Status](https://drone.io/github.com/atotto/clipboard/status.png)](https://drone.io/github.com/atotto/clipboard/latest)
[![GoDoc](https://godoc.org/github.com/atotto/clipboard?status.svg)](http://godoc.org/github.com/atotto/clipboard)
# Clipboard for Go
Provide copying and pasting to the Clipboard for Go.
Download shell commands at https://drone.io/github.com/atotto/clipboard/files
Build:
$ go get github.com/atotto/clipboard
Platforms:
* OSX
* Windows 7 (probably work on other Windows)
* Linux, Unix (requires 'xclip' or 'xsel' command to be installed)
Document:
* http://godoc.org/github.com/atotto/clipboard
Notes:
* Text string only
* UTF-8 text encoding only (no conversion)
TODO:
* Clipboard watcher(?)
## Commands:
paste shell command:
$ go get github.com/atotto/clipboard/cmd/gopaste
$ # example:
$ gopaste > document.txt
copy shell command:
$ go get github.com/atotto/clipboard/cmd/gocopy
$ # example:
$ cat document.txt | gocopy

22
vendor/github.com/atotto/clipboard/clipboard.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,22 @@
// Copyright 2013 @atotto. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package clipboard read/write on clipboard
package clipboard
import ()
// ReadAll read string from clipboard
func ReadAll() (string, error) {
return readAll()
}
// WriteAll write string to clipboard
func WriteAll(text string) error {
return writeAll(text)
}
// Unsupported might be set true during clipboard init, to help callers decide
// whether or not to offer clipboard options.
var Unsupported bool

52
vendor/github.com/atotto/clipboard/clipboard_darwin.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,52 @@
// Copyright 2013 @atotto. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin
package clipboard
import (
"os/exec"
)
var (
pasteCmdArgs = "pbpaste"
copyCmdArgs = "pbcopy"
)
func getPasteCommand() *exec.Cmd {
return exec.Command(pasteCmdArgs)
}
func getCopyCommand() *exec.Cmd {
return exec.Command(copyCmdArgs)
}
func readAll() (string, error) {
pasteCmd := getPasteCommand()
out, err := pasteCmd.Output()
if err != nil {
return "", err
}
return string(out), nil
}
func writeAll(text string) error {
copyCmd := getCopyCommand()
in, err := copyCmd.StdinPipe()
if err != nil {
return err
}
if err := copyCmd.Start(); err != nil {
return err
}
if _, err := in.Write([]byte(text)); err != nil {
return err
}
if err := in.Close(); err != nil {
return err
}
return copyCmd.Wait()
}

90
vendor/github.com/atotto/clipboard/clipboard_unix.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,90 @@
// Copyright 2013 @atotto. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build freebsd linux netbsd openbsd solaris
package clipboard
import (
"errors"
"os/exec"
)
const (
xsel = "xsel"
xclip = "xclip"
)
var (
pasteCmdArgs []string
copyCmdArgs []string
xselPasteArgs = []string{xsel, "--output", "--clipboard"}
xselCopyArgs = []string{xsel, "--input", "--clipboard"}
xclipPasteArgs = []string{xclip, "-out", "-selection", "clipboard"}
xclipCopyArgs = []string{xclip, "-in", "-selection", "clipboard"}
missingCommands = errors.New("No clipboard utilities available. Please install xsel or xclip.")
)
func init() {
pasteCmdArgs = xclipPasteArgs
copyCmdArgs = xclipCopyArgs
if _, err := exec.LookPath(xclip); err == nil {
return
}
pasteCmdArgs = xselPasteArgs
copyCmdArgs = xselCopyArgs
if _, err := exec.LookPath(xsel); err == nil {
return
}
Unsupported = true
}
func getPasteCommand() *exec.Cmd {
return exec.Command(pasteCmdArgs[0], pasteCmdArgs[1:]...)
}
func getCopyCommand() *exec.Cmd {
return exec.Command(copyCmdArgs[0], copyCmdArgs[1:]...)
}
func readAll() (string, error) {
if Unsupported {
return "", missingCommands
}
pasteCmd := getPasteCommand()
out, err := pasteCmd.Output()
if err != nil {
return "", err
}
return string(out), nil
}
func writeAll(text string) error {
if Unsupported {
return missingCommands
}
copyCmd := getCopyCommand()
in, err := copyCmd.StdinPipe()
if err != nil {
return err
}
if err := copyCmd.Start(); err != nil {
return err
}
if _, err := in.Write([]byte(text)); err != nil {
return err
}
if err := in.Close(); err != nil {
return err
}
return copyCmd.Wait()
}

101
vendor/github.com/atotto/clipboard/clipboard_windows.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,101 @@
// Copyright 2013 @atotto. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows
package clipboard
import (
"syscall"
"unsafe"
)
const (
cfUnicodetext = 13
gmemFixed = 0x0000
)
var (
user32 = syscall.MustLoadDLL("user32")
openClipboard = user32.MustFindProc("OpenClipboard")
closeClipboard = user32.MustFindProc("CloseClipboard")
emptyClipboard = user32.MustFindProc("EmptyClipboard")
getClipboardData = user32.MustFindProc("GetClipboardData")
setClipboardData = user32.MustFindProc("SetClipboardData")
kernel32 = syscall.NewLazyDLL("kernel32")
globalAlloc = kernel32.NewProc("GlobalAlloc")
globalFree = kernel32.NewProc("GlobalFree")
globalLock = kernel32.NewProc("GlobalLock")
globalUnlock = kernel32.NewProc("GlobalUnlock")
lstrcpy = kernel32.NewProc("lstrcpyW")
)
func readAll() (string, error) {
r, _, err := openClipboard.Call(0)
if r == 0 {
return "", err
}
defer closeClipboard.Call()
h, _, err := getClipboardData.Call(cfUnicodetext)
if r == 0 {
return "", err
}
l, _, err := globalLock.Call(h)
if l == 0 {
return "", err
}
text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:])
r, _, err = globalUnlock.Call(h)
if r == 0 {
return "", err
}
return text, nil
}
func writeAll(text string) error {
r, _, err := openClipboard.Call(0)
if r == 0 {
return err
}
defer closeClipboard.Call()
r, _, err = emptyClipboard.Call(0)
if r == 0 {
return err
}
data := syscall.StringToUTF16(text)
h, _, err := globalAlloc.Call(gmemFixed, uintptr(len(data)*int(unsafe.Sizeof(data[0]))))
if h == 0 {
return err
}
l, _, err := globalLock.Call(h)
if l == 0 {
return err
}
r, _, err = lstrcpy.Call(l, uintptr(unsafe.Pointer(&data[0])))
if r == 0 {
return err
}
r, _, err = globalUnlock.Call(h)
if r == 0 {
return err
}
r, _, err = setClipboardData.Call(cfUnicodetext, h)
if r == 0 {
return err
}
return nil
}