Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support github-actions plugins #1212

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions cmd/plugin/github-actions/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import (
"github.com/devstream-io/devstream/internal/pkg/configmanager"
"github.com/devstream-io/devstream/internal/pkg/plugin/githubactions/general"
"github.com/devstream-io/devstream/internal/pkg/statemanager"
"github.com/devstream-io/devstream/pkg/util/log"
)

// NAME is the name of this DevStream plugin.
const NAME = "github-actions"

// Plugin is the type used by DevStream core. It's a string.
type Plugin string

// Create implements the installation of some GitHub Actions workflows.
func (p Plugin) Create(options configmanager.RawOptions) (statemanager.ResourceStatus, error) {
return general.Create(options)
}

// Update implements the installation of some GitHub Actions workflows.
func (p Plugin) Update(options configmanager.RawOptions) (statemanager.ResourceStatus, error) {
return general.Update(options)
}

// Read implements the healthy check of GitHub Actions workflows.
func (p Plugin) Read(options configmanager.RawOptions) (statemanager.ResourceStatus, error) {
return general.Read(options)
}

// Delete implements the installation of some GitHub Actions workflows.
func (p Plugin) Delete(options configmanager.RawOptions) (bool, error) {
return general.Delete(options)
}

// DevStreamPlugin is the exported variable used by the DevStream core.
var DevStreamPlugin Plugin

func main() {
log.Infof("%T: %s is a plugin for DevStream. Use it with DevStream.\n", DevStreamPlugin, NAME)
}
9 changes: 9 additions & 0 deletions docs/plugins/github-actions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# github-actions plugin

TODO(dtm): Add your document here.

## Usage

``` yaml
--8<-- "github-actions.yaml"
```
23 changes: 23 additions & 0 deletions docs/plugins/github-actions.zh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# github-actions 插件

_该插件用于在项目中创建 Github Action Workflows_

## 用例

_该插件运行依赖以下环境变量:_

- GITHUB_TOKEN

请在使用插件前配置该环境变量。如果你不知道如何创建这个 TOKEN,可以查看以下文档:

- [创建个人访问 token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)

_如果你需要推送镜像到仓库,请配置以下环境变量:_

- IMAGE_REPO_PASSWORD: 该变量表示登陆仓库的密码

下面的内容是一个示例配置文件用于创建 Github Workflows:

``` yaml
--8<-- "github-actions.yaml"
```
31 changes: 31 additions & 0 deletions internal/pkg/plugin/githubactions/general/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package general

import (
"github.com/devstream-io/devstream/internal/pkg/configmanager"
"github.com/devstream-io/devstream/internal/pkg/plugininstaller"
"github.com/devstream-io/devstream/internal/pkg/plugininstaller/ci/cifile"
"github.com/devstream-io/devstream/pkg/util/log"
)

func Create(options map[string]interface{}) (map[string]interface{}, error) {
// Initialize Operator with Operations
operator := &plugininstaller.Operator{
PreExecuteOperations: plugininstaller.PreExecuteOperations{
setDefault,
validate,
},
ExecuteOperations: plugininstaller.ExecuteOperations{
preConfigGithub,
cifile.PushCIFiles,
},
GetStatusOperation: getState,
}

// Execute all Operations in Operator
status, err := operator.Execute(configmanager.RawOptions(options))
if err != nil {
return nil, err
}
log.Debugf("Return map: %v", status)
return status, nil
}
30 changes: 30 additions & 0 deletions internal/pkg/plugin/githubactions/general/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package general

import (
"github.com/devstream-io/devstream/internal/pkg/configmanager"
"github.com/devstream-io/devstream/internal/pkg/plugininstaller"
"github.com/devstream-io/devstream/internal/pkg/plugininstaller/ci/cifile"
)

