├── .github └── workflows │ ├── go.yml │ ├── release.yml │ └── wospm.yml ├── CODE_OF_CONDUCT ├── CONTRIBUTING.md ├── README.md ├── go.mod ├── go.sum ├── lib ├── parser │ └── parser.go └── repo │ └── repo.go └── main.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Test & Build 2 | on: [push, pull_request] 3 | jobs: 4 | 5 | test-build: 6 | name: Test & Build 7 | runs-on: ubuntu-latest 8 | steps: 9 | 10 | - name: Set up Go 1.13 11 | uses: actions/setup-go@v1 12 | with: 13 | go-version: 1.13 14 | id: go 15 | 16 | - name: Check out code into the Go module directory 17 | uses: actions/checkout@v1 18 | 19 | - name: Build 20 | run: go build ./... 21 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build And Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Checkout 14 | uses: actions/checkout@v2 15 | - 16 | name: Unshallow 17 | run: git fetch --prune --unshallow 18 | - 19 | name: Set up Go 20 | uses: actions/setup-go@v1 21 | with: 22 | go-version: 1.13.x 23 | - 24 | name: Run GoReleaser 25 | uses: goreleaser/goreleaser-action@v1 26 | with: 27 | version: latest 28 | args: release --rm-dist 29 | key: ${{ secrets.YOUR_PRIVATE_KEY }} 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/wospm.yml: -------------------------------------------------------------------------------- 1 | name: WOSPM Checker 2 | on: [push] 3 | 4 | jobs: 5 | wospm_checker: 6 | runs-on: ubuntu-latest 7 | name: WOSPM Checker 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v2 11 | - name: WOSPM Checker Github Action 12 | uses: WOSPM/wospm-checker-github-action@v1 13 | - name: Upload HTML Report When Success 14 | uses: actions/upload-artifact@v1 15 | with: 16 | name: HTML Report 17 | path: wospm.html 18 | - name: Upload HTML Report When Failed 19 | uses: actions/upload-artifact@v1 20 | if: failure() 21 | with: 22 | name: HTML Report 23 | path: wospm.html 24 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html) 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq) 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Welcome! 2 | 3 | If you are reading this document then you are interested in contributing to hacker-laws-cli, and that's awesome! 4 | 5 | All contributions are welcome: use-cases, documentation, code, patches, bug reports, feature requests, etc. You do not need to be a programmer to speak up! 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hacker Laws CLI 2 | 3 | ![Build And Release](https://github.com/umutphp/hacker-laws-cli/workflows/Build%20And%20Release/badge.svg) ![WOSPM Checker](https://github.com/umutphp/hacker-laws-cli/workflows/WOSPM%20Checker/badge.svg) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](CONTRIBUTING.md) 4 | 5 | A simple command line tool to give you a fancy command line interface to dive into laws, theories, principles and patterns listed in the repo [hacker-laws](https://github.com/dwmkerr/hacker-laws) by [Dave Kerr](https://github.com/dwmkerr). 6 | 7 | ## How To Install 8 | 9 | ### Basic 10 | 11 | Follow the steps; 12 | 13 | ```bash 14 | git clone git@github.com:umutphp/hacker-laws-cli.git 15 | 16 | cd hacker-laws-cli 17 | 18 | go run main.go 19 | ``` 20 | 21 | ### Build as binary 22 | 23 | Follow the steps; 24 | 25 | ```bash 26 | git clone git@github.com:umutphp/hacker-laws-cli.git 27 | 28 | cd hacker-laws-cli 29 | 30 | sudo go build -o /usr/local/bin/hacker-laws-cli . 31 | 32 | hacker-laws-cli list 33 | ``` 34 | 35 | ### Download and use official binary 36 | 37 | Visit the [latest release page](https://github.com/umutphp/hacker-laws-cli/releases/latest) and download the binary correspondingly. 38 | 39 | ```bash 40 | wget -O /usr/local/bin/hacker-laws-cli https://latest-binary-url 41 | 42 | hacker-laws-cli 43 | ``` 44 | 45 | ## How To Use 46 | 47 | The build will create an executable with name *hacker-laws-cli*. 48 | 49 | Execute it without argument to see the argument option; 50 | 51 | ```bash 52 | $ ./hacker-laws-cli 53 | Options for the command: 54 | help To display argument list. 55 | list To list the laws and principles. 56 | random To display random law or principles. 57 | ``` 58 | 59 | Sample execution #1 60 | 61 | ```bash 62 | $ ./hacker-laws-cli list 63 | Laws 64 | - Amdahl's Law 65 | - The Broken Windows Theory 66 | - Brooks' Law 67 | - Conway's Law 68 | - Cunningham's Law 69 | - Dunbar's Number 70 | - Gall's Law 71 | - Goodhart's Law 72 | - Hanlon's Razor 73 | - Hofstadter's Law 74 | - Hutber's Law 75 | - The Hype Cycle & Amara's Law 76 | - Hyrum's Law (The Law of Implicit Interfaces) 77 | - Metcalfe's Law 78 | - Moore's Law 79 | - Murphy's Law / Sod's Law 80 | - Occam's Razor 81 | - Parkinson's Law 82 | - Premature Optimization Effect 83 | - Putt's Law 84 | - Reed's Law 85 | - The Law of Conservation of Complexity (Tesler's Law) 86 | - The Law of Leaky Abstractions 87 | - The Law of Triviality 88 | - The Unix Philosophy 89 | - The Spotify Model 90 | - Wadler's Law 91 | Principles 92 | - Wheaton's Law 93 | - The Dilbert Principle 94 | - The Pareto Principle (The 80/20 Rule) 95 | - The Peter Principle 96 | - The Robustness Principle (Postel's Law) 97 | - SOLID 98 | - The Single Responsibility Principle 99 | - The Open/Closed Principle 100 | - The Liskov Substitution Principle 101 | - The Interface Segregation Principle 102 | - The Dependency Inversion Principle 103 | - The DRY Principle 104 | - The KISS principle 105 | - YAGNI 106 | ``` 107 | 108 | Sample execution #2 109 | 110 | ```bash 111 | $ ./hacker-laws-cli random 112 | 113 | ----------------------------------------------------- 114 | The Peter Principle 115 | ----------------------------------------------------- 116 | 117 | The Peter Principle on Wikipedia 118 | 119 | > People in a hierarchy tend to rise to their "level of incompetence". 120 | > 121 | > _Laurence J. Peter_ 122 | 123 | A management concept developed by Laurence J. Peter, the Peter Principle observes that people who are good at their jobs are promoted, until they reach a level where they are no longer successful (their "level of incompetence". At this point, as they are more senior, they are less likely to be removed from the organisation (unless they perform spectacularly badly) and will continue to reside in a role which they have few intrinsic skills at, as their original skills which made them successful are not necessarily the skills required for their new jobs. 124 | 125 | This is of particular interest to engineers - who initial start out in deeply technical roles, but often have a career path which leads to _managing_ other engineers - which requires a fundamentally different skills-set. 126 | 127 | See Also: 128 | 129 | - The Dilbert Principle 130 | - Putt's Law 131 | 132 | ----------------------------------------------------- 133 | github.com/dwmkerr/hacker-laws by Dave Kerr 134 | ----------------------------------------------------- 135 | ``` 136 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module hacker-laws-cli 2 | 3 | go 1.13 4 | 5 | require github.com/fatih/color v1.9.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= 2 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 3 | github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= 4 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 5 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 6 | github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= 7 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 8 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 9 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= 10 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 11 | -------------------------------------------------------------------------------- /lib/parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "strings" 5 | 6 | "hacker-laws-cli/lib/repo" 7 | ) 8 | 9 | func Parse(readme string, r *repo.Repo) { 10 | var cat = 0 11 | 12 | var parseStatus = false 13 | var content repo.Content 14 | for _, line := range strings.Split(strings.TrimSuffix(readme, "\n"), "\n") { 15 | if IsCategory(line) { 16 | if !IsCategoryIgnored(line) { 17 | if LineToTitle(line) == "Laws" { 18 | cat = 0 19 | } else { 20 | if cat == 0 { 21 | r.Categories[cat].Contents = append(r.Categories[cat].Contents, content) 22 | content = repo.NewContent("", "") 23 | } 24 | 25 | cat = 1 26 | } 27 | 28 | parseStatus = true 29 | 30 | continue 31 | } else { 32 | parseStatus = false 33 | } 34 | } 35 | 36 | if parseStatus == true { 37 | if IsContent(line) { 38 | if content.Title != "" { 39 | r.Categories[cat].Contents = append(r.Categories[cat].Contents, content) 40 | } 41 | 42 | content = repo.NewContent(LineToTitle(line), "") 43 | continue 44 | } 45 | 46 | content.Body = content.Body + line + "\n" 47 | } 48 | } 49 | 50 | r.Categories[cat].Contents = append(r.Categories[cat].Contents, content) 51 | } 52 | 53 | func IsCategory(line string) bool { 54 | return strings.HasPrefix(line, "## ") && !strings.HasPrefix(line, "### ") 55 | } 56 | 57 | func IsContent(line string) bool { 58 | return strings.HasPrefix(line, "### ") 59 | } 60 | 61 | func IsCategoryIgnored(line string) bool { 62 | ignoreList := []string{"Reading List", "Contributing", "TODO", "Introduction", "Translations", "Online Resources", "Podcast"} 63 | str := LineToTitle(line) 64 | for _, s := range ignoreList { 65 | if s == str { 66 | return true 67 | } 68 | } 69 | 70 | return false 71 | } 72 | 73 | func LineToTitle(line string) string { 74 | line = strings.Replace(line, "#", "", -1) 75 | line = strings.Trim(line, " ") 76 | 77 | return line 78 | } 79 | -------------------------------------------------------------------------------- /lib/repo/repo.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | "strings" 8 | "regexp" 9 | 10 | "github.com/fatih/color" 11 | ) 12 | 13 | type Repo struct { 14 | Categories []Category 15 | } 16 | 17 | type Category struct { 18 | Name string 19 | Contents []Content 20 | } 21 | 22 | type Content struct { 23 | Title string 24 | Slug string 25 | Body string 26 | } 27 | 28 | func New() Repo { 29 | return Repo{ 30 | Categories: []Category{ 31 | { 32 | Name: "Laws", 33 | Contents: []Content{}, 34 | }, 35 | { 36 | Name: "Principles", 37 | Contents: []Content{}, 38 | }, 39 | }, 40 | } 41 | } 42 | 43 | func NewContent(title string, body string) Content { 44 | return Content{ 45 | Title: title, 46 | Body: body, 47 | } 48 | } 49 | 50 | func (r *Repo) RandomContent() Content { 51 | rand.Seed(time.Now().UnixNano()) 52 | 53 | cat := rand.Intn(1000)% len(r.Categories) 54 | content := rand.Intn(1000)% len(r.Categories[cat].Contents) 55 | 56 | return r.Categories[cat].Contents[content] 57 | } 58 | 59 | func (r *Repo) DisplayContentList() { 60 | for _, cat := range r.Categories { 61 | fmt.Println(cat.Name) 62 | for _, content := range cat.Contents { 63 | fmt.Println("\t - ", content.Title) 64 | } 65 | } 66 | } 67 | 68 | func (c *Content) Display() { 69 | fmt.Println("") 70 | 71 | c.DisplayTitle(c.Title) 72 | 73 | for _, line := range strings.Split(strings.TrimSuffix(c.Body, "\n"), "\n") { 74 | c.DisplayLine(line) 75 | } 76 | 77 | c.DisplayFooter() 78 | } 79 | 80 | func (c *Content) DisplayTitle(title string) { 81 | d := color.New(color.FgGreen, color.Bold) 82 | d.Println("-----------------------------------------------------") 83 | d.Println(strings.Trim(title, "\n")) 84 | d.Println("-----------------------------------------------------") 85 | } 86 | 87 | func (c *Content) DisplayLine(line string) { 88 | if strings.HasPrefix(line, ">") { 89 | color.Yellow(strings.Trim(line, "\n")) 90 | return 91 | } 92 | 93 | //color.White(strings.Trim(line, "\n")) 94 | color.White(strings.Trim(StripMDTags(line), "\n")) 95 | } 96 | 97 | func (c *Content) DisplayFooter() { 98 | d := color.New(color.FgGreen, color.Bold) 99 | d.Println("-----------------------------------------------------") 100 | d.Println(" github.com/dwmkerr/hacker-laws by Dave Kerr ") 101 | d.Println("-----------------------------------------------------") 102 | } 103 | 104 | func StripMDTags(line string) string { 105 | re := regexp.MustCompile(`\[(.*?)\]\((.*?)\)`) 106 | line = re.ReplaceAllString(line, `$1`) 107 | 108 | return line 109 | } 110 | 111 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "io/ioutil" 7 | "os" 8 | "time" 9 | 10 | "hacker-laws-cli/lib/repo" 11 | "hacker-laws-cli/lib/parser" 12 | ) 13 | 14 | func main() { 15 | hackerLaws := repo.New() 16 | arguments := os.Args[1:] 17 | 18 | if len(arguments) == 0 { 19 | DisplayHelp() 20 | return 21 | } 22 | 23 | if arguments[0] == "help" { 24 | DisplayHelp() 25 | return 26 | } 27 | 28 | responseString := ParseHackerLawsRepo() 29 | 30 | parser.Parse(responseString, &hackerLaws) 31 | 32 | if arguments[0] == "list" { 33 | hackerLaws.DisplayContentList() 34 | return 35 | } 36 | 37 | if arguments[0] == "random" { 38 | randomContent := hackerLaws.RandomContent() 39 | randomContent.Display() 40 | return 41 | } 42 | } 43 | 44 | func DisplayHelp() { 45 | fmt.Println("Options for the command:") 46 | fmt.Println(PadLeft("help", " ", 12), " ", "To display argument list.") 47 | fmt.Println(PadLeft("list", " ", 12), " ", "To list the laws and principles.") 48 | fmt.Println(PadLeft("random", " ", 12), " ", "To display random law or principles.") 49 | } 50 | 51 | func PadLeft(str, pad string, lenght int) string { 52 | for { 53 | str = pad + str 54 | if len(str) >= lenght { 55 | return str[0:lenght] 56 | } 57 | } 58 | } 59 | 60 | func ParseHackerLawsRepo() string { 61 | home, err := os.UserHomeDir() 62 | 63 | if err != nil { 64 | fmt.Println(err) 65 | return "" 66 | } 67 | 68 | cacheFile := home + string(os.PathSeparator) + ".hlcache" 69 | 70 | if FileExists(cacheFile) && FileUptoDate(cacheFile) { 71 | content, err := ioutil.ReadFile(cacheFile) 72 | 73 | if err != nil { 74 | fmt.Println(err) 75 | } else { 76 | return string(content) 77 | } 78 | } 79 | 80 | response, err := http.Get("https://raw.githubusercontent.com/dwmkerr/hacker-laws/master/README.md") 81 | if err != nil { 82 | fmt.Println(err) 83 | } 84 | defer response.Body.Close() 85 | 86 | responseData, err := ioutil.ReadAll(response.Body) 87 | 88 | if err != nil { 89 | fmt.Println(err) 90 | return "" 91 | } 92 | 93 | responseString := string(responseData) 94 | 95 | file, err := os.Create(cacheFile) 96 | 97 | if err != nil { 98 | fmt.Println(err) 99 | return "" 100 | } 101 | 102 | file.WriteString(responseString) 103 | 104 | return responseString 105 | } 106 | 107 | func FileExists(filename string) bool { 108 | info, err := os.Stat(filename) 109 | if os.IsNotExist(err) { 110 | return false 111 | } 112 | return !info.IsDir() 113 | } 114 | 115 | func FileUptoDate(filename string) bool { 116 | info, _ := os.Stat(filename) 117 | modifiedtime := info.ModTime() 118 | 119 | return !IsOlderThanOneDay(modifiedtime) 120 | } 121 | 122 | func IsOlderThanOneDay(t time.Time) bool { 123 | return time.Now().Sub(t) > 24*time.Hour 124 | } 125 | --------------------------------------------------------------------------------