diff --git a/docs/resources/codearts_build_task.md b/docs/resources/codearts_build_task.md
index 12aefb21265..8a86f800465 100644
--- a/docs/resources/codearts_build_task.md
+++ b/docs/resources/codearts_build_task.md
@@ -141,7 +141,7 @@ The `steps` block supports:
* `enable` - (Optional, Bool) Specifies whether to enable the step. Defaults to **false**.
-* `properties` - (Optional, Map) Specifies the build step properties.
+* `properties` - (Optional, Map) Specifies the build step properties. Value is JSON format string.
* `version` - (Optional, String) Specifies the build step version.
@@ -166,13 +166,13 @@ In addition to all arguments above, the following attributes are exported:
* `id` - The resource ID.
-* `steps` - Specifies the build execution steps.
+* `steps` - Indicates the build execution steps.
The [steps](#attrblock--steps) structure is documented below.
The `steps` block supports:
-* `properties_all` - Specifies the build step properties.
+* `properties_all` - Indicates the build step properties.
## Import
diff --git a/docs/resources/codearts_build_template.md b/docs/resources/codearts_build_template.md
new file mode 100644
index 00000000000..c7cadd3f74d
--- /dev/null
+++ b/docs/resources/codearts_build_template.md
@@ -0,0 +1,140 @@
+---
+subcategory: "CodeArts Build"
+layout: "huaweicloud"
+page_title: "HuaweiCloud: huaweicloud_codearts_build_template"
+description: |-
+ Manages a CodeArts Build template resource within HuaweiCloud.
+---
+
+# huaweicloud_codearts_build_template
+
+Manages a CodeArts Build template resource within HuaweiCloud.
+
+## Example Usage
+
+```hcl
+resource "huaweicloud_codearts_build_template" "test" {
+ name = "test-api"
+ description = "demo"
+
+ steps {
+ enable = true
+ module_id = "devcloud2018.codeci_action_20057.action"
+ name = "update OBS"
+ properties = {
+ objectKey = jsonencode("./")
+ backetName = jsonencode("test")
+ uploadDirectory = jsonencode(true)
+ artifactSourcePath = jsonencode("bin/*")
+ authorizationUser = jsonencode({
+ "displayName": "current user",
+ "value": "build"
+ })
+ obsHeaders = jsonencode([
+ {
+ "headerKey": "test",
+ "headerValue": "test"
+ }
+ ])
+ }
+ }
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `region` - (Optional, String, ForceNew) Specifies the region in which to create the resource.
+ If omitted, the provider-level region will be used.
+ Changing this creates a new resource.
+
+* `name` - (Required, String, NonUpdatable) Specifies the name of the build template.
+
+* `steps` - (Required, List, NonUpdatable) Specifies the build execution steps.
+ The [steps](#block--steps) structure is documented below.
+
+* `description` - (Optional, String, NonUpdatable) Specifies the template description.
+
+* `parameters` - (Optional, List, NonUpdatable) Specifies the build execution parameter list.
+ The [parameters](#block--parameters) structure is documented below.
+
+* `tool_type` - (Optional, String, NonUpdatable) Specifies the tool type.
+
+
+The `steps` block supports:
+
+* `module_id` - (Required, String, NonUpdatable) Specifies the build step module ID.
+
+* `name` - (Required, String, NonUpdatable) Specifies the build step name.
+
+* `enable` - (Optional, Bool, NonUpdatable) Specifies whether to enable the step. Defaults to **false**.
+
+* `properties` - (Optional, Map, NonUpdatable) Specifies the build step properties. Value is JSON format string.
+
+* `version` - (Optional, String, NonUpdatable) Specifies the build step version.
+
+
+The `parameters` block supports:
+
+* `name` - (Optional, String, NonUpdatable) Specifies the parameter definition name.
+
+* `params` - (Optional, List, NonUpdatable) Specifies the build execution sub-parameters.
+ The [params](#block--parameters--params) structure is documented below.
+
+
+The `params` block supports:
+
+* `name` - (Optional, String, NonUpdatable) Specifies the parameter field name.
+
+* `value` - (Optional, String, NonUpdatable) Specifies the parameter field value.
+
+* `limits` - (Optional, List, NonUpdatable) Specifies the enumeration parameter restrictions.
+ The [limits](#block--parameters--params--limits) structure is documented below.
+
+
+The `limits` block supports:
+
+* `disable` - (Optional, String, NonUpdatable) Specifies whether it is effective.
+
+* `display_name` - (Optional, String, NonUpdatable) Specifies the displayed name of the parameter.
+
+* `name` - (Optional, String, NonUpdatable) Specifies the parameter name.
+
+## Attribute Reference
+
+In addition to all arguments above, the following attributes are exported:
+
+* `id` - The resource ID.
+
+* `create_time` - Indicates the template creation time.
+
+* `favorite` - Indicates whether the template is favorite.
+
+* `nick_name` - Indicates the nick name.
+
+* `public` - Indicates whether the template is public.
+
+* `scope` - Indicates the scope.
+
+* `steps` - Indicates the build execution steps.
+ The [steps](#attrblock--steps) structure is documented below.
+
+* `template_id` - Indicates ID in database.
+
+* `type` - Indicates the template type.
+
+* `weight` - Indicates the weight of the template.
+
+
+The `steps` block supports:
+
+* `properties_all` - Indicates the build step properties.
+
+## Import
+
+The template can be imported using `id`, e.g.
+
+```bash
+$ terraform import huaweicloud_codearts_build_template.test
+```
diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go
index dfa76ecf0ab..ceebcca8258 100644
--- a/huaweicloud/provider.go
+++ b/huaweicloud/provider.go
@@ -2726,7 +2726,8 @@ func Provider() *schema.Provider {
"huaweicloud_codearts_pipeline_template": codeartspipeline.ResourceCodeArtsPipelineTemplate(),
"huaweicloud_codearts_pipeline_service_endpoint": codeartspipeline.ResourceCodeArtsPipelineServiceEndpoint(),
- "huaweicloud_codearts_build_task": codeartsbuild.ResourceCodeArtsBuildTask(),
+ "huaweicloud_codearts_build_task": codeartsbuild.ResourceCodeArtsBuildTask(),
+ "huaweicloud_codearts_build_template": codeartsbuild.ResourceCodeArtsBuildTemplate(),
"huaweicloud_dsc_instance": dsc.ResourceDscInstance(),
"huaweicloud_dsc_asset_obs": dsc.ResourceAssetObs(),
diff --git a/huaweicloud/services/acceptance/codeartsbuild/resource_huaweicloud_codearts_build_template_test.go b/huaweicloud/services/acceptance/codeartsbuild/resource_huaweicloud_codearts_build_template_test.go
new file mode 100644
index 00000000000..6a3e1e415f3
--- /dev/null
+++ b/huaweicloud/services/acceptance/codeartsbuild/resource_huaweicloud_codearts_build_template_test.go
@@ -0,0 +1,131 @@
+package codeartsbuild
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config"
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance"
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/codeartsbuild"
+)
+
+func getBuildTemplateResourceFunc(conf *config.Config, state *terraform.ResourceState) (interface{}, error) {
+ client, err := conf.NewServiceClient("codearts_build", acceptance.HW_REGION_NAME)
+ if err != nil {
+ return nil, fmt.Errorf("error creating CodeArts Build client: %s", err)
+ }
+
+ return codeartsbuild.GetBuildTemplate(client, state.Primary.ID)
+}
+
+func TestAccBuildTemplate_basic(t *testing.T) {
+ var obj interface{}
+
+ name := acceptance.RandomAccResourceName()
+ rName := "huaweicloud_codearts_build_template.test"
+
+ rc := acceptance.InitResourceCheck(
+ rName,
+ &obj,
+ getBuildTemplateResourceFunc,
+ )
+
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() {
+ acceptance.TestAccPreCheck(t)
+ },
+ ProviderFactories: acceptance.TestAccProviderFactories,
+ CheckDestroy: rc.CheckResourceDestroy(),
+ Steps: []resource.TestStep{
+ {
+ Config: testBuildTemplate_basic(name),
+ Check: resource.ComposeTestCheckFunc(
+ rc.CheckResourceExists(),
+ resource.TestCheckResourceAttr(rName, "name", name),
+ resource.TestCheckResourceAttr(rName, "description", "demo"),
+ resource.TestCheckResourceAttrSet(rName, "parameters.#"),
+ resource.TestCheckResourceAttrSet(rName, "steps.#"),
+ ),
+ },
+ {
+ ResourceName: rName,
+ ImportState: true,
+ ImportStateVerify: true,
+ ImportStateVerifyIgnore: []string{"steps.1.properties"},
+ },
+ },
+ })
+}
+
+func testBuildTemplate_basic(name string) string {
+ return fmt.Sprintf(`
+resource "huaweicloud_codearts_build_template" "test" {
+ name = "%[1]s"
+ description = "demo"
+
+ parameters {
+ name = "hudson.model.StringParameterDefinition"
+
+ params {
+ name = "name"
+ value = "test"
+ }
+ params {
+ name = "type"
+ value = "customizeparam"
+ }
+ params {
+ name = "defaultValue"
+ value = "cs"
+ }
+ params {
+ name = "staticVar"
+ value = "false"
+ }
+ params {
+ name = "sensitiveVar"
+ value = "false"
+ }
+ params {
+ name = "deletion"
+ value = "false"
+ }
+ params {
+ name = "defaults"
+ value = "false"
+ }
+ }
+
+ steps {
+ enable = true
+ module_id = "devcloud2018.codeci_action_20035.action"
+ name = "Docker Command"
+ }
+
+ steps {
+ enable = true
+ module_id = "devcloud2018.codeci_action_20057.action"
+ name = "update OBS"
+ properties = {
+ objectKey = jsonencode("./")
+ backetName = jsonencode("test")
+ uploadDirectory = jsonencode(true)
+ artifactSourcePath = jsonencode("bin/*")
+ authorizationUser = jsonencode({
+ "displayName": "current user",
+ "value": "build"
+ })
+ obsHeaders = jsonencode([
+ {
+ "headerKey": "1",
+ "headerValue": "1"
+ }
+ ])
+ }
+ }
+}
+`, name)
+}
diff --git a/huaweicloud/services/codeartsbuild/common.go b/huaweicloud/services/codeartsbuild/common.go
index 6f730557add..d8774ef0454 100644
--- a/huaweicloud/services/codeartsbuild/common.go
+++ b/huaweicloud/services/codeartsbuild/common.go
@@ -10,6 +10,7 @@ import (
const (
buildTaskNotFoundErr = "DEVCB.00031006"
+ templateNotFoundErr = "DEV.CB.0520002"
)
// checkResponseError use to check whether the CodeArts Build API response with OkCode but body contains error code.
diff --git a/huaweicloud/services/codeartsbuild/resource_huaweicloud_codearts_build_task.go b/huaweicloud/services/codeartsbuild/resource_huaweicloud_codearts_build_task.go
index 17822e77194..361f63ba080 100644
--- a/huaweicloud/services/codeartsbuild/resource_huaweicloud_codearts_build_task.go
+++ b/huaweicloud/services/codeartsbuild/resource_huaweicloud_codearts_build_task.go
@@ -160,7 +160,7 @@ func resourceSchemeTaskSteps() *schema.Resource {
Type: schema.TypeMap,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
- Description: `Specifies the build step properties.`,
+ Description: `Indicates the build step properties.`,
},
},
}
diff --git a/huaweicloud/services/codeartsbuild/resource_huaweicloud_codearts_build_template.go b/huaweicloud/services/codeartsbuild/resource_huaweicloud_codearts_build_template.go
new file mode 100644
index 00000000000..d0fce4906d2
--- /dev/null
+++ b/huaweicloud/services/codeartsbuild/resource_huaweicloud_codearts_build_template.go
@@ -0,0 +1,272 @@
+package codeartsbuild
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "github.com/hashicorp/go-multierror"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+
+ "github.com/chnsz/golangsdk"
+
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/common"
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config"
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils"
+)
+
+var templateNonUpdatableParams = []string{
+ "name", "description", "tool_type", "parameters",
+ "steps", "steps.*.module_id", "steps.*.name", "steps.*.properties", "steps.*.version", "steps.*.enable",
+}
+
+// @API CodeArtsBuild POST /v1/template/create
+// @API CodeArtsBuild GET /v1/template/custom
+// @API CodeArtsBuild DELETE /v1/template/{uuid}/delete
+func ResourceCodeArtsBuildTemplate() *schema.Resource {
+ return &schema.Resource{
+ CreateContext: resourceBuildTemplateCreate,
+ ReadContext: resourceBuildTemplateRead,
+ UpdateContext: resourceBuildTemplateUpdate,
+ DeleteContext: resourceBuildTemplateDelete,
+
+ Importer: &schema.ResourceImporter{
+ StateContext: schema.ImportStatePassthroughContext,
+ },
+
+ CustomizeDiff: config.FlexibleForceNew(templateNonUpdatableParams),
+
+ Schema: map[string]*schema.Schema{
+ "region": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ },
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ Description: `Specifies the name of the build template.`,
+ },
+ "steps": {
+ Type: schema.TypeList,
+ Required: true,
+ Description: `Specifies the build execution steps.`,
+ Elem: resourceSchemeTaskSteps(),
+ },
+ "description": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: `Specifies the template description.`,
+ },
+ "tool_type": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: `Specifies the tool type.`,
+ },
+ "parameters": {
+ Type: schema.TypeSet,
+ Optional: true,
+ Description: `Specifies the build execution parameter list.`,
+ Elem: resourceSchemeTaskParameters(),
+ },
+ "enable_force_new": {
+ Type: schema.TypeString,
+ Optional: true,
+ ValidateFunc: validation.StringInSlice([]string{"true", "false"}, false),
+ Description: utils.SchemaDesc("", utils.SchemaDescInput{Internal: true}),
+ },
+ "favorite": {
+ Type: schema.TypeBool,
+ Computed: true,
+ Description: `Indicates whether the template is favorite.`,
+ },
+ "nick_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `Indicates the nick name.`,
+ },
+ "template_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `Indicates ID in database.`,
+ },
+ "type": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `Indicates the template type.`,
+ },
+ "public": {
+ Type: schema.TypeBool,
+ Computed: true,
+ Description: `Indicates whether the template is public.`,
+ },
+ "create_time": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `Indicates the template creation time.`,
+ },
+ "weight": {
+ Type: schema.TypeInt,
+ Computed: true,
+ Description: `Indicates the weight of the template.`,
+ },
+ "scope": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `Indicates the scope.`,
+ },
+ },
+ }
+}
+
+func resourceBuildTemplateCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ cfg := meta.(*config.Config)
+ client, err := cfg.NewServiceClient("codearts_build", cfg.GetRegion(d))
+ if err != nil {
+ return diag.Errorf("error creating CodeArts Build client: %s", err)
+ }
+
+ httpUrl := "v1/template/create"
+ createPath := client.Endpoint + httpUrl
+ createOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ JSONBody: utils.RemoveNil(buildCreateOrUpdateBuildTemplateBodyParams(d)),
+ }
+
+ createResp, err := client.Request("POST", createPath, &createOpt)
+ if err != nil {
+ return diag.Errorf("error creating CodeArts Build template: %s", err)
+ }
+ createRespBody, err := utils.FlattenResponse(createResp)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ if err := checkResponseError(createRespBody); err != nil {
+ return diag.Errorf("error creating CodeArts Build template: %s", err)
+ }
+
+ id := utils.PathSearch("result.uuid", createRespBody, "").(string)
+ if id == "" {
+ return diag.Errorf("unable to find the CodeArts Build template ID from the API response")
+ }
+
+ d.SetId(id)
+
+ return resourceBuildTemplateRead(ctx, d, meta)
+}
+
+func buildCreateOrUpdateBuildTemplateBodyParams(d *schema.ResourceData) map[string]interface{} {
+ bodyParams := map[string]interface{}{
+ "name": d.Get("name"),
+ "description": d.Get("description"),
+ "tool_type": utils.ValueIgnoreEmpty(d.Get("tool_type")),
+ "parameters": buildBuildTaskParameters(d),
+ "template": map[string]interface{}{
+ "steps": buildBuildTaskSteps(d),
+ },
+ }
+
+ return bodyParams
+}
+
+func GetBuildTemplate(client *golangsdk.ServiceClient, id string) (interface{}, error) {
+ httpUrl := "v1/template/custom"
+ listPath := client.Endpoint + httpUrl
+ listOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ }
+
+ pageSize := 100
+ page := 1
+ for {
+ currentPath := listPath + fmt.Sprintf("?page_size=%d&page=%d", pageSize, page)
+ listResp, err := client.Request("GET", currentPath, &listOpt)
+ if err != nil {
+ return nil, err
+ }
+ listRespBody, err := utils.FlattenResponse(listResp)
+ if err != nil {
+ return nil, err
+ }
+
+ templates := utils.PathSearch("result.items", listRespBody, make([]interface{}, 0)).([]interface{})
+ if len(templates) == 0 {
+ return nil, golangsdk.ErrDefault404{}
+ }
+
+ searchPath := fmt.Sprintf("result.items[?uuid=='%s']|[0]", id)
+ result := utils.PathSearch(searchPath, listRespBody, nil)
+ if result != nil {
+ return result, nil
+ }
+
+ page++
+ }
+}
+
+func resourceBuildTemplateRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ cfg := meta.(*config.Config)
+ region := cfg.GetRegion(d)
+ client, err := cfg.NewServiceClient("codearts_build", region)
+ if err != nil {
+ return diag.Errorf("error creating CodeArts Build client: %s", err)
+ }
+
+ template, err := GetBuildTemplate(client, d.Id())
+ if err != nil {
+ return common.CheckDeletedDiag(d, err, "error retrieving CodeArts Build template")
+ }
+
+ mErr := multierror.Append(nil,
+ d.Set("region", region),
+ d.Set("name", utils.PathSearch("name", template, nil)),
+ d.Set("description", utils.PathSearch("description", template, nil)),
+ d.Set("tool_type", utils.PathSearch("tool_type", template, nil)),
+ d.Set("parameters", flattenBuildTaskParameters(template)),
+ d.Set("steps", flattenBuildTaskSteps(d, utils.PathSearch("template", template, nil))),
+ d.Set("nick_name", utils.PathSearch("nick_name", template, nil)),
+ d.Set("template_id", utils.PathSearch("id", template, nil)),
+ d.Set("type", utils.PathSearch("type", template, nil)),
+ d.Set("public", utils.PathSearch("public", template, nil)),
+ d.Set("create_time", utils.PathSearch("create_time", template, nil)),
+ d.Set("weight", utils.PathSearch("weight", template, nil)),
+ d.Set("scope", utils.PathSearch("scope", template, nil)),
+
+ //nolint:misspell
+ d.Set("favorite", utils.PathSearch("favourite", template, nil)),
+ )
+
+ return diag.FromErr(mErr.ErrorOrNil())
+}
+
+func resourceBuildTemplateUpdate(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
+ return nil
+}
+
+func resourceBuildTemplateDelete(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ cfg := meta.(*config.Config)
+ region := cfg.GetRegion(d)
+ client, err := cfg.NewServiceClient("codearts_build", region)
+ if err != nil {
+ return diag.Errorf("error creating CodeArts Build client: %s", err)
+ }
+
+ httpUrl := "v1/template/{uuid}/delete"
+ deletePath := client.Endpoint + httpUrl
+ deletePath = strings.ReplaceAll(deletePath, "{uuid}", d.Id())
+ deleteOpt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ }
+
+ _, err = client.Request("DELETE", deletePath, &deleteOpt)
+ if err != nil {
+ return common.CheckDeletedDiag(d, common.ConvertUndefinedErrInto404Err(err, 422, "error_code", templateNotFoundErr),
+ "error deleting CodeArts Build template")
+ }
+
+ return nil
+}