cute command to show a thank you to contributors
this commit is the result of April's hack day. it animates a thank you for a project's contributors. it only really works on linux/mac; animation woes led me to just make it static and plain on windows.
This commit is contained in:
parent
e172f31c80
commit
1aab2084f1
1 changed files with 248 additions and 0 deletions
248
command/credits.go
Normal file
248
command/credits.go
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
|
||||
"github.com/cli/cli/utils"
|
||||
)
|
||||
|
||||
var isWindows bool
|
||||
|
||||
var thankYou = `
|
||||
_ _
|
||||
| | | |
|
||||
_|_ | | __, _ _ | | __
|
||||
| |/ \ / | / |/ | |/_) | | / \_| |
|
||||
|_/| |_/\_/|_/ | |_/| \_/ \_/|/\__/ \_/|_/
|
||||
/|
|
||||
\|
|
||||
_
|
||||
o | | |
|
||||
__ __ _ _ _|_ ,_ | | _|_ __ ,_ , |
|
||||
/ / \_/ |/ | | / | | |/ \_| | | / \_/ | / \_|
|
||||
\___/\__/ | |_/|_/ |_/|_/\_/ \_/|_/|_/\__/ |_/ \/ o
|
||||
|
||||
|
||||
`
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
isWindows = runtime.GOOS == "windows"
|
||||
|
||||
RootCmd.AddCommand(creditsCmd)
|
||||
|
||||
creditsCmd.Flags().BoolP("static", "s", false, "Print a static version of the credits")
|
||||
}
|
||||
|
||||
var creditsCmd = &cobra.Command{
|
||||
Use: "credits [repository]",
|
||||
Short: "View project's credits",
|
||||
Long: `View animated credits for this or another project.
|
||||
|
||||
Examples:
|
||||
|
||||
gh credits # see a credits animation for this project
|
||||
gh credits owner/repo # see a credits animation for owner/repo
|
||||
gh credts -s # display a static thank you instead of animating
|
||||
gh credits | cat # just print the contributors, one per line
|
||||
`,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: credits,
|
||||
}
|
||||
|
||||
func credits(cmd *cobra.Command, args []string) error {
|
||||
ctx := contextForCommand(cmd)
|
||||
client, err := apiClientForContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
owner := "cli"
|
||||
repo := "cli"
|
||||
if len(args) > 0 {
|
||||
parts := strings.SplitN(args[0], "/", 2)
|
||||
owner = parts[0]
|
||||
repo = parts[1]
|
||||
}
|
||||
|
||||
type Contributor struct {
|
||||
Login string
|
||||
}
|
||||
|
||||
type Result []Contributor
|
||||
|
||||
result := Result{}
|
||||
body := bytes.NewBufferString("")
|
||||
path := fmt.Sprintf("repos/%s/%s/contributors", owner, repo)
|
||||
|
||||
err = client.REST("GET", path, body, &result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out := cmd.OutOrStdout()
|
||||
isTTY := false
|
||||
outFile, isFile := out.(*os.File)
|
||||
if isFile {
|
||||
isTTY = utils.IsTerminal(outFile)
|
||||
if isTTY {
|
||||
// FIXME: duplicates colorableOut
|
||||
out = utils.NewColorable(outFile)
|
||||
}
|
||||
}
|
||||
|
||||
static, err := cmd.Flags().GetBool("static")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
static = static || isWindows
|
||||
|
||||
if isTTY && static {
|
||||
fmt.Fprintln(out, "THANK YOU CONTRIBUTORS!!! <3")
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
logins := []string{}
|
||||
for x, c := range result {
|
||||
if isTTY && !static {
|
||||
logins = append(logins, getColor(x)(c.Login))
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n", c.Login)
|
||||
}
|
||||
}
|
||||
|
||||
if !isTTY || static {
|
||||
return nil
|
||||
}
|
||||
|
||||
lines := []string{}
|
||||
|
||||
thankLines := strings.Split(thankYou, "\n")
|
||||
for x, tl := range thankLines {
|
||||
lines = append(lines, getColor(x)(tl))
|
||||
}
|
||||
lines = append(lines, "")
|
||||
lines = append(lines, logins...)
|
||||
lines = append(lines, "( <3 press ctrl-c to quit <3 )")
|
||||
|
||||
termWidth, termHeight, err := terminal.GetSize(int(outFile.Fd()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
margin := termWidth / 3
|
||||
|
||||
starLinesLeft := []string{}
|
||||
for x := 0; x < len(lines); x++ {
|
||||
starLinesLeft = append(starLinesLeft, starLine(margin))
|
||||
}
|
||||
|
||||
starLinesRight := []string{}
|
||||
for x := 0; x < len(lines); x++ {
|
||||
lineWidth := termWidth - (margin + len(lines[x]))
|
||||
starLinesRight = append(starLinesRight, starLine(lineWidth))
|
||||
}
|
||||
|
||||
loop := true
|
||||
startx := termHeight - 1
|
||||
li := 0
|
||||
|
||||
for loop {
|
||||
clear()
|
||||
for x := 0; x < termHeight; x++ {
|
||||
if x == startx || startx < 0 {
|
||||
starty := 0
|
||||
if startx < 0 {
|
||||
starty = int(math.Abs(float64(startx)))
|
||||
}
|
||||
for y := starty; y < li+1; y++ {
|
||||
if y >= len(lines) {
|
||||
continue
|
||||
}
|
||||
starLineLeft := starLinesLeft[y]
|
||||
starLinesLeft[y] = twinkle(starLineLeft)
|
||||
starLineRight := starLinesRight[y]
|
||||
starLinesRight[y] = twinkle(starLineRight)
|
||||
fmt.Fprintf(out, "%s %s %s\n", starLineLeft, lines[y], starLineRight)
|
||||
}
|
||||
li += 1
|
||||
x += li
|
||||
} else {
|
||||
fmt.Fprintf(out, "\n")
|
||||
}
|
||||
}
|
||||
if li < len(lines) {
|
||||
startx -= 1
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func starLine(width int) string {
|
||||
line := ""
|
||||
starChance := 0.1
|
||||
for y := 0; y < width; y++ {
|
||||
chance := rand.Float64()
|
||||
if chance <= starChance {
|
||||
charRoll := rand.Float64()
|
||||
switch {
|
||||
case charRoll < 0.3:
|
||||
line += "."
|
||||
case charRoll > 0.3 && charRoll < 0.6:
|
||||
line += "+"
|
||||
default:
|
||||
line += "*"
|
||||
}
|
||||
} else {
|
||||
line += " "
|
||||
}
|
||||
}
|
||||
|
||||
return line
|
||||
}
|
||||
|
||||
func twinkle(starLine string) string {
|
||||
starLine = strings.ReplaceAll(starLine, ".", "P")
|
||||
starLine = strings.ReplaceAll(starLine, "+", "A")
|
||||
starLine = strings.ReplaceAll(starLine, "*", ".")
|
||||
starLine = strings.ReplaceAll(starLine, "P", "+")
|
||||
starLine = strings.ReplaceAll(starLine, "A", "*")
|
||||
return starLine
|
||||
}
|
||||
|
||||
func getColor(x int) func(string) string {
|
||||
rainbow := []func(string) string{
|
||||
utils.Magenta,
|
||||
utils.Red,
|
||||
utils.Yellow,
|
||||
utils.Green,
|
||||
utils.Cyan,
|
||||
utils.Blue,
|
||||
}
|
||||
|
||||
ix := x % len(rainbow)
|
||||
|
||||
return rainbow[ix]
|
||||
}
|
||||
|
||||
func clear() {
|
||||
// on windows we'd do cmd := exec.Command("cmd", "/c", "cls"); unfortunately the draw speed is so
|
||||
// slow that the animation is very jerky, flashy, and painful to look at.
|
||||
cmd := exec.Command("clear")
|
||||
cmd.Stdout = os.Stdout
|
||||
_ = cmd.Run()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue