Add onboard command
This commit is contained in:
parent
38a5637cf5
commit
7951ee6bb5
|
@ -8,6 +8,7 @@ import (
|
||||||
_ "github.com/ooni/probe-cli/internal/cli/info"
|
_ "github.com/ooni/probe-cli/internal/cli/info"
|
||||||
_ "github.com/ooni/probe-cli/internal/cli/list"
|
_ "github.com/ooni/probe-cli/internal/cli/list"
|
||||||
_ "github.com/ooni/probe-cli/internal/cli/nettest"
|
_ "github.com/ooni/probe-cli/internal/cli/nettest"
|
||||||
|
_ "github.com/ooni/probe-cli/internal/cli/onboard"
|
||||||
_ "github.com/ooni/probe-cli/internal/cli/run"
|
_ "github.com/ooni/probe-cli/internal/cli/run"
|
||||||
_ "github.com/ooni/probe-cli/internal/cli/show"
|
_ "github.com/ooni/probe-cli/internal/cli/show"
|
||||||
_ "github.com/ooni/probe-cli/internal/cli/upload"
|
_ "github.com/ooni/probe-cli/internal/cli/upload"
|
||||||
|
|
134
internal/cli/onboard/onboard.go
Normal file
134
internal/cli/onboard/onboard.go
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
package onboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alecthomas/kingpin"
|
||||||
|
"github.com/apex/log"
|
||||||
|
"github.com/ooni/probe-cli/internal/cli/root"
|
||||||
|
"github.com/ooni/probe-cli/internal/colors"
|
||||||
|
"github.com/ooni/probe-cli/internal/output"
|
||||||
|
"gopkg.in/AlecAivazis/survey.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cmd := root.Command("onboard", "Starts the onboarding process")
|
||||||
|
|
||||||
|
cmd.Action(func(_ *kingpin.ParseContext) error {
|
||||||
|
output.SectionTitle("What is OONI Probe?")
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
output.Paragraph("Your tool for detecting internet censorship!")
|
||||||
|
fmt.Println()
|
||||||
|
output.Paragraph("OONI Probe checks whether your provider blocks access to sites and services. Run OONI Probe to collect evidence of internet censorship and to measure your network performance.")
|
||||||
|
fmt.Println()
|
||||||
|
output.PressEnterToContinue("Press 'Enter' to continue...")
|
||||||
|
|
||||||
|
output.SectionTitle("Heads Up")
|
||||||
|
fmt.Println()
|
||||||
|
output.Bullet("Anyone monitoring your internet activity (such as your government or ISP) may be able to see that you are running OONI Probe.")
|
||||||
|
fmt.Println()
|
||||||
|
output.Bullet("The network data you will collect will automatically be published (unless you opt-out in the settings).")
|
||||||
|
fmt.Println()
|
||||||
|
output.Bullet("You may test objectionable sites.")
|
||||||
|
fmt.Println()
|
||||||
|
output.Bullet("Read the documentation to learn more.")
|
||||||
|
fmt.Println()
|
||||||
|
output.PressEnterToContinue("Press 'Enter' to continue...")
|
||||||
|
|
||||||
|
output.SectionTitle("Pop Quiz!")
|
||||||
|
output.Paragraph("")
|
||||||
|
answer := ""
|
||||||
|
quiz1 := &survey.Select{
|
||||||
|
Message: "Anyone monitoring my internet activity may be able to see that I am running OONI Probe.",
|
||||||
|
Options: []string{"true", "false"},
|
||||||
|
Default: "true",
|
||||||
|
}
|
||||||
|
survey.AskOne(quiz1, &answer, nil)
|
||||||
|
if answer != "true" {
|
||||||
|
output.Paragraph(colors.Red("Actually..."))
|
||||||
|
output.Paragraph("OONI Probe is not a privacy tool. Therefore, anyone monitoring your internet activity may be able to see which software you are running.")
|
||||||
|
} else {
|
||||||
|
output.Paragraph(colors.Blue("Good job!"))
|
||||||
|
}
|
||||||
|
answer = ""
|
||||||
|
quiz2 := &survey.Select{
|
||||||
|
Message: "The network data I will collect will automatically be published (unless I opt-out in the settings).",
|
||||||
|
Options: []string{"true", "false"},
|
||||||
|
Default: "true",
|
||||||
|
}
|
||||||
|
survey.AskOne(quiz2, &answer, nil)
|
||||||
|
if answer != "true" {
|
||||||
|
output.Paragraph(colors.Red("Actually..."))
|
||||||
|
output.Paragraph("The network data you will collect will automatically be published to increase transparency of internet censorship (unless you opt-out in the settings).")
|
||||||
|
} else {
|
||||||
|
output.Paragraph(colors.Blue("Well done!"))
|
||||||
|
}
|
||||||
|
|
||||||
|
changeDefaults := false
|
||||||
|
prompt := &survey.Confirm{
|
||||||
|
Message: "Do you want to change the default settings?",
|
||||||
|
Default: false,
|
||||||
|
}
|
||||||
|
survey.AskOne(prompt, &changeDefaults, nil)
|
||||||
|
|
||||||
|
settings := struct {
|
||||||
|
IncludeIP bool
|
||||||
|
IncludeNetwork bool
|
||||||
|
IncludeCountry bool
|
||||||
|
UploadResults bool
|
||||||
|
SendCrashReports bool
|
||||||
|
}{}
|
||||||
|
settings.IncludeIP = false
|
||||||
|
settings.IncludeNetwork = true
|
||||||
|
settings.IncludeCountry = true
|
||||||
|
settings.UploadResults = true
|
||||||
|
settings.SendCrashReports = true
|
||||||
|
|
||||||
|
if changeDefaults == true {
|
||||||
|
var qs = []*survey.Question{
|
||||||
|
{
|
||||||
|
Name: "IncludeIP",
|
||||||
|
Prompt: &survey.Confirm{Message: "Should we include your IP?"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "IncludeNetwork",
|
||||||
|
Prompt: &survey.Confirm{
|
||||||
|
Message: "Can we include your network name?",
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "IncludeCountry",
|
||||||
|
Prompt: &survey.Confirm{
|
||||||
|
Message: "Can we include your country name?",
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "UploadResults",
|
||||||
|
Prompt: &survey.Confirm{
|
||||||
|
Message: "Can we upload your results?",
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "SendCrashReports",
|
||||||
|
Prompt: &survey.Confirm{
|
||||||
|
Message: "Can we send crash reports to OONI?",
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := survey.Ask(qs, &settings)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("there was an error in parsing your responses")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Error("this function is not implemented")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
colorable "github.com/mattn/go-colorable"
|
colorable "github.com/mattn/go-colorable"
|
||||||
"github.com/ooni/probe-cli/internal/log/handlers/cli/progress"
|
"github.com/ooni/probe-cli/internal/log/handlers/cli/progress"
|
||||||
|
"github.com/ooni/probe-cli/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default handler outputting to stderr.
|
// Default handler outputting to stderr.
|
||||||
|
@ -68,7 +69,7 @@ func logSectionTitle(w io.Writer, f log.Fields) error {
|
||||||
|
|
||||||
title := f.Get("title").(string)
|
title := f.Get("title").(string)
|
||||||
fmt.Fprintf(w, "┏"+strings.Repeat("━", colWidth+2)+"┓\n")
|
fmt.Fprintf(w, "┏"+strings.Repeat("━", colWidth+2)+"┓\n")
|
||||||
fmt.Fprintf(w, "┃ %s ┃\n", RightPad(title, colWidth))
|
fmt.Fprintf(w, "┃ %s ┃\n", util.RightPad(title, colWidth))
|
||||||
fmt.Fprintf(w, "┗"+strings.Repeat("━", colWidth+2)+"┛\n")
|
fmt.Fprintf(w, "┗"+strings.Repeat("━", colWidth+2)+"┛\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -85,7 +86,7 @@ func logTable(w io.Writer, f log.Fields) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
line := fmt.Sprintf("%s: %s", color.Sprint(name), f.Get(name))
|
line := fmt.Sprintf("%s: %s", color.Sprint(name), f.Get(name))
|
||||||
lineLength := escapeAwareRuneCountInString(line)
|
lineLength := util.EscapeAwareRuneCountInString(line)
|
||||||
lines = append(lines, line)
|
lines = append(lines, line)
|
||||||
if colWidth < lineLength {
|
if colWidth < lineLength {
|
||||||
colWidth = lineLength
|
colWidth = lineLength
|
||||||
|
@ -95,7 +96,7 @@ func logTable(w io.Writer, f log.Fields) error {
|
||||||
fmt.Fprintf(w, "┏"+strings.Repeat("━", colWidth+2)+"┓\n")
|
fmt.Fprintf(w, "┏"+strings.Repeat("━", colWidth+2)+"┓\n")
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
fmt.Fprintf(w, "┃ %s ┃\n",
|
fmt.Fprintf(w, "┃ %s ┃\n",
|
||||||
RightPad(line, colWidth),
|
util.RightPad(line, colWidth),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "┗"+strings.Repeat("━", colWidth+2)+"┛\n")
|
fmt.Fprintf(w, "┗"+strings.Repeat("━", colWidth+2)+"┛\n")
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
|
"github.com/ooni/probe-cli/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// XXX Copy-pasta from nettest/groups
|
// XXX Copy-pasta from nettest/groups
|
||||||
|
@ -118,21 +119,21 @@ func logResultItem(w io.Writer, f log.Fields) error {
|
||||||
fmt.Fprintf(w, "┢"+strings.Repeat("━", colWidth*2+2)+"┪\n")
|
fmt.Fprintf(w, "┢"+strings.Repeat("━", colWidth*2+2)+"┪\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
firstRow := RightPad(fmt.Sprintf("#%d - %s", rID, startTime.Format(time.RFC822)), colWidth*2)
|
firstRow := util.RightPad(fmt.Sprintf("#%d - %s", rID, startTime.Format(time.RFC822)), colWidth*2)
|
||||||
fmt.Fprintf(w, "┃ "+firstRow+" ┃\n")
|
fmt.Fprintf(w, "┃ "+firstRow+" ┃\n")
|
||||||
fmt.Fprintf(w, "┡"+strings.Repeat("━", colWidth*2+2)+"┩\n")
|
fmt.Fprintf(w, "┡"+strings.Repeat("━", colWidth*2+2)+"┩\n")
|
||||||
|
|
||||||
summary := makeSummary(name, f.Get("summary").(string))
|
summary := makeSummary(name, f.Get("summary").(string))
|
||||||
|
|
||||||
fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n",
|
fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n",
|
||||||
RightPad(name, colWidth),
|
util.RightPad(name, colWidth),
|
||||||
RightPad(summary[0], colWidth)))
|
util.RightPad(summary[0], colWidth)))
|
||||||
fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n",
|
fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n",
|
||||||
RightPad(networkName, colWidth),
|
util.RightPad(networkName, colWidth),
|
||||||
RightPad(summary[1], colWidth)))
|
util.RightPad(summary[1], colWidth)))
|
||||||
fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n",
|
fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n",
|
||||||
RightPad(asn, colWidth),
|
util.RightPad(asn, colWidth),
|
||||||
RightPad(summary[2], colWidth)))
|
util.RightPad(summary[2], colWidth)))
|
||||||
|
|
||||||
if index == totalCount-1 {
|
if index == totalCount-1 {
|
||||||
fmt.Fprintf(w, "└┬──────────────┬──────────────┬──────────────┬")
|
fmt.Fprintf(w, "└┬──────────────┬──────────────┬──────────────┬")
|
||||||
|
@ -156,9 +157,9 @@ func logResultSummary(w io.Writer, f log.Fields) error {
|
||||||
}
|
}
|
||||||
// └┬──────────────┬──────────────┬──────────────┬
|
// └┬──────────────┬──────────────┬──────────────┬
|
||||||
fmt.Fprintf(w, " │ %s │ %s │ %s │\n",
|
fmt.Fprintf(w, " │ %s │ %s │ %s │\n",
|
||||||
RightPad(fmt.Sprintf("%d tests", tests), 12),
|
util.RightPad(fmt.Sprintf("%d tests", tests), 12),
|
||||||
RightPad(fmt.Sprintf("%d nets", networks), 12),
|
util.RightPad(fmt.Sprintf("%d nets", networks), 12),
|
||||||
RightPad(fmt.Sprintf("%d ⬆ %d ⬇", dataUp, dataDown), 12))
|
util.RightPad(fmt.Sprintf("%d ⬆ %d ⬇", dataUp, dataDown), 12))
|
||||||
fmt.Fprintf(w, " └──────────────┴──────────────┴──────────────┘\n")
|
fmt.Fprintf(w, " └──────────────┴──────────────┴──────────────┘\n")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Finds the control character sequences (like colors)
|
|
||||||
var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d")
|
|
||||||
|
|
||||||
func escapeAwareRuneCountInString(s string) int {
|
|
||||||
n := utf8.RuneCountInString(s)
|
|
||||||
for _, sm := range ctrlFinder.FindAllString(s, -1) {
|
|
||||||
n -= utf8.RuneCountInString(sm)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func RightPad(str string, length int) string {
|
|
||||||
return str + strings.Repeat(" ", length-escapeAwareRuneCountInString(str))
|
|
||||||
}
|
|
|
@ -1,9 +1,13 @@
|
||||||
package output
|
package output
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
|
"github.com/ooni/probe-cli/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Progress logs a progress type event
|
// Progress logs a progress type event
|
||||||
|
@ -76,3 +80,18 @@ func SectionTitle(text string) {
|
||||||
"title": text,
|
"title": text,
|
||||||
}).Info(text)
|
}).Info(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Paragraph(text string) {
|
||||||
|
const width = 80
|
||||||
|
fmt.Println(util.WrapString(text, width))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Bullet(text string) {
|
||||||
|
const width = 80
|
||||||
|
fmt.Printf("• %s\n", util.WrapString(text, width))
|
||||||
|
}
|
||||||
|
|
||||||
|
func PressEnterToContinue(text string) {
|
||||||
|
fmt.Print(text)
|
||||||
|
bufio.NewReader(os.Stdin).ReadBytes('\n')
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/ooni/probe-cli/internal/colors"
|
"github.com/ooni/probe-cli/internal/colors"
|
||||||
)
|
)
|
||||||
|
@ -17,3 +22,86 @@ func Fatal(err error) {
|
||||||
fmt.Fprintf(os.Stderr, "\n %s %s\n\n", colors.Red("Error:"), err)
|
fmt.Fprintf(os.Stderr, "\n %s %s\n\n", colors.Red("Error:"), err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finds the control character sequences (like colors)
|
||||||
|
var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d")
|
||||||
|
|
||||||
|
func EscapeAwareRuneCountInString(s string) int {
|
||||||
|
n := utf8.RuneCountInString(s)
|
||||||
|
for _, sm := range ctrlFinder.FindAllString(s, -1) {
|
||||||
|
n -= utf8.RuneCountInString(sm)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func RightPad(str string, length int) string {
|
||||||
|
return str + strings.Repeat(" ", length-EscapeAwareRuneCountInString(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapString wraps the given string within lim width in characters.
|
||||||
|
//
|
||||||
|
// Wrapping is currently naive and only happens at white-space. A future
|
||||||
|
// version of the library will implement smarter wrapping. This means that
|
||||||
|
// pathological cases can dramatically reach past the limit, such as a very
|
||||||
|
// long word.
|
||||||
|
// This is taken from: https://github.com/mitchellh/go-wordwrap/tree/f253961a26562056904822f2a52d4692347db1bd
|
||||||
|
func WrapString(s string, lim uint) string {
|
||||||
|
// Initialize a buffer with a slightly larger size to account for breaks
|
||||||
|
init := make([]byte, 0, len(s))
|
||||||
|
buf := bytes.NewBuffer(init)
|
||||||
|
|
||||||
|
var current uint
|
||||||
|
var wordBuf, spaceBuf bytes.Buffer
|
||||||
|
|
||||||
|
for _, char := range s {
|
||||||
|
if char == '\n' {
|
||||||
|
if wordBuf.Len() == 0 {
|
||||||
|
if current+uint(spaceBuf.Len()) > lim {
|
||||||
|
current = 0
|
||||||
|
} else {
|
||||||
|
current += uint(spaceBuf.Len())
|
||||||
|
spaceBuf.WriteTo(buf)
|
||||||
|
}
|
||||||
|
spaceBuf.Reset()
|
||||||
|
} else {
|
||||||
|
current += uint(spaceBuf.Len() + wordBuf.Len())
|
||||||
|
spaceBuf.WriteTo(buf)
|
||||||
|
spaceBuf.Reset()
|
||||||
|
wordBuf.WriteTo(buf)
|
||||||
|
wordBuf.Reset()
|
||||||
|
}
|
||||||
|
buf.WriteRune(char)
|
||||||
|
current = 0
|
||||||
|
} else if unicode.IsSpace(char) {
|
||||||
|
if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
|
||||||
|
current += uint(spaceBuf.Len() + wordBuf.Len())
|
||||||
|
spaceBuf.WriteTo(buf)
|
||||||
|
spaceBuf.Reset()
|
||||||
|
wordBuf.WriteTo(buf)
|
||||||
|
wordBuf.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
spaceBuf.WriteRune(char)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
wordBuf.WriteRune(char)
|
||||||
|
|
||||||
|
if current+uint(spaceBuf.Len()+wordBuf.Len()) > lim && uint(wordBuf.Len()) < lim {
|
||||||
|
buf.WriteRune('\n')
|
||||||
|
current = 0
|
||||||
|
spaceBuf.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if wordBuf.Len() == 0 {
|
||||||
|
if current+uint(spaceBuf.Len()) <= lim {
|
||||||
|
spaceBuf.WriteTo(buf)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spaceBuf.WriteTo(buf)
|
||||||
|
wordBuf.WriteTo(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user