gh
Github CLI
> progo-cli
Clear is better than clever.
// 1. Go Modul erstellen (erzeugt go.mod)
> go mod init crossnative.com/progo-cli
// 2. Datei main.go erstellen
package main
import "fmt"
func main() {
fmt.Println("Hello Go Proverbs!")
}
Ausführen
go build . // 1. Code kompilieren
./progo-cli // 2. Binary ausführen
go run . // Code kompilieren und ausführen
// 1. Proverbs Modul Dependency
> go get github.com/jboursiquot/go-proverbs
// 2. Go Modul Descriptor go.mod
module crossnative.com/progo-cli
go 1.23
require github.com/jboursiquot/go-proverbs v0.0.2
package main
import (
"fmt"
"github.com/jboursiquot/go-proverbs"
)
func main() {
fmt.Println(proverbs.Random())
}
// Ausgabe `go run .`
&{Make the zero value useful. http://youtube.com/322}
package main
import (
"fmt"
"github.com/jboursiquot/go-proverbs"
)
func main() {
// Pointer Variable auf Proverb Struct
var p *proverbs.Proverb = proverbs.Random()
fmt.Println(p)
}
// Struct statt Klasse
type Proverb struct {
Saying string
Link string
}
package main
import (
"fmt"
"github.com/jboursiquot/go-proverbs"
)
func main() {
var p *proverbs.Proverb = proverbs.Random()
// Zugriff auf Property des Structs
fmt.Println(p.Saying)
}
// Ausgabe `go run .`
Make the zero value useful.
> pro -count=3
Clear is better than clever.
Documentation is for users.
Don't panic.
import (
"flag"
"fmt"
"github.com/jboursiquot/go-proverbs"
)
func main() {
// 1. Flags definieren
var count int
flag.IntVar(&count, "count", 1, "proverb count")
// 2. Flags parsen
flag.Parse()
// 3. Ausgabe in Schleife
for i := 0; i < count; i++ {
var p *proverbs.Proverb = proverbs.Random()
fmt.Println(p.Saying)
}
}
// Compile mit `go build .`, Ausgabe mit `progo-cli -count=2`
Make the zero value useful.
Reflection is never clear.
> progo-cli -count=
flag needs an argument: -count
Usage of ./progo-cli:
-count int
proverb count (default 1)
import (
"flag"
"fmt"
"github.com/jboursiquot/go-proverbs"
)
func main() {
// 1. Flags definieren
var count int
flag.IntVar(&count, "count", 1, "proverb count")
// 2. Flags parsen
flag.Parse()
// 3. Ausgabe in Schleife
for range count {
p := proverbs.Random()
fmt.Println(p.Saying)
}
}
// Compile mit `go build .`, Ausgabe mit `pro -count=2`
Make the zero value useful.
Reflection is never clear.
# Aufbau Cobra CLIs
progo-cli {command} {subcommand} {args..} {flags..}
# Cobra CLI installieren
go install github.com/spf13/cobra-cli@latest
# Cobra Projekt aufsetzen
cobra-cli init
# Projektstruktur
├── cmd
│ ├── root.go
│ ├── cmd1.go
│ ├── print.go
│ └── {...}.go
└── main.go
var printCmd = &cobra.Command{
Use: "print",
Short: "Prints some proverbs",
Long: `Nifty tool that prints the Proverbs in your terminal.`,
Args: cobra.MatchAll(cobra.MaximumNArgs(2), cobra.OnlyValidArgs),
Run: func(cmd *cobra.Command, args []string) {
// 1. count Flag abfragen
count, _ := cmd.Flags().GetInt("count")
// 2. Ausgabe in Schleife
for range count {
p := proverbs.Random()
fmt.Println(p.Saying)
}
},
}
func init() {
rootCmd.AddCommand(printCmd)
// Flags
printCmd.Flags().IntP("count", "c", 1, "Count of proverbs to print.")
}
var printCmd = &cobra.Command{
Use: "print",
Short: "Prints some proverbs",
Long: `Nifty tool that prints the Proverbs in your terminal.`,
Args: cobra.MatchAll(cobra.MaximumNArgs(2), cobra.OnlyValidArgs),
RunE: func(cmd *cobra.Command, args []string) error {
// 1. count Flag abfragen
count, err := cmd.Flags().GetInt("count")
if err != nil {
// 1a. Return mit Fehler
return err
}
// 2. Ausgabe in Schleife
for range count {
p := proverbs.Random()
fmt.Println(p.Saying)
}
// 3. Return ohne Fehler
return nil
},
}
# Query cat API
curl -s http://localhost:8080/api/cats | jq
[
{
"name": "Ginger"
}
]
# 1. Verzeichnis erstellen
mkdir cats
cd cats
# 2. Go Modul aufsetzen
go mod init crossnative.com/cats
# 3. Go Datei erstellen
touch main.go
func catAPIHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Meow!")
w.WriteHeader(http.StatusOK)
}
func main() {
http.HandleFunc("GET /api/cats", catAPIHandler)
http.ListenAndServe(":8080", nil)
}
// 1. Struct mit JSON Tag definieren
type Cat struct {
Name string `json:"name"`
}
func catAPIHandler(w http.ResponseWriter, r *http.Request) {
// 2. Slice erstellen
cats := make([]Cat, 1)
// 3. Struct erstellen und einfügen
cats[0] = Cat{Name: "Ginger"}
// 4. JSON rendern
json.NewEncoder(w).Encode(cats)
}
func main() {
http.HandleFunc("GET /api/cats", catAPIHandler)
http.ListenAndServe(":8080", nil)
}
cat_api_test.go
func TestCatAPIHandler(t *testing.T) {
// 1. Test Request erstellen
req, _ := http.NewRequest("GET", "/api/cats", nil)
// 2. HTTP Recorder erstellen (ist http.ResponseWriter)
rec := httptest.NewRecorder()
// 3. Handler aufrufen
catAPIHandler(rec, req)
// 4. Response prüfen
if rec.Code != http.StatusOK {
t.Errorf("got status %v expected %v", rec.Code, http.StatusOK)
}
}
go test -v ./...
=== RUN TestCatAPIHandler
--- PASS: TestCatAPIHandler (0.00s)
PASS
coverage: 50.0% of statements
ok crossnative.com/cats 0.127s coverage: 50.0% of statements
func indexHandler(w http.ResponseWriter, r *http.Request) {
tpl := template.Must(template.ParseFiles("index.html"))
tpl.Execute(w, nil)
}
func main() {
// 1. Router erzeugen
router := http.NewServeMux()
// 2. Handler registrieren
router.HandleFunc("/", indexHandler)
// 3. Server mit Router starten
http.ListenAndServe(":8080", router)
}
//go:embed assets
var assets embed.FS
func main() {
router := http.NewServeMux()
router.HandleFunc("/", indexHandler)
// Serve Files
router.Handle("/assets/", http.FileServer(http.FS(assets)))
http.ListenAndServe(":8080", router)
}
type Cat struct {
Name string
}
func indexHandler(w ResponseWriter, r *Request) {
// 1. Slice erstellen
cat := make([]Cat, 1)
// 2. Struct erstellen und einfügen
cat[0] = Cat{Name: "Ginger"}
// 3. Template rendern
tpl := template.Must(template.ParseFiles("index.html"))
tpl.Execute(w, cat)
}
<body>
<h1>Cats App</h1>
{{ range . }}
<h2>{{ .Name }}</h2>
{{ end }}
</body>
GET https://api.thecatapi.com/v1/breeds?limit=5
[
{
"id": "abys",
"name": "Abyssinian",
"image": {
"url": "https://cdn2.thecatapi.com/0XYvRd7oD.jpg"
}
},
{
"id": "aege",
"name": "Aegean",
"image": {
"url": "https://cdn2.thecatapi.com/ozEvzdVM-.jpg"
}
},
...
]
type Cat struct {
ID string `json:"id"`
Name string `json:"name"`
Image struct {
URL string `json:"url"`
} `json:"image"`
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
// 1. Cat API Aufruf (Fehler ignorieren)
resp, _ := http.Get("https://api.thecatapi.com/v1/breeds?limit=5")
// 2. Slice für Cat Structs
cat := make([]Cat, 5)
// 3. Response Body parsen
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
json.Unmarshal(body, &cat)
// 4. Template rendern
tpl.Execute(w, cat)
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
// 1. Cat API Aufruf
resp, err := http.Get("https://api.thecatapi.com/v1/breeds?limit=5")
// Fehler behandeln
if err != nil {
http.Error(w, "Cats API error", http.StatusInternalServerError)
return
}
// 2. Slice für Cat Structs
cat := make([]Cat, 5)
// 3. Response Body parsen
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
err := json.Unmarshal(body, &cat)
// TODO: Fehler behandeln
// 4. Template rendern
err := tpl.Execute(w, cat)
// TODO: Fehler behandeln
}
# 1. App Builder
FROM golang AS builder
WORKDIR /app
ADD . /app
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o build/cats .
# 2. App Container
FROM gcr.io/distroless/static
COPY --from=builder /app/build/cats /usr/bin/
EXPOSE 8080
ENTRYPOINT ["/usr/bin/cats"]