Skip to content

Commit

Permalink
feat: inital commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ShuhaoQing committed Aug 1, 2024
1 parent 8bfc72b commit a756f2d
Show file tree
Hide file tree
Showing 86 changed files with 7,417 additions and 0 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/cd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: CD

on:
release:
types:
- released
- prereleased

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
upload-cli-artifact-to-oss:
strategy:
matrix:
include:
- arch: amd64
os: linux
- arch: amd64
os: darwin
- arch: arm64
os: linux
- arch: arm64
os: darwin
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Check if version is semantic
run: echo "${{ github.ref_name }}" | grep -q -E '^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-((0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$'
- name: Checkout
uses: actions/checkout@v3
- name: Set up go
uses: actions/setup-go@v4
- name: Build cocli
run: |
make build-binary
mv bin/cocli bin/cocli-${{ matrix.os }}-${{ matrix.arch }}
- name: Upload release artifact
uses: softprops/action-gh-release@v2
with:
files: bin/cocli-${{ matrix.os }}-${{ matrix.arch }}
25 changes: 25 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up go
uses: actions/setup-go@v3
with:
go-version: 1.22
- name: Cache
if: success()
uses: actions/cache@v3
with:
path: |
~/.cache/cocli/${{ runner.os }}/${{ runner.arch }}/go/pkg/mod
key: ${{ runner.os }}-golang-${{ hashFiles('**/go.sum', 'make/**') }}
restore-keys: |
${{ runner.os }}-golang-
- name: Lint
run: make lint
- name: Build
run: make build
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Autogenerated by makego. DO NOT EDIT.
/.DS_Store
/.env/
/.idea/
/.tmp/
/.vscode/
/bin/
/cmd/cocli/cocli
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
MAKEGO := make/go
PROJECT := cocli
GO_MODULE := github.com/coscene-io/cocli

include make/cocli/all.mk
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# coScene CLI (coCLI)

