Skip to content

Commit 4b4048a

Browse files
committed
🎉 first version of the cli
1 parent 0e73c86 commit 4b4048a

File tree

17 files changed

+752
-56
lines changed

17 files changed

+752
-56
lines changed

.github/workflows/build.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Build
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
paths:
8+
- "**.go"
9+
- "Makefile"
10+
- "go.*"
11+
- ".github/workflows/build.yml"
12+
13+
permissions:
14+
contents: read
15+
16+
jobs:
17+
build:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: ⬇️ Checkout repository
21+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
22+
- name: 🔎 golangci-lint
23+
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9
24+
with:
25+
version: v2.7.1
26+
args: --timeout=300s
27+
only-new-issues: true
28+
- name: 📦 Build
29+
run: go build -o gli main.go
30+
unit:
31+
runs-on: [self-hosted, eu-west-2]
32+
steps:
33+
- name: ⬇️ Checkout repository
34+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
35+
- name: 🧪 Test
36+
run: go test ./...
37+
env:
38+
OSC_ACCESS_KEY: ${{ secrets.OSC_ACCESS_KEY }}
39+
OSC_SECRET_KEY: ${{ secrets.OSC_SECRET_KEY }}
40+
OSC_REGION: "eu-west-2"

.github/workflows/release.yaml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
goreleaser:
13+
environment: release
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
18+
19+
- name: Set up Go
20+
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6
21+
with:
22+
go-version-file: "./go.mod"
23+
24+
# - name: Import GPG key
25+
# id: import_gpg
26+
# uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6
27+
# with:
28+
# GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
29+
# PASSPHRASE: ${{ secrets.PASSPHRASE }}
30+
31+
- name: Run GoReleaser
32+
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6
33+
with:
34+
version: '~> v2'
35+
args: release --clean
36+
env:
37+
# GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
38+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Added by goreleaser init:
2+
dist/

.goreleaser.yaml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# This is an example .goreleaser.yml file with some sensible defaults.
2+
# Make sure to check the documentation at https://goreleaser.com
3+
4+
# The lines below are called `modelines`. See `:help modeline`
5+
# Feel free to remove those if you don't want/need to use them.
6+
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
7+
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
8+
9+
version: 2
10+
11+
before:
12+
hooks:
13+
# You may remove this if you don't use go modules.
14+
- go mod tidy
15+
# you may remove this if you don't need go generate
16+
- go generate ./...
17+
18+
builds:
19+
- env:
20+
- CGO_ENABLED=0
21+
goos:
22+
- linux
23+
- windows
24+
- darwin
25+
ldflags:
26+
- "-s -w -X {{ .ModulePath }}/cmd.Version={{.Version}}"
27+
28+
archives:
29+
- formats: [binary]
30+
# this name template makes the OS and Arch compatible with the results of `uname`.
31+
name_template: >-
32+
{{ .ProjectName }}_
33+
{{- title .Os }}_
34+
{{- if eq .Arch "amd64" }}x86_64
35+
{{- else if eq .Arch "386" }}i386
36+
{{- else }}{{ .Arch }}{{ end }}
37+
{{- if .Arm }}v{{ .Arm }}{{ end }}
38+
39+
changelog:
40+
use: github-native
41+
42+
release:
43+
draft: true

README.md

Lines changed: 31 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## **< Project Name >**
1+
## **GLI, An Experimental CLI for Outscale, written in Go**
22