func Delete(options map[string]interface{}) (bool, error) {
// Initialize Operator with Operations
operator := &plugininstaller.Operator{
PreExecuteOperations: plugininstaller.PreExecuteOperations{
setDefault,
validate,
},
ExecuteOperations: plugininstaller.ExecuteOperations{
//TODO(jiafeng meng): delete github secret
cifile.DeleteCIFiles,
},
GetStatusOperation: getState,
}

// Execute all Operations in Operator
_, err := operator.Execute(configmanager.RawOptions(options))
if err != nil {
return false, err
}

return true, nil
}
30 changes: 30 additions & 0 deletions internal/pkg/plugin/githubactions/general/githubactions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package general

import (
"github.com/devstream-io/devstream/internal/pkg/configmanager"
"github.com/devstream-io/devstream/internal/pkg/plugininstaller/ci/step"
"github.com/devstream-io/devstream/pkg/util/log"
"github.com/devstream-io/devstream/pkg/util/scm/github"
)

func preConfigGithub(options configmanager.RawOptions) error {
opts, err := newActionOptions(options)
if err != nil {
return err
}

stepConfigs := step.ExtractValidStepConfig(opts.Action)
githubClient, err := github.NewClient(opts.ProjectRepo)
if err != nil {
log.Debugf("init github client failed: %+v", err)
return err
}
for _, c := range stepConfigs {
err := c.ConfigGithub(githubClient)
if err != nil {
log.Debugf("githubaction config github failed: %+v", err)
return err
}
}
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package general_test

import (
"os"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestGitlabcedocker(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Plugin github-actions Suite")
}

var (
githubEnv, gitlabEnv string
)

var _ = BeforeSuite(func() {
githubEnv = os.Getenv("GITHUB_TOKEN")
gitlabEnv = os.Getenv("GITLAB_TOKEN")
err := os.Unsetenv("GITHUB_TOKEN")
Expect(err).Error().ShouldNot(HaveOccurred())
err = os.Unsetenv("GITLAB_TOKEN")
Expect(err).Error().ShouldNot(HaveOccurred())
})

var _ = AfterSuite(func() {
if githubEnv != "" {
os.Setenv("GITHUB_TOKEN", githubEnv)
}
if gitlabEnv != "" {
os.Setenv("GITLAB_TOKEN", gitlabEnv)
}
})
51 changes: 51 additions & 0 deletions internal/pkg/plugin/githubactions/general/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package general

import (
"github.com/mitchellh/mapstructure"

"github.com/devstream-io/devstream/internal/pkg/configmanager"
"github.com/devstream-io/devstream/internal/pkg/plugininstaller/ci/cifile"
"github.com/devstream-io/devstream/internal/pkg/plugininstaller/ci/step"
"github.com/devstream-io/devstream/pkg/util/log"
"github.com/devstream-io/devstream/pkg/util/scm"
"github.com/devstream-io/devstream/pkg/util/scm/git"
)

// actionOptions is the struct for configurations of the github-actions plugin.
type actionOptions struct {
SCM scm.SCMInfo `mapstructure:"scm"`
Action action `mapstructure:"action"`

// used in package
CIConfig *cifile.CIConfig `mapstructure:"ci"`
ProjectRepo *git.RepoInfo `mapstructure:"projectRepo"`
}

type action struct {
ConfigLocation string `mapstructure:"configLocation" validate:"required"`
ImageRepo *step.ImageRepoStepConfig `mapstructure:"imageRepo"`
Dingtalk *step.DingtalkStepConfig `mapstructure:"dingTalk"`
Sonarqube *step.SonarQubeStepConfig `mapstructure:"sonarqube"`
}

// newOptions create options by raw options
func newActionOptions(options configmanager.RawOptions) (actionOptions, error) {
var opts actionOptions
if err := mapstructure.Decode(options, &opts); err != nil {
return opts, err
}
return opts, nil
}

func (a *action) buildCIConfig(repoInfo *git.RepoInfo) *cifile.CIConfig {
ciConfig := &cifile.CIConfig{
Type: "github",
ConfigLocation: a.ConfigLocation,
}
// update ci render variables by plugins
rawConfigVars := step.GenerateCIFileVars(a, repoInfo)
rawConfigVars.Set("AppName", repoInfo.Repo)
ciConfig.Vars = rawConfigVars
log.Debugf("github-actions pipeline get render vars: %+v", ciConfig.Vars)
return ciConfig
}
61 changes: 61 additions & 0 deletions internal/pkg/plugin/githubactions/general/options_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package general

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"github.com/devstream-io/devstream/internal/pkg/plugininstaller/ci/cifile"
"github.com/devstream-io/devstream/internal/pkg/plugininstaller/ci/step"
"github.com/devstream-io/devstream/pkg/util/scm/git"
)

var _ = Describe("action struct", func() {
var (
a *action
imageRepoURL, user, repoName, configLocation string
r *git.RepoInfo
)
BeforeEach(func() {
imageRepoURL = "exmaple.com"
user = "test_user"
repoName = "test_repo"
configLocation = "123/workflows"
a = &action{
ConfigLocation: configLocation,
ImageRepo: &step.ImageRepoStepConfig{
URL: imageRepoURL,
User: user,
},
}
r = &git.RepoInfo{
Repo: repoName,
}
})
Context("buildCIConfig method", func() {
It("should work normal", func() {
var nilStepConfig *step.SonarQubeStepConfig
var nilDingTalkConfig *step.DingtalkStepConfig
ciConfig := a.buildCIConfig(r)
Expect(string(ciConfig.Type)).Should(Equal("github"))
Expect(ciConfig.ConfigLocation).Should(Equal(configLocation))
expectVars := cifile.CIFileVarsMap{
"SonarqubeSecretKey": "SONAR_SECRET_TOKEN",
"AppName": "test_repo",
"ImageRepoSecret": "IMAGE_REPO_SECRET",
"ImageRepoDockerSecret": "image-repo-auth",
"imageRepo": map[string]interface{}{
"url": "exmaple.com",
"user": "test_user",
},
"dingTalk": nilDingTalkConfig,
"DingTalkSecretKey": "DINGTALK_SECURITY_VALUE",
"DingTalkSecretToken": "DINGTALK_SECURITY_TOKEN",
"StepGlobalVars": "",
"configLocation": "123/workflows",
"sonarqube": nilStepConfig,
"GitlabConnectionID": "gitlabConnection",
}
Expect(ciConfig.Vars).Should(Equal(expectVars))
})
})
})
26 changes: 26 additions & 0 deletions internal/pkg/plugin/githubactions/general/read.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package general

import (
"github.com/devstream-io/devstream/internal/pkg/configmanager"
"github.com/devstream-io/devstream/internal/pkg/plugininstaller"
"github.com/devstream-io/devstream/pkg/util/log"
)

func Read(options map[string]interface{}) (map[string]interface{}, error) {
// Initialize Operator with Operations
operator := &plugininstaller.Operator{
PreExecuteOperations: plugininstaller.PreExecuteOperations{
setDefault,
validate,
},
GetStatusOperation: getState,
}

// Execute all Operations in Operator
status, err := operator.Execute(configmanager.RawOptions(options))
if err != nil {
return nil, err
}
log.Debugf("Return map: %v", status)
return status, nil
}
12 changes: 12 additions & 0 deletions internal/pkg/plugin/githubactions/general/state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package general

import (
"github.com/devstream-io/devstream/internal/pkg/configmanager"
"github.com/devstream-io/devstream/internal/pkg/plugininstaller/ci/cifile"
"github.com/devstream-io/devstream/internal/pkg/statemanager"
)

func getState(options configmanager.RawOptions) (statemanager.ResourceStatus, error) {
// get ci sate
return cifile.GetCIFileStatus(options)
}
5 changes: 5 additions & 0 deletions internal/pkg/plugin/githubactions/general/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package general

func Update(options map[string]interface{}) (map[string]interface{}, error) {
return Create(options)
}
Loading