`cocli` 是基于 [cobra](https://github.com/spf13/cobra) 开发的 coScene 命令行工具,用于与 coScene 平台进行交互,便于用户在终端中对 coScene 平台的资源进行管理。

具体的使用方法请参考 [coScene CLI 文档](https://docs.coscene.cn/docs/category/cli)

另外,`cocli` 所有的命令都可以通过添加 `-h` 参数查看帮助文档。

## 本地安装

### 克隆代码

```shell
git clone https://github.com/coscene-io/cocli.git
```
### 本地构建可执行文件

```shell
# 进入项目目录
cd cocli

# 构建可执行文件, 生成的可执行文件在 `./bin` 目录下
make build-binary

# 将可执行文件移动到任意系统路径 PATH 下以便全局使用,当前示例移动到 `/usr/local/bin/` 目录下
mv bin/cocli /usr/local/bin/

# 运行 cocli 命令, 查看帮助文档, 确认安装成功
cocli -h
```

207 changes: 207 additions & 0 deletions api/action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
// Copyright 2024 coScene
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package api

import (
"context"
"fmt"
"strings"

openv1alpha1connect "buf.build/gen/go/coscene-io/coscene-openapi/connectrpc/go/coscene/openapi/dataplatform/v1alpha1/services/servicesconnect"
openv1alpha1resource "buf.build/gen/go/coscene-io/coscene-openapi/protocolbuffers/go/coscene/openapi/dataplatform/v1alpha1/resources"
openv1alpha1service "buf.build/gen/go/coscene-io/coscene-openapi/protocolbuffers/go/coscene/openapi/dataplatform/v1alpha1/services"
"connectrpc.com/connect"
"github.com/coscene-io/cocli/internal/constants"
"github.com/coscene-io/cocli/internal/name"
"github.com/samber/lo"
)

type ActionInterface interface {
// GetByName gets an action by name.
GetByName(ctx context.Context, actionName *name.Action) (*openv1alpha1resource.Action, error)

// ListAllActions lists all actions in the current organization.
ListAllActions(ctx context.Context, listOpts *ListActionsOptions) ([]*openv1alpha1resource.Action, error)

// CreateActionRun creates an action run.
CreateActionRun(ctx context.Context, action *openv1alpha1resource.Action, record *name.Record) error

// ListAllActionRuns lists all action runs in the current organization.
ListAllActionRuns(ctx context.Context, listOpts *ListActionRunsOptions) ([]*openv1alpha1resource.ActionRun, error)

// ActionId2Name converts an action id or name to an action name.
ActionId2Name(ctx context.Context, actionIdOrName string, projectNameStr *name.Project) (*name.Action, error)
}

type actionClient struct {
actionServiceClient openv1alpha1connect.ActionServiceClient
actionRunServiceClient openv1alpha1connect.ActionRunServiceClient
}

func NewActionClient(
actionServiceClient openv1alpha1connect.ActionServiceClient,
actionRunServiceClient openv1alpha1connect.ActionRunServiceClient,
) ActionInterface {
return &actionClient{
actionServiceClient: actionServiceClient,
actionRunServiceClient: actionRunServiceClient,
}
}

type ListActionsOptions struct {
Parent string
}

type ListActionRunsOptions struct {
Parent string
RecordNames []*name.Record
}

func (c *actionClient) GetByName(ctx context.Context, actionName *name.Action) (*openv1alpha1resource.Action, error) {
req := connect.NewRequest(&openv1alpha1service.GetActionRequest{
Name: actionName.String(),
})
res, err := c.actionServiceClient.GetAction(ctx, req)
if err != nil {
return nil, fmt.Errorf("failed to get action: %w", err)
}

return res.Msg, nil
}

func (c *actionClient) ListAllActions(ctx context.Context, listOpts *ListActionsOptions) ([]*openv1alpha1resource.Action, error) {
filter := c.filter(listOpts)

var (
skip = 0
ret []*openv1alpha1resource.Action
)

for {
req := connect.NewRequest(&openv1alpha1service.ListActionsRequest{
Parent: listOpts.Parent,
Filter: filter,
Skip: int32(skip),
PageSize: int32(constants.MaxPageSize),
})
res, err := c.actionServiceClient.ListActions(ctx, req)
if err != nil {
return nil, fmt.Errorf("failed to list actions: %w", err)
}

ret = append(ret, res.Msg.Actions...)
if len(res.Msg.Actions) < constants.MaxPageSize {
break
}
skip += constants.MaxPageSize
}

return ret, nil
}

func (c *actionClient) filter(opt *ListActionsOptions) string {
return ""
}

func (c *actionClient) CreateActionRun(ctx context.Context, action *openv1alpha1resource.Action, record *name.Record) error {
req := connect.NewRequest(&openv1alpha1service.CreateActionRunRequest{
Parent: record.Project().String(),
ActionRun: &openv1alpha1resource.ActionRun{
Action: action,
Records: []*openv1alpha1resource.Record{
{
Name: record.String(),
},
},
},
})
_, err := c.actionRunServiceClient.CreateActionRun(ctx, req)
if err != nil {
return fmt.Errorf("failed to create action run: %w", err)
}

return nil
}

func (c *actionClient) ListAllActionRuns(ctx context.Context, listOpts *ListActionRunsOptions) ([]*openv1alpha1resource.ActionRun, error) {
filter := c.filterRun(listOpts)

var (
skip = 0
ret []*openv1alpha1resource.ActionRun
)

for {
req := connect.NewRequest(&openv1alpha1service.ListActionRunsRequest{
Parent: listOpts.Parent,
Filter: filter,
Skip: int32(skip),
PageSize: int32(constants.MaxPageSize),
})
res, err := c.actionRunServiceClient.ListActionRuns(ctx, req)
if err != nil {
return nil, fmt.Errorf("failed to list action runs: %w", err)
}

ret = append(ret, res.Msg.ActionRuns...)
if len(res.Msg.ActionRuns) < constants.MaxPageSize {
break
}
skip += constants.MaxPageSize
}

return ret, nil
}

func (c *actionClient) filterRun(opts *ListActionRunsOptions) string {
var filters []string
if opts.RecordNames != nil {
filters = append(
filters,
fmt.Sprintf(
"match.records==[%s]",
strings.Join(lo.Map(opts.RecordNames, func(r *name.Record, _ int) string { return "\"" + r.String() + "\"" }), ","),
),
)
}
return strings.Join(filters, " && ")
}

func (c *actionClient) ActionId2Name(ctx context.Context, actionIdOrName string, projectName *name.Project) (*name.Action, error) {
actionName, err := name.NewAction(actionIdOrName)
if err == nil {
return actionName, nil
}

if !name.IsUUID(actionIdOrName) {
return nil, fmt.Errorf("invalid action id or name: %s", actionIdOrName)
}

// Try fetching assuming it's a project action
if act, err := c.GetByName(ctx, &name.Action{
ProjectID: projectName.ProjectID,
ID: actionIdOrName,
}); err == nil {
return name.NewAction(act.Name)
}

if act, err := c.GetByName(ctx, &name.Action{
ID: actionIdOrName,
}); err == nil {
return name.NewAction(act.Name)
}

return nil, fmt.Errorf("failed to convert action id to name: %s", actionIdOrName)
}
Loading

0 comments on commit a756f2d

Please sign in to comment.