33
[![Project Stage](https://docs.outscale.com/fr/userguide/_images/Project-Sandbox-yellow.svg)](https://docs.outscale.com/en/userguide/Open-Source-Projects.html) [![](https://dcbadge.limes.pink/api/server/HUVtY5gT6s?style=flat&theme=default-inverted)](https://discord.gg/HUVtY5gT6s)
44

@@ -32,50 +32,37 @@
3232

3333
## 🧭 Overview
3434

35-
**< Project Name >** is a <short description of what the project does, e.g., "CLI tool to manage...">.
36-
37-
Key features:
38-
- <Feature 1>
39-
- <Feature 2>
40-
- <Feature 3>
35+
**CLI** is an experimental CLI for the Outscale APIs, written in Go.
4136

4237
---
4338

4439
## ✅ Requirements
4540

46-
- <Dependency 1> (e.g., Rust, Go, Python 3.11+)
47-
- <Dependency 2> (e.g., Git)
4841
- Access to the OUTSCALE API (with appropriate credentials)
4942

5043
---
5144

5245
## ⚙ Installation
5346

54-
### Option 1: Download from Releases
55-
56-
Download the latest binary from the [Releases page](https://github.com/outscale/<project-name>/releases).
47+
Download the latest binary from the [Releases page](https://github.com/outscale/gli/releases).
5748

58-
### Option 2: Install from source
49+
---
5950

60-
```bash
61-
git clone https://github.com/outscale/<project-name>.git
62-
cd <project-name>
63-
<build or install command>
64-
````
51+
## 🛠 Configuration
6552

66-
Example (for Go projects):
6753

68-
```bash
69-
go install github.com/outscale/<project-name>@latest
70-
```
54+
The tool expects either environment variables or a configuration file.
7155

72-
---
56+
### Environment variables
7357

74-
## 🛠 Configuration
58+
The tool will try to read the following environment variables:
59+
* `OSC_ACCESS_KEY`
60+
* `OSC_SECRET_KEY`
61+
* `OSC_REGION`
7562

76-
\<Explain where the credentials or config are stored, e.g.:>
63+
### Profile file
7764

78-
The tool expects a configuration file at `~/.osc/config.json`.
65+
If no environment variables are defined, the tool will read `~/.osc/config.json` and look for the `default` profile.
7966

8067
### Example
8168

@@ -89,39 +76,39 @@ The tool expects a configuration file at `~/.osc/config.json`.
8976
}
9077
```
9178

92-
Use the `--profile` flag to select another profile.
79+
Use `OSC_CONFIG_FILE` to define an alternate config file and `OSC_PROFILE` an alternate profile.
9380

9481
---
9582

9683
## 🚀 Usage
9784

9885
```bash
99-
<command> [OPTIONS]
86+
gli <command> <api call>
10087
```
10188

89+
### Commands
90+
91+
| Command | Description |
92+
| ------- | ----------- |
93+
| `oapi` | Call OAPI |
94+
| `version` | Display version |
95+
10296
### Options
10397

104-
| Option | Description |
105-
| ------------------ | -------------------------------------- |
106-
| `-f, --flag` | What this flag does |
107-
| `-c, --count <N>` | Run N times |
108-
| `--profile <name>` | Use a specific profile from the config |
109-
| `-v, --version` | Print version and exit |
98+
| Option | Description |
99+
| ------------------ | ----------- |
100+
| `-v, --verbose` | Dump HTTP request and response |
101+
| `-h, --help` | Help about a command |
110102

111103
---
112104

113105
## 💡 Examples
114106

115-
### Basic usage
116-
117-
```bash
118-
<command>
119-
```
120-
121-
### With options
107+
### ReadVms
122108

109+
List all VMs in the `running` state:
123110
```bash
124-
<command> --flag value --profile test
111+
gli oapi ReadVms --Filters.VmStateNames running
125112
```
126113

127114
### Using `jq` to filter JSON output
@@ -134,9 +121,9 @@ jq '.[] | select(.ResponseStatusCode != 200)' logs.json
134121

135122
## 📜 License
136123

137-
**< Project Name >** is released under the < License Name > license.
124+
**<GLI** is released under the BSD 3-Clause license.
138125

139-
© < Year > Outscale SAS
126+
© 2026 Outscale SAS
140127

141128
See [LICENSE](./LICENSE) for full details.
142129

@@ -147,15 +134,3 @@ See [LICENSE](./LICENSE) for full details.
147134
We welcome contributions!
148135

149136
Please read our [Contributing Guidelines](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) before submitting a pull request.
150-
151-
---
152-
153-
### Notes for reuse:
154-
- Replace all `<...>` placeholders with your content.
155-
- You can prefill the `Project Stage` badge with values like:
156-
- `Project-Incubating-blue.svg`
157-
- `Project-Graduated-green.svg`
158-
- You may include platform-specific instructions (macOS/Linux/Windows) in collapsible `<details>` blocks if needed.
159-
160-
>Labels are centrally managed in the outscale/.github repository (labels.yml).
161-
>This repo includes a workflow (.github/workflows/sync-labels.yml) that syncs labels from there.

cmd/oapi.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
Copyright © 2026 NAME HERE <EMAIL ADDRESS>
3+
*/
4+
package cmd
5+
6+
import (
7+
"context"
8+
"encoding/json"
9+
"os"
10+
"reflect"
11+
"strings"
12+
13+
"github.com/outscale/gli/pkg/debug"
14+
"github.com/outscale/gli/pkg/errors"
15+
"github.com/outscale/gli/pkg/flags"
16+
"github.com/outscale/gli/pkg/sdk"
17+
"github.com/outscale/osc-sdk-go/v3/pkg/middleware"
18+
"github.com/outscale/osc-sdk-go/v3/pkg/osc"
19+
"github.com/outscale/osc-sdk-go/v3/pkg/profile"
20+
"github.com/outscale/osc-sdk-go/v3/pkg/utils"
21+
"github.com/spf13/cobra"
22+
)
23+
24+
// oapiCmd represents the oapi command
25+
var oapiCmd = &cobra.Command{
26+
Use: "oapi",
27+
Short: "OAPI calls",
28+
Long: `Send commands to the Outscale API.`,
29+
}
30+
31+
func init() {
32+
rootCmd.AddCommand(oapiCmd)
33+
34+
c := reflect.TypeOf(&osc.Client{})
35+
debug.Println(c.PkgPath(), c.Name())
36+
for i := range c.NumMethod() {
37+
m := c.Method(i)
38+
if m.Type.NumIn() != 4 || m.Type.NumOut() != 2 || strings.HasSuffix(m.Name, "Raw") {
39+
continue
40+
}
41+
cmd := &cobra.Command{
42+
Use: m.Name,
43+
Run: func(cmd *cobra.Command, args []string) {
44+
oapi(cmd)
45+
},
46+
}
47+
arg := m.Type.In(2)
48+
flags.FromStruct(cmd.Flags(), arg, "")
49+
oapiCmd.AddCommand(cmd)
50+
}
51+
}
52+
53+
func oapi(cmd *cobra.Command) {
54+
debug.Println(cmd.Name() + " called")
55+
56+
p, err := profile.New()
57+
if err != nil {
58+
errors.ExitErr(err)
59+
}
60+
var opt middleware.MiddlewareChainOption
61+
if verbose, _ := cmd.Flags().GetBool("verbose"); verbose {
62+
opt = utils.WithLogging(sdk.VerboseLogger{})
63+
} else {
64+
opt = utils.WithoutLogging()
65+
}
66+
cl, err := osc.NewClient(p, opt)
67+
if err != nil {
68+
errors.ExitErr(err)
69+
}
70+
clt := reflect.TypeOf(cl)
71+
m, _ := clt.MethodByName(cmd.Name())
72+
argType := m.Type.In(2)
73+
arg := reflect.New(argType)
74+
err = flags.ToStruct(cmd.Flags(), arg, "")
75+
if err != nil {
76+
errors.ExitErr(err)
77+
}
78+
ctx := context.Background()
79+
res := reflect.ValueOf(cl).MethodByName(cmd.Name()).Call([]reflect.Value{
80+
reflect.ValueOf(ctx),
81+
arg.Elem(),
82+
})
83+
debug.Println(len(res), "results")
84+
if !res[1].IsNil() {
85+
err = res[1].Interface().(error)
86+
if oerr := osc.AsErrorResponse(err); oerr != nil {
87+
enc := json.NewEncoder(os.Stderr)
88+
enc.SetIndent("", " ")
89+
_ = enc.Encode(oerr)
90+
os.Exit(1)
91+
}
92+
errors.ExitErr(err)
93+
}
94+
enc := json.NewEncoder(os.Stdout)
95+
enc.SetIndent("", " ")
96+
_ = enc.Encode(res[0].Interface())
97+
}

0 commit comments

Comments
 (0)