Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
github
hub
提交
a6318c02
H
hub
项目概览
github
/
hub
10 个月 前同步成功
通知
3
Star
22523
Fork
2406
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
H
hub
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
a6318c02
编写于
10月 31, 2018
作者:
M
Mislav Marohnić
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'origin/master' into bugfix/remote-ssh-protocol-not-own-repo
上级
6c38dffd
57fda944
变更
23
隐藏空白更改
内联
并排
Showing
23 changed file
with
664 addition
and
54 deletion
+664
-54
commands/ci_status.go
commands/ci_status.go
+1
-3
commands/compare.go
commands/compare.go
+3
-3
commands/fork.go
commands/fork.go
+3
-0
commands/issue.go
commands/issue.go
+83
-0
commands/pr.go
commands/pr.go
+4
-0
commands/pull_request.go
commands/pull_request.go
+2
-1
commands/release.go
commands/release.go
+12
-4
features/compare.feature
features/compare.feature
+7
-0
features/issue.feature
features/issue.feature
+124
-2
features/pr-list.feature
features/pr-list.feature
+50
-1
features/pull_request.feature
features/pull_request.feature
+20
-16
features/release.feature
features/release.feature
+37
-0
fixtures/test_configs.go
fixtures/test_configs.go
+30
-0
github/client.go
github/client.go
+45
-2
github/config.go
github/config.go
+2
-0
github/config_decoder.go
github/config_decoder.go
+1
-0
github/config_encoder.go
github/config_encoder.go
+1
-0
github/config_service_test.go
github/config_service_test.go
+103
-0
github/editor.go
github/editor.go
+22
-13
github/http.go
github/http.go
+19
-4
github/http_test.go
github/http_test.go
+32
-4
utils/utils.go
utils/utils.go
+3
-1
utils/utils_test.go
utils/utils_test.go
+60
-0
未找到文件。
commands/ci_status.go
浏览文件 @
a6318c02
...
...
@@ -88,15 +88,13 @@ func ciStatus(cmd *Command, args *Args) {
response
,
err
:=
gh
.
FetchCIStatus
(
project
,
sha
)
utils
.
Check
(
err
)
state
:=
response
.
State
state
:=
""
if
len
(
response
.
Statuses
)
>
0
{
for
_
,
status
:=
range
response
.
Statuses
{
if
checkSeverity
(
status
.
State
)
>
checkSeverity
(
state
)
{
state
=
status
.
State
}
}
}
else
if
len
(
response
.
Statuses
)
==
0
{
state
=
""
}
var
exitCode
int
...
...
commands/compare.go
浏览文件 @
a6318c02
...
...
@@ -72,14 +72,14 @@ func compare(command *Command, args *Args) {
r
string
)
branch
,
project
,
err
=
localRepo
.
RemoteBranchAndProject
(
""
,
false
)
utils
.
Check
(
err
)
usageHelp
:=
func
()
{
utils
.
Check
(
fmt
.
Errorf
(
"Usage: hub compare [-u] [-b <BASE>] [<USER>] [[<START>...]<END>]"
))
}
if
args
.
IsParamsEmpty
()
{
branch
,
project
,
err
=
localRepo
.
RemoteBranchAndProject
(
""
,
false
)
utils
.
Check
(
err
)
if
branch
==
nil
||
(
branch
.
IsMaster
()
&&
flagCompareBase
==
""
)
||
(
flagCompareBase
==
branch
.
ShortName
())
{
...
...
commands/fork.go
浏览文件 @
a6318c02
...
...
@@ -17,6 +17,9 @@ var cmdFork = &Command{
--no-remote
Skip adding a git remote for the fork.
--remote-name=<REMOTE>
Set the name for the new git remote.
--org=<ORGANIZATION>
Fork the repository within this organization.
...
...
commands/issue.go
浏览文件 @
a6318c02
...
...
@@ -18,6 +18,7 @@ var (
Run
:
listIssues
,
Usage
:
`
issue [-a <ASSIGNEE>] [-c <CREATOR>] [-@ <USER>] [-s <STATE>] [-f <FORMAT>] [-M <MILESTONE>] [-l <LABELS>] [-d <DATE>] [-o <SORT_KEY> [-^]] [-L <LIMIT>]
issue show [-f <FORMAT>] <NUMBER>
issue create [-oc] [-m <MESSAGE>|-F <FILE>] [--edit] [-a <USERS>] [-M <MILESTONE>] [-l <LABELS>]
issue labels [--color]
`
,
...
...
@@ -27,6 +28,9 @@ issue labels [--color]
With no arguments, show a list of open issues.
* _show_:
Show an existing issue specified by <NUMBER>.
* _create_:
Open an issue in the current project.
...
...
@@ -153,6 +157,13 @@ With no arguments, show a list of open issues.
Long
:
"Open an issue in the current project."
,
}
cmdShowIssue
=
&
Command
{
Key
:
"show"
,
Run
:
showIssue
,
Usage
:
"issue show <NUMBER>"
,
Long
:
"Show an issue in the current project."
,
}
cmdLabel
=
&
Command
{
Key
:
"labels"
,
Run
:
listLabels
,
...
...
@@ -163,6 +174,7 @@ With no arguments, show a list of open issues.
flagIssueAssignee
,
flagIssueState
,
flagIssueFormat
,
flagShowIssueFormat
,
flagIssueMessage
,
flagIssueMilestoneFilter
,
flagIssueCreator
,
...
...
@@ -189,6 +201,8 @@ With no arguments, show a list of open issues.
)
func
init
()
{
cmdShowIssue
.
Flag
.
StringVarP
(
&
flagShowIssueFormat
,
"format"
,
"f"
,
""
,
"FORMAT"
)
cmdCreateIssue
.
Flag
.
StringVarP
(
&
flagIssueMessage
,
"message"
,
"m"
,
""
,
"MESSAGE"
)
cmdCreateIssue
.
Flag
.
StringVarP
(
&
flagIssueFile
,
"file"
,
"F"
,
""
,
"FILE"
)
cmdCreateIssue
.
Flag
.
Uint64VarP
(
&
flagIssueMilestone
,
"milestone"
,
"M"
,
0
,
"MILESTONE"
)
...
...
@@ -213,6 +227,7 @@ func init() {
cmdLabel
.
Flag
.
BoolVarP
(
&
flagLabelsColorize
,
"color"
,
""
,
false
,
"COLORIZE"
)
cmdIssue
.
Use
(
cmdShowIssue
)
cmdIssue
.
Use
(
cmdCreateIssue
)
cmdIssue
.
Use
(
cmdLabel
)
CmdRunner
.
Use
(
cmdIssue
)
...
...
@@ -248,6 +263,8 @@ func listIssues(cmd *Command, args *Args) {
if
flagIssueSortAscending
{
filters
[
"direction"
]
=
"asc"
}
else
{
filters
[
"direction"
]
=
"desc"
}
if
cmd
.
FlagPassed
(
"since"
)
{
...
...
@@ -310,6 +327,15 @@ func formatIssuePlaceholders(issue github.Issue, colorize bool) map[string]strin
assignees
=
append
(
assignees
,
assignee
.
Login
)
}
var
requestedReviewers
[]
string
for
_
,
requestedReviewer
:=
range
issue
.
RequestedReviewers
{
requestedReviewers
=
append
(
requestedReviewers
,
requestedReviewer
.
Login
)
}
for
_
,
requestedTeam
:=
range
issue
.
RequestedTeams
{
teamSlug
:=
fmt
.
Sprintf
(
"%s/%s"
,
issue
.
Base
.
Repo
.
Owner
.
Login
,
requestedTeam
.
Slug
)
requestedReviewers
=
append
(
requestedReviewers
,
teamSlug
)
}
var
milestoneNumber
,
milestoneTitle
string
if
issue
.
Milestone
!=
nil
{
milestoneNumber
=
fmt
.
Sprintf
(
"%d"
,
issue
.
Milestone
.
Number
)
...
...
@@ -349,6 +375,7 @@ func formatIssuePlaceholders(issue github.Issue, colorize bool) map[string]strin
"b"
:
issue
.
Body
,
"au"
:
issue
.
User
.
Login
,
"as"
:
strings
.
Join
(
assignees
,
", "
),
"rs"
:
strings
.
Join
(
requestedReviewers
,
", "
),
"Mn"
:
milestoneNumber
,
"Mt"
:
milestoneTitle
,
"NC"
:
numComments
,
...
...
@@ -369,6 +396,62 @@ func formatIssue(issue github.Issue, format string, colorize bool) string {
return
ui
.
Expand
(
format
,
placeholders
,
colorize
)
}
func
showIssue
(
cmd
*
Command
,
args
*
Args
)
{
issueNumber
:=
cmd
.
Arg
(
0
)
if
issueNumber
==
""
{
utils
.
Check
(
fmt
.
Errorf
(
cmd
.
Synopsis
()))
}
localRepo
,
err
:=
github
.
LocalRepo
()
utils
.
Check
(
err
)
project
,
err
:=
localRepo
.
MainProject
()
utils
.
Check
(
err
)
gh
:=
github
.
NewClient
(
project
.
Host
)
var
issue
=
&
github
.
Issue
{}
issue
,
err
=
gh
.
FetchIssue
(
project
,
issueNumber
)
utils
.
Check
(
err
)
args
.
NoForward
()
colorize
:=
ui
.
IsTerminal
(
os
.
Stdout
)
if
flagShowIssueFormat
!=
""
{
ui
.
Printf
(
formatIssue
(
*
issue
,
flagShowIssueFormat
,
colorize
))
return
}
var
closed
=
""
if
issue
.
State
!=
"open"
{
closed
=
"[CLOSED] "
}
commentsList
,
err
:=
gh
.
FetchComments
(
project
,
issueNumber
)
utils
.
Check
(
err
)
ui
.
Printf
(
"# %s%s
\n\n
"
,
closed
,
issue
.
Title
)
ui
.
Printf
(
"* created by @%s on %s
\n
"
,
issue
.
User
.
Login
,
issue
.
CreatedAt
.
String
())
if
len
(
issue
.
Assignees
)
>
0
{
var
assignees
[]
string
for
_
,
user
:=
range
issue
.
Assignees
{
assignees
=
append
(
assignees
,
user
.
Login
)
}
ui
.
Printf
(
"* assignees: %s
\n
"
,
strings
.
Join
(
assignees
,
", "
))
}
ui
.
Printf
(
"
\n
%s
\n
"
,
issue
.
Body
)
if
issue
.
Comments
>
0
{
ui
.
Printf
(
"
\n
## Comments:
\n
"
)
for
_
,
comment
:=
range
commentsList
{
ui
.
Printf
(
"
\n
### comment by @%s on %s
\n\n
%s
\n
"
,
comment
.
User
.
Login
,
comment
.
CreatedAt
.
String
(),
comment
.
Body
)
}
}
return
}
func
createIssue
(
cmd
*
Command
,
args
*
Args
)
{
localRepo
,
err
:=
github
.
LocalRepo
()
utils
.
Check
(
err
)
...
...
commands/pr.go
浏览文件 @
a6318c02
...
...
@@ -72,6 +72,8 @@ pr checkout <PR-NUMBER> [<BRANCH>]
%as: comma-separated list of assignees
%rs: comma-separated list of requested reviewers
%Mn: milestone number
%Mt: milestone title
...
...
@@ -182,6 +184,8 @@ func listPulls(cmd *Command, args *Args) {
}
if
flagPullRequestSortAscending
{
filters
[
"direction"
]
=
"asc"
}
else
{
filters
[
"direction"
]
=
"desc"
}
pulls
,
err
:=
gh
.
FetchPullRequests
(
project
,
filters
,
flagPullRequestLimit
,
nil
)
...
...
commands/pull_request.go
浏览文件 @
a6318c02
...
...
@@ -72,7 +72,8 @@ pull-request -i <ISSUE>
is deprecated.
-l, --labels <LABELS>
Add a comma-separated list of labels to this pull request.
Add a comma-separated list of labels to this pull request. Labels will be
created if they don't already exist.
## Examples:
$ hub pull-request
...
...
commands/release.go
浏览文件 @
a6318c02
...
...
@@ -17,8 +17,8 @@ var (
cmdRelease
=
&
Command
{
Run
:
listReleases
,
Usage
:
`
release [--include-drafts] [--exclude-prereleases] [-L <LIMIT>]
release show <TAG>
release [--include-drafts] [--exclude-prereleases] [-L <LIMIT>]
[-f <FORMAT>]
release show
[-f <FORMAT>]
<TAG>
release create [-dpoc] [-a <FILE>] [-m <MESSAGE>|-F <FILE>] [-t <TARGET>] <TAG>
release edit [<options>] <TAG>
release download <TAG>
...
...
@@ -178,6 +178,7 @@ hub(1), git-tag(1)
flagReleaseMessage
,
flagReleaseFile
,
flagReleaseFormat
,
flagShowReleaseFormat
,
flagReleaseCommitish
string
flagReleaseAssets
stringSliceValue
...
...
@@ -192,6 +193,7 @@ func init() {
cmdRelease
.
Flag
.
StringVarP
(
&
flagReleaseFormat
,
"format"
,
"f"
,
"%T%n"
,
"FORMAT"
)
cmdShowRelease
.
Flag
.
BoolVarP
(
&
flagReleaseShowDownloads
,
"show-downloads"
,
"d"
,
false
,
"DRAFTS"
)
cmdShowRelease
.
Flag
.
StringVarP
(
&
flagShowReleaseFormat
,
"format"
,
"f"
,
""
,
"FORMAT"
)
cmdCreateRelease
.
Flag
.
BoolVarP
(
&
flagReleaseEdit
,
"edit"
,
"e"
,
false
,
"EDIT"
)
cmdCreateRelease
.
Flag
.
BoolVarP
(
&
flagReleaseDraft
,
"draft"
,
"d"
,
false
,
"DRAFT"
)
...
...
@@ -315,6 +317,8 @@ func showRelease(cmd *Command, args *Args) {
gh
:=
github
.
NewClient
(
project
.
Host
)
args
.
NoForward
()
if
args
.
Noop
{
ui
.
Printf
(
"Would display information for `%s' release
\n
"
,
tagName
)
}
else
{
...
...
@@ -323,6 +327,12 @@ func showRelease(cmd *Command, args *Args) {
body
:=
strings
.
TrimSpace
(
release
.
Body
)
colorize
:=
ui
.
IsTerminal
(
os
.
Stdout
)
if
flagShowReleaseFormat
!=
""
{
ui
.
Printf
(
formatRelease
(
*
release
,
flagShowReleaseFormat
,
colorize
))
return
}
ui
.
Println
(
release
.
Name
)
if
body
!=
""
{
ui
.
Printf
(
"
\n
%s
\n
"
,
body
)
...
...
@@ -338,8 +348,6 @@ func showRelease(cmd *Command, args *Args) {
}
}
}
args
.
NoForward
()
}
func
downloadRelease
(
cmd
*
Command
,
args
*
Args
)
{
...
...
features/compare.feature
浏览文件 @
a6318c02
...
...
@@ -155,3 +155,10 @@ Feature: hub compare
Aborted: the origin remote doesn't point to a GitHub repository.\n
"""
And
the exit status should be 1
Scenario
:
Comparing two branches while not on a local branch
Given
I am in detached HEAD
And
I run `hub compare refactor...master`
Then
the exit status should be 0
And
there should be no output
And "open https
:
//github.com/mislav/dotfiles/compare/refactor...master"
should
be
run
features/issue.feature
浏览文件 @
a6318c02
...
...
@@ -9,7 +9,7 @@ Feature: hub issue
get('/repos/github/hub/issues') {
assert :assignee => "Cornwe19",
:sort => nil,
:direction =>
nil
:direction =>
"desc"
json [
{ :number => 999,
...
...
@@ -76,7 +76,7 @@ Feature: hub issue
get('/repos/github/hub/issues') {
assert :assignee => "Cornwe19",
:sort => nil,
:direction =>
nil
:direction =>
"desc"
json [
{ :number => 999,
...
...
@@ -530,3 +530,125 @@ Feature: hub issue
bug
feature\n
"""
Scenario
:
Fetch single issue
Given the GitHub API server
:
"""
get('/repos/github/hub/issues/102') {
json \
:number => 102,
:state => "open",
:body => "I want this feature",
:title => "Feature request for hub issue show",
:created_at => "2017-04-14T16:00:49Z",
:user => { :login => "royels" },
:assignees => [{:login => "royels"}],
:comments => 1
}
get('/repos/github/hub/issues/102/comments') {
json [
{ :body => "I am from the future",
:created_at => "2011-04-14T16:00:49Z",
:user => { :login => "octocat" }
},
{ :body => "I did the thing",
:created_at => "2013-10-30T22:20:00Z",
:user => { :login => "hubot" }
},
]
}
"""
When
I successfully run `hub issue show 102`
Then the output should contain exactly
:
"""
# Feature request for hub issue show
* created by @royels on 2017-04-14 16:00:49 +0000 UTC
* assignees: royels
I want this feature
## Comments:
### comment by @octocat on 2011-04-14 16:00:49 +0000 UTC
I am from the future
### comment by @hubot on 2013-10-30 22:20:00 +0000 UTC
I did the thing\n
"""
Scenario
:
Format single issue
Given the GitHub API server
:
"""
get('/repos/github/hub/issues/102') {
json \
:number => 102,
:state => "open",
:body => "I want this feature",
:title => "Feature request for hub issue show",
:created_at => "2017-04-14T16:00:49Z",
:user => { :login => "royels" },
:assignees => [{:login => "royels"}],
:comments => 1
}
get('/repos/github/hub/issues/102/comments') {
json [
{ :body => "I am from the future",
:created_at => "2011-04-14T16:00:49Z",
:user => { :login => "octocat" }
},
{ :body => "I did the thing",
:created_at => "2013-10-30T22:20:00Z",
:user => { :login => "hubot" }
},
]
}
"""
When
I successfully run `hub issue show 102 --format='%I %t%n%n%b%n'`
Then the output should contain exactly
:
"""
102 Feature request for hub issue show
I want this feature\n
"""
Scenario
:
Did not supply an issue number
When
I run `hub issue show`
Then
the exit status should be 1
Then the output should contain exactly "Usage
:
hub
issue
show
<NUMBER>\n"
Scenario
:
Show error message if http code is not 200 for issues endpoint
Given the GitHub API server
:
"""
get('/repos/github/hub/issues/102') {
status 500
}
"""
When
I run `hub issue show 102`
Then the output should contain exactly
:
"""
Error fetching issue: Internal Server Error (HTTP 500)\n
"""
Scenario
:
Show error message if http code is not 200 for comments endpoint
Given the GitHub API server
:
"""
get('/repos/github/hub/issues/102') {
json \
:number => 102,
:body => "I want this feature",
:title => "Feature request for hub issue show",
:created_at => "2017-04-14T16:00:49Z",
:user => { :login => "royels" }
}
get('/repos/github/hub/issues/102/comments') {
status 404
}
"""
When
I run `hub issue show 102`
Then the output should contain exactly
:
"""
Error fetching comments for issue: Not Found (HTTP 404)\n
"""
features/pr-list.feature
浏览文件 @
a6318c02
...
...
@@ -10,7 +10,7 @@ Feature: hub pr list
assert :per_page => "100",
:page => :no,
:sort => nil,
:direction =>
nil
:direction =>
"desc"
response.headers["Link"] = %(<https://api.github.com/repositories/12345?per_page=100&page=2>; rel="next")
...
...
@@ -63,6 +63,55 @@ Feature: hub pr list
#7 Fourth\n
"""
Scenario
:
List pull requests with requested reviewers
Given the GitHub API server
:
"""
get('/repos/github/hub/pulls') {
assert :per_page => "100",
:page => :no,
:sort => nil,
:direction => "desc"
json [
{ :number => 999,
:title => "First",
:state => "open",
:base => {
:ref => "master",
:label => "github:master",
:repo => { :owner => { :login => "github" } }
},
:head => { :ref => "patch-1", :label => "octocat:patch-1" },
:user => { :login => "octocat" },
:requested_reviewers => [
{ :login => "rey" },
],
:requested_teams => [
{ :slug => "troopers" },
{ :slug => "cantina-band" },
]
},
{ :number => 102,
:title => "Second",
:state => "open",
:base => { :ref => "master", :label => "github:master" },
:head => { :ref => "patch-2", :label => "octocat:patch-2" },
:user => { :login => "octocat" },
:requested_reviewers => [
{ :login => "luke" },
{ :login => "jyn" },
]
},
]
}
"""
When
I successfully run `hub pr list -f
"%sC%>(8)%i %rs%n"
`
Then the output should contain exactly
:
"""
#999 rey, github/troopers, github/cantina-band
#102 luke, jyn\n
"""
Scenario
:
Sort by number of comments ascending
Given the GitHub API server
:
"""
...
...
features/pull_request.feature
浏览文件 @
a6318c02
...
...
@@ -269,18 +269,22 @@ Feature: hub pull-request
Hello
# Requesting a pull to mislav:master from mislav:topic
#
# Write a message for this pull request. The first block
# of text is the title and the rest is the description.
#
# Changes:
#
# SHA1SHA (Hub, 0 seconds ago)
# Two on topic
#
# SHA1SHA (Hub, 0 seconds ago)
# One on topic
# ------------------------ >8 ------------------------
# Do not modify or remove the line above.
# Everything below it will be ignored.
Requesting a pull to mislav:master from mislav:topic
Write a message for this pull request. The first block
of text is the title and the rest is the description.
Changes:
SHA1SHA (Hub, 0 seconds ago)
Two on topic
SHA1SHA (Hub, 0 seconds ago)
One on topic
"""
...
...
@@ -363,7 +367,7 @@ Feature: hub pull-request
"""
# Dat title
/ This line is commented out.
/ This line is
not
commented out.
Dem body.
"""
...
...
@@ -371,7 +375,7 @@ Feature: hub pull-request
"""
post('/repos/mislav/coral/pulls') {
assert :title => '# Dat title',
:body =>
'Dem body.'
:body =>
"/ This line is not commented out.\n\nDem body."
status 201
json :html_url => "the://url"
}
...
...
@@ -873,7 +877,7 @@ Feature: hub pull-request
status 201
json :html_url => "the://url", :number => 1234,
:requested_reviewers => [{ :login => "josh" }],
:requested_teams => [{ :
name
=> "robots" }]
:requested_teams => [{ :
slug
=> "robots" }]
}
post('/repos/mislav/coral/pulls/1234/requested_reviewers') {
assert :reviewers => ["mislav", "pcorpet"]
...
...
@@ -894,7 +898,7 @@ Feature: hub pull-request
status 201
json :html_url => "the://url", :number => 1234,
:requested_reviewers => [{ :login => "josh" }, { :login => "mislav" }],
:requested_teams => [{ :
name
=> "robots" }]
:requested_teams => [{ :
slug
=> "robots" }]
}
"""
When
I successfully run `hub pull-request -m hereyougo -r mislav,josh -rgithub/robots`
...
...
features/release.feature
浏览文件 @
a6318c02
...
...
@@ -325,6 +325,43 @@ MARKDOWN
https://github.com/mislav/will_paginate/archive/v1.2.0.tar.gz\n
"""
Scenario
:
Format specific release
Given the GitHub API server
:
"""
get('/repos/mislav/will_paginate/releases') {
json [
{ tag_name: 'v1.2.0',
name: 'will_paginate 1.2.0',
draft: true,
prerelease: false,
tarball_url: "https://github.com/mislav/will_paginate/archive/v1.2.0.tar.gz",
zipball_url: "https://github.com/mislav/will_paginate/archive/v1.2.0.zip",
assets: [
{ browser_download_url: "https://github.com/mislav/will_paginate/releases/download/v1.2.0/example.zip",
},
],
body: <<MARKDOWN
### Hello to my release
Here is what's broken:
- everything
MARKDOWN
},
]
}
"""
When
I successfully run `hub release show v1.2.0 --format='%t (%T)%n%as%n%n%b%n'`
Then the output should contain exactly
:
"""
will_paginate 1.2.0 (v1.2.0)
https://github.com/mislav/will_paginate/releases/download/v1.2.0/example.zip
### Hello to my release
Here is what's broken:
- everything\n\n
"""
Scenario
:
Show release no tag
When
I run `hub release show`
Then
the exit status should be 1
...
...
fixtures/test_configs.go
浏览文件 @
a6318c02
...
...
@@ -28,6 +28,21 @@ func SetupTomlTestConfig() *TestConfigs {
return
&
TestConfigs
{
file
.
Name
()}
}
func
SetupTomlTestConfigWithUnixSocket
()
*
TestConfigs
{
file
,
_
:=
ioutil
.
TempFile
(
""
,
"test-gh-config-"
)
content
:=
`[[hosts]]
host = "github.com"
user = "jingweno"
access_token = "123"
protocol = "http"
unix_socket = "/tmp/go.sock"`
ioutil
.
WriteFile
(
file
.
Name
(),
[]
byte
(
content
),
os
.
ModePerm
)
os
.
Setenv
(
"HUB_CONFIG"
,
file
.
Name
())
return
&
TestConfigs
{
file
.
Name
()}
}
func
SetupTestConfigs
()
*
TestConfigs
{
file
,
_
:=
ioutil
.
TempFile
(
""
,
"test-gh-config-"
)
...
...
@@ -41,3 +56,18 @@ github.com:
return
&
TestConfigs
{
file
.
Name
()}
}
func
SetupTestConfigsWithUnixSocket
()
*
TestConfigs
{
file
,
_
:=
ioutil
.
TempFile
(
""
,
"test-gh-config-"
)
content
:=
`---
github.com:
- user: jingweno
oauth_token: 123
protocol: http
unix_socket: /tmp/go.sock`
ioutil
.
WriteFile
(
file
.
Name
(),
[]
byte
(
content
),
os
.
ModePerm
)
os
.
Setenv
(
"HUB_CONFIG"
,
file
.
Name
())
return
&
TestConfigs
{
file
.
Name
()}
}
github/client.go
浏览文件 @
a6318c02
...
...
@@ -525,6 +525,13 @@ func (client *Client) ForkRepository(project *Project, params map[string]interfa
return
}
type
Comment
struct
{
Id
int
`json:"id"`
Body
string
`json:"body"`
User
*
User
`json:"user"`
CreatedAt
time
.
Time
`json:"created_at"`
}
type
Issue
struct
{
Number
int
`json:"number"`
State
string
`json:"state"`
...
...
@@ -550,6 +557,8 @@ type Issue struct {
ApiUrl
string
`json:"url"`
HtmlUrl
string
`json:"html_url"`
ClosedBy
*
User
`json:"closed_by"`
}
type
PullRequest
Issue
...
...
@@ -578,7 +587,7 @@ func (pr *PullRequest) HasRequestedReviewer(name string) bool {
func
(
pr
*
PullRequest
)
HasRequestedTeam
(
name
string
)
bool
{
for
_
,
team
:=
range
pr
.
RequestedTeams
{
if
strings
.
EqualFold
(
team
.
Name
,
name
)
{
if
strings
.
EqualFold
(
team
.
Slug
,
name
)
{
return
true
}
}
...
...
@@ -596,6 +605,7 @@ type User struct {
type
Team
struct
{
Name
string
`json:"name"`
Slug
string
`json:"slug"`
}
type
Milestone
struct
{
...
...
@@ -649,6 +659,38 @@ func (client *Client) FetchIssues(project *Project, filterParams map[string]inte
return
}
func
(
client
*
Client
)
FetchIssue
(
project
*
Project
,
number
string
)
(
issue
*
Issue
,
err
error
)
{
api
,
err
:=
client
.
simpleApi
()
if
err
!=
nil
{
return
}
res
,
err
:=
api
.
Get
(
fmt
.
Sprintf
(
"repos/%s/%s/issues/%s"
,
project
.
Owner
,
project
.
Name
,
number
))
if
err
=
checkStatus
(
200
,
"fetching issue"
,
res
,
err
);
err
!=
nil
{
return
nil
,
err
}
issue
=
&
Issue
{}
err
=
res
.
Unmarshal
(
issue
)
return
}
func
(
client
*
Client
)
FetchComments
(
project
*
Project
,
number
string
)
(
comments
[]
Comment
,
err
error
)
{
api
,
err
:=
client
.
simpleApi
()
if
err
!=
nil
{
return
}
res
,
err
:=
api
.
Get
(
fmt
.
Sprintf
(
"repos/%s/%s/issues/%s/comments"
,
project
.
Owner
,
project
.
Name
,
number
))
if
err
=
checkStatus
(
200
,
"fetching comments for issue"
,
res
,
err
);
err
!=
nil
{
return
nil
,
err
}
comments
=
[]
Comment
{}
err
=
res
.
Unmarshal
(
&
comments
)
return
}
func
(
client
*
Client
)
CreateIssue
(
project
*
Project
,
params
interface
{})
(
issue
*
Issue
,
err
error
)
{
api
,
err
:=
client
.
simpleApi
()
if
err
!=
nil
{
...
...
@@ -848,7 +890,8 @@ func (client *Client) simpleApi() (c *simpleClient, err error) {
}
func
(
client
*
Client
)
apiClient
()
*
simpleClient
{
httpClient
:=
newHttpClient
(
os
.
Getenv
(
"HUB_TEST_HOST"
),
os
.
Getenv
(
"HUB_VERBOSE"
)
!=
""
)
unixSocket
:=
os
.
ExpandEnv
(
client
.
Host
.
UnixSocket
)
httpClient
:=
newHttpClient
(
os
.
Getenv
(
"HUB_TEST_HOST"
),
os
.
Getenv
(
"HUB_VERBOSE"
)
!=
""
,
unixSocket
)
apiRoot
:=
client
.
absolute
(
normalizeHost
(
client
.
Host
.
Host
))
if
client
.
Host
!=
nil
&&
client
.
Host
.
Host
!=
GitHubHost
{
apiRoot
.
Path
=
"/api/v3/"
...
...
github/config.go
浏览文件 @
a6318c02
...
...
@@ -21,6 +21,7 @@ type yamlHost struct {
User
string
`yaml:"user"`
OAuthToken
string
`yaml:"oauth_token"`
Protocol
string
`yaml:"protocol"`
UnixSocket
string
`yaml:"unix_socket,omitempty"`
}
type
yamlConfig
map
[
string
][]
yamlHost
...
...
@@ -30,6 +31,7 @@ type Host struct {
User
string
`toml:"user"`
AccessToken
string
`toml:"access_token"`
Protocol
string
`toml:"protocol"`
UnixSocket
string
`toml:"unix_socket,omitempty"`
}
type
Config
struct
{
...
...
github/config_decoder.go
浏览文件 @
a6318c02
...
...
@@ -46,6 +46,7 @@ func (y *yamlConfigDecoder) Decode(r io.Reader, c *Config) error {
User
:
vv
.
User
,
AccessToken
:
vv
.
OAuthToken
,
Protocol
:
vv
.
Protocol
,
UnixSocket
:
vv
.
UnixSocket
,
}
c
.
Hosts
=
append
(
c
.
Hosts
,
host
)
}
...
...
github/config_encoder.go
浏览文件 @
a6318c02
...
...
@@ -30,6 +30,7 @@ func (y *yamlConfigEncoder) Encode(w io.Writer, c *Config) error {
User
:
h
.
User
,
OAuthToken
:
h
.
AccessToken
,
Protocol
:
h
.
Protocol
,
UnixSocket
:
h
.
UnixSocket
,
},
}
}
...
...
github/config_service_test.go
浏览文件 @
a6318c02
...
...
@@ -30,6 +30,28 @@ func TestConfigService_TomlLoad(t *testing.T) {
assert
.
Equal
(
t
,
"http"
,
host
.
Protocol
)
}
func
TestConfigService_TomlLoad_UnixSocket
(
t
*
testing
.
T
)
{
testConfigUnixSocket
:=
fixtures
.
SetupTomlTestConfigWithUnixSocket
()
defer
testConfigUnixSocket
.
TearDown
()
cc
:=
&
Config
{}
cs
:=
&
configService
{
Encoder
:
&
tomlConfigEncoder
{},
Decoder
:
&
tomlConfigDecoder
{},
}
err
:=
cs
.
Load
(
testConfigUnixSocket
.
Path
,
cc
)
assert
.
Equal
(
t
,
nil
,
err
)
assert
.
Equal
(
t
,
1
,
len
(
cc
.
Hosts
))
host
:=
cc
.
Hosts
[
0
]
assert
.
Equal
(
t
,
"github.com"
,
host
.
Host
)
assert
.
Equal
(
t
,
"jingweno"
,
host
.
User
)
assert
.
Equal
(
t
,
"123"
,
host
.
AccessToken
)
assert
.
Equal
(
t
,
"http"
,
host
.
Protocol
)
assert
.
Equal
(
t
,
"/tmp/go.sock"
,
host
.
UnixSocket
)
}
func
TestConfigService_YamlLoad
(
t
*
testing
.
T
)
{
testConfig
:=
fixtures
.
SetupTestConfigs
()
defer
testConfig
.
TearDown
()
...
...
@@ -50,6 +72,28 @@ func TestConfigService_YamlLoad(t *testing.T) {
assert
.
Equal
(
t
,
"http"
,
host
.
Protocol
)
}
func
TestConfigService_YamlLoad_Unix_Socket
(
t
*
testing
.
T
)
{
testConfigUnixSocket
:=
fixtures
.
SetupTestConfigsWithUnixSocket
()
defer
testConfigUnixSocket
.
TearDown
()
cc
:=
&
Config
{}
cs
:=
&
configService
{
Encoder
:
&
yamlConfigEncoder
{},
Decoder
:
&
yamlConfigDecoder
{},
}
err
:=
cs
.
Load
(
testConfigUnixSocket
.
Path
,
cc
)
assert
.
Equal
(
t
,
nil
,
err
)
assert
.
Equal
(
t
,
1
,
len
(
cc
.
Hosts
))
host
:=
cc
.
Hosts
[
0
]
assert
.
Equal
(
t
,
"github.com"
,
host
.
Host
)
assert
.
Equal
(
t
,
"jingweno"
,
host
.
User
)
assert
.
Equal
(
t
,
"123"
,
host
.
AccessToken
)
assert
.
Equal
(
t
,
"http"
,
host
.
Protocol
)
assert
.
Equal
(
t
,
"/tmp/go.sock"
,
host
.
UnixSocket
)
}
func
TestConfigService_TomlSave
(
t
*
testing
.
T
)
{
file
,
_
:=
ioutil
.
TempFile
(
""
,
"test-gh-config-"
)
defer
os
.
RemoveAll
(
file
.
Name
())
...
...
@@ -78,6 +122,36 @@ func TestConfigService_TomlSave(t *testing.T) {
assert
.
Equal
(
t
,
content
,
strings
.
TrimSpace
(
string
(
b
)))
}
func
TestConfigService_TomlSave_UnixSocket
(
t
*
testing
.
T
)
{
file
,
_
:=
ioutil
.
TempFile
(
""
,
"test-gh-config-"
)
defer
os
.
RemoveAll
(
file
.
Name
())
host
:=
&
Host
{
Host
:
"github.com"
,
User
:
"jingweno"
,
AccessToken
:
"123"
,
Protocol
:
"https"
,
UnixSocket
:
"/tmp/go.sock"
,
}
c
:=
&
Config
{
Hosts
:
[]
*
Host
{
host
}}
cs
:=
&
configService
{
Encoder
:
&
tomlConfigEncoder
{},
Decoder
:
&
tomlConfigDecoder
{},
}
err
:=
cs
.
Save
(
file
.
Name
(),
c
)
assert
.
Equal
(
t
,
nil
,
err
)
b
,
_
:=
ioutil
.
ReadFile
(
file
.
Name
())
content
:=
`[[hosts]]
host = "github.com"
user = "jingweno"
access_token = "123"
protocol = "https"
unix_socket = "/tmp/go.sock"`
assert
.
Equal
(
t
,
content
,
strings
.
TrimSpace
(
string
(
b
)))
}
func
TestConfigService_YamlSave
(
t
*
testing
.
T
)
{
file
,
_
:=
ioutil
.
TempFile
(
""
,
"test-gh-config-"
)
defer
os
.
RemoveAll
(
file
.
Name
())
...
...
@@ -104,3 +178,32 @@ func TestConfigService_YamlSave(t *testing.T) {
protocol: https`
assert
.
Equal
(
t
,
content
,
strings
.
TrimSpace
(
string
(
b
)))
}
func
TestConfigService_YamlSave_UnixSocket
(
t
*
testing
.
T
)
{
file
,
_
:=
ioutil
.
TempFile
(
""
,
"test-gh-config-"
)
defer
os
.
RemoveAll
(
file
.
Name
())
host
:=
&
Host
{
Host
:
"github.com"
,
User
:
"jingweno"
,
AccessToken
:
"123"
,
Protocol
:
"https"
,
UnixSocket
:
"/tmp/go.sock"
,
}
c
:=
&
Config
{
Hosts
:
[]
*
Host
{
host
}}
cs
:=
&
configService
{
Encoder
:
&
yamlConfigEncoder
{},
Decoder
:
&
yamlConfigDecoder
{},
}
err
:=
cs
.
Save
(
file
.
Name
(),
c
)
assert
.
Equal
(
t
,
nil
,
err
)
b
,
_
:=
ioutil
.
ReadFile
(
file
.
Name
())
content
:=
`github.com:
- user: jingweno
oauth_token: "123"
protocol: https
unix_socket: /tmp/go.sock`
assert
.
Equal
(
t
,
content
,
strings
.
TrimSpace
(
string
(
b
)))
}
github/editor.go
浏览文件 @
a6318c02
...
...
@@ -14,6 +14,8 @@ import (
"github.com/github/hub/git"
)
const
Scissors
=
"------------------------ >8 ------------------------"
func
NewEditor
(
filename
,
topic
,
message
string
)
(
editor
*
Editor
,
err
error
)
{
gitDir
,
err
:=
git
.
Dir
()
if
err
!=
nil
{
...
...
@@ -44,20 +46,25 @@ func NewEditor(filename, topic, message string) (editor *Editor, err error) {
}
type
Editor
struct
{
Program
string
Topic
string
File
string
Message
string
CS
string
openEditor
func
(
program
,
file
string
)
error
Program
string
Topic
string
File
string
Message
string
CS
string
addedFirstComment
bool
openEditor
func
(
program
,
file
string
)
error
}
func
(
e
*
Editor
)
AddCommentedSection
(
text
string
)
{
startRegexp
:=
regexp
.
MustCompilePOSIX
(
"^"
)
endRegexp
:=
regexp
.
MustCompilePOSIX
(
" +$"
)
commentedText
:=
startRegexp
.
ReplaceAllString
(
text
,
e
.
CS
+
" "
)
commentedText
=
endRegexp
.
ReplaceAllString
(
commentedText
,
""
)
e
.
Message
=
e
.
Message
+
"
\n
"
+
commentedText
if
!
e
.
addedFirstComment
{
scissors
:=
e
.
CS
+
" "
+
Scissors
+
"
\n
"
scissors
+=
e
.
CS
+
" Do not modify or remove the line above.
\n
"
scissors
+=
e
.
CS
+
" Everything below it will be ignored.
\n
"
e
.
Message
=
e
.
Message
+
"
\n
"
+
scissors
e
.
addedFirstComment
=
true
}
e
.
Message
=
e
.
Message
+
"
\n
"
+
text
}
func
(
e
*
Editor
)
DeleteFile
()
error
{
...
...
@@ -75,11 +82,13 @@ func (e *Editor) EditContent() (content string, err error) {
scanner
:=
bufio
.
NewScanner
(
reader
)
unquotedLines
:=
[]
string
{}
scissorsLine
:=
e
.
CS
+
" "
+
Scissors
for
scanner
.
Scan
()
{
line
:=
scanner
.
Text
()
if
e
.
CS
==
""
||
!
strings
.
HasPrefix
(
line
,
e
.
CS
)
{
unquotedLines
=
append
(
unquotedLines
,
line
)
if
line
==
scissorsLine
{
break
}
unquotedLines
=
append
(
unquotedLines
,
line
)
}
if
err
=
scanner
.
Err
();
err
!=
nil
{
return
...
...
github/http.go
浏览文件 @
a6318c02
...
...
@@ -135,20 +135,35 @@ func (t *verboseTransport) verbosePrintln(msg string) {
fmt
.
Fprintln
(
t
.
Out
,
msg
)
}
func
newHttpClient
(
testHost
string
,
verbose
bool
)
*
http
.
Client
{
func
newHttpClient
(
testHost
string
,
verbose
bool
,
unixSocket
string
)
*
http
.
Client
{
var
testURL
*
url
.
URL
if
testHost
!=
""
{
testURL
,
_
=
url
.
Parse
(
testHost
)
}
tr
:=
&
verboseTransport
{
Transport
:
&
http
.
Transport
{
var
httpTransport
*
http
.
Transport
if
unixSocket
!=
""
{
dialFunc
:=
func
(
network
,
addr
string
)
(
net
.
Conn
,
error
)
{
return
net
.
Dial
(
"unix"
,
unixSocket
)
}
httpTransport
=
&
http
.
Transport
{
Dial
:
dialFunc
,
DialTLS
:
dialFunc
,
ResponseHeaderTimeout
:
30
*
time
.
Second
,
ExpectContinueTimeout
:
10
*
time
.
Second
,
TLSHandshakeTimeout
:
10
*
time
.
Second
,
}
}
else
{
httpTransport
=
&
http
.
Transport
{
Proxy
:
proxyFromEnvironment
,
Dial
:
(
&
net
.
Dialer
{
Timeout
:
30
*
time
.
Second
,
KeepAlive
:
30
*
time
.
Second
,
})
.
Dial
,
TLSHandshakeTimeout
:
10
*
time
.
Second
,
},
}
}
tr
:=
&
verboseTransport
{
Transport
:
httpTransport
,
Verbose
:
verbose
,
OverrideURL
:
testURL
,
Out
:
ui
.
Stderr
,
...
...
github/http_test.go
浏览文件 @
a6318c02
...
...
@@ -3,19 +3,32 @@ package github
import
(
"bytes"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
"github.com/bmizerany/assert"
)
func
setupTestServer
()
*
testServer
{
func
setupTestServer
(
unixSocket
string
)
*
testServer
{
m
:=
http
.
NewServeMux
()
s
:=
httptest
.
NewServer
(
m
)
u
,
_
:=
url
.
Parse
(
s
.
URL
)
if
unixSocket
!=
""
{
os
.
Remove
(
unixSocket
)
unixListener
,
err
:=
net
.
Listen
(
"unix"
,
unixSocket
)
if
err
!=
nil
{
log
.
Fatal
(
"Unable to listen on unix-socket: "
,
err
)
}
go
http
.
Serve
(
unixListener
,
m
)
}
return
&
testServer
{
Server
:
s
,
ServeMux
:
m
,
...
...
@@ -34,7 +47,7 @@ func (s *testServer) Close() {
}
func
TestNewHttpClient_OverrideURL
(
t
*
testing
.
T
)
{
s
:=
setupTestServer
()
s
:=
setupTestServer
(
""
)
defer
s
.
Close
()
s
.
HandleFunc
(
"/override"
,
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
...
...
@@ -42,7 +55,7 @@ func TestNewHttpClient_OverrideURL(t *testing.T) {
assert
.
Equal
(
t
,
"example.com"
,
r
.
Host
)
})
c
:=
newHttpClient
(
s
.
URL
.
String
(),
false
)
c
:=
newHttpClient
(
s
.
URL
.
String
(),
false
,
""
)
c
.
Get
(
"https://example.com/override"
)
s
.
HandleFunc
(
"/not-override"
,
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
...
...
@@ -50,10 +63,25 @@ func TestNewHttpClient_OverrideURL(t *testing.T) {
assert
.
Equal
(
t
,
s
.
URL
.
Host
,
r
.
Host
)
})
c
=
newHttpClient
(
""
,
false
)
c
=
newHttpClient
(
""
,
false
,
""
)
c
.
Get
(
fmt
.
Sprintf
(
"%s/not-override"
,
s
.
URL
.
String
()))
}
func
TestNewHttpClient_UnixSocket
(
t
*
testing
.
T
)
{
sock
:=
"/tmp/hub-go.sock"
s
:=
setupTestServer
(
sock
)
defer
s
.
Close
()
s
.
HandleFunc
(
"/unix-socket"
,
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
w
.
Write
([]
byte
(
"unix-socket-works"
))
})
c
:=
newHttpClient
(
""
,
false
,
sock
)
resp
,
err
:=
c
.
Get
(
fmt
.
Sprintf
(
"%s/unix-socket"
,
s
.
URL
.
String
()))
assert
.
Equal
(
t
,
nil
,
err
)
result
,
_
:=
ioutil
.
ReadAll
(
resp
.
Body
)
assert
.
Equal
(
t
,
"unix-socket-works"
,
string
(
result
))
}
func
TestVerboseTransport_VerbosePrintln
(
t
*
testing
.
T
)
{
var
b
bytes
.
Buffer
tr
:=
&
verboseTransport
{
...
...
utils/utils.go
浏览文件 @
a6318c02
...
...
@@ -14,6 +14,8 @@ import (
"github.com/github/hub/ui"
)
var
timeNow
=
time
.
Now
func
Check
(
err
error
)
{
if
err
!=
nil
{
ui
.
Errorln
(
err
)
...
...
@@ -113,7 +115,7 @@ func (c *Color) Brightness() float32 {
}
func
TimeAgo
(
t
time
.
Time
)
string
{
duration
:=
time
.
Since
(
t
)
duration
:=
time
Now
()
.
Sub
(
t
)
minutes
:=
duration
.
Minutes
()
hours
:=
duration
.
Hours
()
days
:=
hours
/
24
...
...
utils/utils_test.go
浏览文件 @
a6318c02
...
...
@@ -3,6 +3,7 @@ package utils
import
(
"github.com/bmizerany/assert"
"testing"
"time"
)
func
TestSearchBrowserLauncher
(
t
*
testing
.
T
)
{
...
...
@@ -16,3 +17,62 @@ func TestSearchBrowserLauncher(t *testing.T) {
func
TestConcatPaths
(
t
*
testing
.
T
)
{
assert
.
Equal
(
t
,
"foo/bar/baz"
,
ConcatPaths
(
"foo"
,
"bar"
,
"baz"
))
}
func
TestTimeAgo
(
t
*
testing
.
T
)
{
timeNow
=
func
()
time
.
Time
{
return
time
.
Date
(
2018
,
10
,
28
,
14
,
34
,
58
,
651387237
,
time
.
UTC
)
}
now
:=
timeNow
()
secAgo
:=
now
.
Add
(
-
1
*
time
.
Second
)
actual
:=
TimeAgo
(
secAgo
)
assert
.
Equal
(
t
,
"now"
,
actual
)
minAgo
:=
now
.
Add
(
-
1
*
time
.
Minute
)
actual
=
TimeAgo
(
minAgo
)
assert
.
Equal
(
t
,
"1 minute ago"
,
actual
)
minsAgo
:=
now
.
Add
(
-
5
*
time
.
Minute
)
actual
=
TimeAgo
(
minsAgo
)
assert
.
Equal
(
t
,
"5 minutes ago"
,
actual
)
hourAgo
:=
now
.
Add
(
-
1
*
time
.
Hour
)
actual
=
TimeAgo
(
hourAgo
)
assert
.
Equal
(
t
,
"1 hour ago"
,
actual
)
hoursAgo
:=
now
.
Add
(
-
3
*
time
.
Hour
)
actual
=
TimeAgo
(
hoursAgo
)
assert
.
Equal
(
t
,
"3 hours ago"
,
actual
)
dayAgo
:=
now
.
Add
(
-
1
*
24
*
time
.
Hour
)
actual
=
TimeAgo
(
dayAgo
)
assert
.
Equal
(
t
,
"1 day ago"
,
actual
)
daysAgo
:=
now
.
Add
(
-
5
*
24
*
time
.
Hour
)
actual
=
TimeAgo
(
daysAgo
)
assert
.
Equal
(
t
,
"5 days ago"
,
actual
)
monthAgo
:=
now
.
Add
(
-
1
*
24
*
31
*
time
.
Hour
)
actual
=
TimeAgo
(
monthAgo
)
assert
.
Equal
(
t
,
"1 month ago"
,
actual
)
monthsAgo
:=
now
.
Add
(
-
2
*
24
*
31
*
time
.
Hour
)
actual
=
TimeAgo
(
monthsAgo
)
assert
.
Equal
(
t
,
"2 months ago"
,
actual
)
yearAgo
:=
now
.
Add
(
-
1
*
24
*
31
*
12
*
time
.
Hour
)
actual
=
TimeAgo
(
yearAgo
)
assert
.
Equal
(
t
,
"1 year ago"
,
actual
)
yearsAgo
:=
now
.
Add
(
-
2
*
24
*
31
*
12
*
time
.
Hour
)
actual
=
TimeAgo
(
yearsAgo
)
assert
.
Equal
(
t
,
"2 years ago"
,
actual
)
}
func
TestColorBrightness
(
t
*
testing
.
T
)
{
c
,
err
:=
NewColor
(
"880000"
)
assert
.
Equal
(
t
,
nil
,
err
)
actual
:=
c
.
Brightness
()
assert
.
Equal
(
t
,
float32
(
0.15946665406227112
),
actual
)
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录