Repository: oam-dev/kubevela (go)

Issue:
[Feature] Workaround to enable re-run single jobs 
Many Github users are expecting Github to enable the ability to re-run single jobs in workflow https://github.com/actions/runner/issues/432, so do we as it's cost another twenty minutes to rerun a failed e2e or unit-test job. But the feature hasn't come out.

Here is a workaround from @salem7 to fix the problem by splitting workflow https://hsalem.com/posts/ability-to-rerun-single-jobs-in-github-actions.html.

For example, we can split workflow `go` to several workflows, in each of which, contains e2e-tests, unit-tests respectively. We just need to rerun e2e-tests, without re-running unit-tests, which will save a lot of time.

![image](https://user-images.githubusercontent.com/2805315/121330734-dc8d2d00-c948-11eb-9ad8-b2c77664bdeb.png)

Do you think it's a good idea? If yes, I would be pleased to overtake the split work. Thanks.


Pull request:
Split workflow to enable re-run single jobs
Fix #1781

Patch:
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 75900c65..47bf7d01 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -33,53 +33,6 @@ jobs:
           do_not_skip: '["workflow_dispatch", "schedule", "push"]'
           concurrent_skipping: false
 
-  unit-tests:
-    runs-on: ubuntu-20.04
-    needs: detect-noop
-    if: needs.detect-noop.outputs.noop != 'true'
-
-    steps:
-      - name: Set up Go
-        uses: actions/setup-go@v1
-        with:
-          go-version: ${{ env.GO_VERSION }}
-        id: go
-
-      - name: Check out code into the Go module directory
-        uses: actions/checkout@v2
-        with:
-          submodules: true
-
-      - name: Cache Go Dependencies
-        uses: actions/cache@v2
-        with:
-          path: .work/pkg
-          key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
-          restore-keys: ${{ runner.os }}-pkg-
-
-      - name: Install ginkgo
-        run:  |
-          sudo apt-get install -y golang-ginkgo-dev
-
-      - name: Setup Kind Cluster
-        uses: engineerd/setup-kind@v0.5.0
-        with:
-          version: ${{ env.KIND_VERSION }}
-
-      - name: install Kubebuilder
-        uses: wonderflow/kubebuilder-action@v1.1
-
-      - name: Run Make test
-        run: make test
-
-      - name: Upload coverage report
-        uses: codecov/codecov-action@v1
-        with:
-          token: ${{ secrets.CODECOV_TOKEN }}
-          file: ./coverage.txt
-          flags: unittests
-          name: codecov-umbrella
-
   compatibility-test:
     runs-on: ubuntu-20.04
     needs: detect-noop
@@ -122,81 +75,6 @@ jobs:
       - name: Clean up testdata
         run: make compatibility-testdata-cleanup
 
-  e2e-tests:
-    runs-on: aliyun
-    needs: detect-noop
-    if: needs.detect-noop.outputs.noop != 'true'
-
-    steps:
-      - name: Check out code into the Go module directory
-        uses: actions/checkout@v2
-
-      - name: Setup Go
-        uses: actions/setup-go@v2
-        with:
-          go-version: ${{ env.GO_VERSION }}
-
-      - name: Get dependencies
-        run: |
-          go get -v -t -d ./...
-
-      - name: Setup Kind
-        uses: engineerd/setup-kind@v0.5.0
-        with:
-          version: ${{ env.KIND_VERSION }}
-          skipClusterCreation: true
-
-      - name: Setup Kind Cluster
-        run: |
-          kind delete cluster
-          kind create cluster --image kindest/node:v1.18.15@sha256:5c1b980c4d0e0e8e7eb9f36f7df525d079a96169c8a8f20d8bd108c0d0889cc4
-          kubectl version
-          kubectl cluster-info
-
-      - name: Load Image to kind cluster
-        run: make kind-load
-
-      - name: Run Make
-        run: make
-
-      - name: Run Make Manager
-        run: make manager
-
-      - name: Prepare for e2e tests
-        run: |
-          make e2e-cleanup
-          make e2e-setup
-          helm lint ./charts/vela-core
-          helm test -n vela-system kubevela --timeout 5m
-
-      - name: Wait for e2e preparation ready
-        run: |
-          timeout 60 bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:38081/api/components)" != "200" ]]; do sleep 5; done' || false
-
-      - name: Run api e2e tests
-        run: make e2e-api-test
-
-      - name: Run e2e tests
-        run: make e2e-test
-
-      - name: Stop kubevela, get profile
-        run: make end-e2e
-
-      - name: Upload coverage report
-        uses: codecov/codecov-action@v1
-        with:
-          token: ${{ secrets.CODECOV_TOKEN }}
-          file: /tmp/e2e-profile.out
-          flags: e2etests
-          name: codecov-umbrella
-
-      - name: Clean e2e profile
-        run: rm /tmp/e2e-profile.out
-
-      - name: Cleanup image
-        if: ${{ always() }}
-        run: make image-cleanup
-
   staticcheck:
     runs-on: ubuntu-20.04
     needs: detect-noop
@@ -222,7 +100,7 @@ jobs:
 
       - name: Install StaticCheck
         run: GO111MODULE=off go get honnef.co/go/tools/cmd/staticcheck
-      
+
       - name: Static Check
         run: staticcheck ./...
 
diff --git a/apis/types/capability.go b/apis/types/capability.go
index e0fd1c18..55093073 100644
--- a/apis/types/capability.go
+++ b/apis/types/capability.go
@@ -134,6 +134,7 @@ type Parameter struct {
 	Required bool        `json:"required,omitempty"`
 	Default  interface{} `json:"default,omitempty"`
 	Usage    string      `json:"usage,omitempty"`
+	Ignore   bool        `json:"ignore,omitempty"`
 	Type     cue.Kind    `json:"type,omitempty"`
 	Alias    string      `json:"alias,omitempty"`
 	JSONType string      `json:"jsonType,omitempty"`
diff --git a/charts/vela-core/templates/defwithtemplate/webservice.yaml b/charts/vela-core/templates/defwithtemplate/webservice.yaml
index dd1dd73e..a1d38e3e 100644
--- a/charts/vela-core/templates/defwithtemplate/webservice.yaml
+++ b/charts/vela-core/templates/defwithtemplate/webservice.yaml
@@ -136,7 +136,8 @@ spec:
         	// +short=p
         	port: *80 | int
         
-        	// If addRevisionLabel is true, the appRevision label will be added to the underlying pods
+        	// +ignore
+        	// +usage=If addRevisionLabel is true, the appRevision label will be added to the underlying pods
         	addRevisionLabel: *false | bool
         
         	// +usage=Commands to run in the container
diff --git a/pkg/cue/convert.go b/pkg/cue/convert.go
index 4236c087..5e8d5d82 100644
--- a/pkg/cue/convert.go
+++ b/pkg/cue/convert.go
@@ -79,7 +79,7 @@ func GetParameters(templateStr string) ([]types.Parameter, error) {
 		if param.Default == nil {
 			param.Default = getDefaultByKind(param.Type)
 		}
-		param.Short, param.Usage, param.Alias = RetrieveComments(val)
+		param.Short, param.Usage, param.Alias, param.Ignore = RetrieveComments(val)
 
 		params = append(params, param)
 	}
@@ -139,11 +139,14 @@ const (
 	ShortPrefix = "+short="
 	// AliasPrefix is an alias of the name of a parameter element, in order to making it more friendly to Cli users
 	AliasPrefix = "+alias="
+	// IgnorePrefix defines parameter in system level which we don't want our end user to see for KubeVela CLI
+	IgnorePrefix = "+ignore"
 )
 
-// RetrieveComments will retrieve Usage, Short and Alias from CUE Value
-func RetrieveComments(value cue.Value) (string, string, string) {
+// RetrieveComments will retrieve Usage, Short, Alias and Ignore from CUE Value
+func RetrieveComments(value cue.Value) (string, string, string, bool) {
 	var short, usage, alias string
+	var ignore bool
 	docs := value.Doc()
 	for _, doc := range docs {
 		lines := strings.Split(doc.Text(), "\n")
@@ -154,6 +157,9 @@ func RetrieveComments(value cue.Value) (string, string, string) {
 			if strings.HasPrefix(line, ShortPrefix) {
 				short = strings.TrimPrefix(line, ShortPrefix)
 			}
+			if strings.HasPrefix(line, IgnorePrefix) {
+				ignore = true
+			}
 			if strings.HasPrefix(line, UsagePrefix) {
 				usage = strings.TrimPrefix(line, UsagePrefix)
 			}
@@ -162,5 +168,5 @@ func RetrieveComments(value cue.Value) (string, string, string) {
 			}
 		}
 	}
-	return short, usage, alias
+	return short, usage, alias, ignore
 }
diff --git a/references/plugins/references.go b/references/plugins/references.go
index 9cd9e4f7..2442af61 100644
--- a/references/plugins/references.go
+++ b/references/plugins/references.go
@@ -469,8 +469,10 @@ func (ref *MarkdownReference) prepareParameter(tableName string, parameterList [
 	switch category {
 	case types.CUECategory:
 		for _, p := range parameterList {
-			printableDefaultValue := ref.getCUEPrintableDefaultValue(p.Default)
-			refContent += fmt.Sprintf(" %s | %s | %s | %t | %s \n", p.Name, p.Usage, p.PrintableType, p.Required, printableDefaultValue)
+			if !p.Ignore {
+				printableDefaultValue := ref.getCUEPrintableDefaultValue(p.Default)
+				refContent += fmt.Sprintf(" %s | %s | %s | %t | %s \n", p.Name, p.Usage, p.PrintableType, p.Required, printableDefaultValue)
+			}
 		}
 	case types.HelmCategory:
 		for _, p := range parameterList {
@@ -500,8 +502,10 @@ func (ref *ParseReference) prepareParameter(tableName string, parameterList []Re
 	switch category {
 	case types.CUECategory:
 		for _, p := range parameterList {
-			printableDefaultValue := ref.getCUEPrintableDefaultValue(p.Default)
-			table.Append([]string{p.Name, p.Usage, p.PrintableType, strconv.FormatBool(p.Required), printableDefaultValue})
+			if !p.Ignore {
+				printableDefaultValue := ref.getCUEPrintableDefaultValue(p.Default)
+				table.Append([]string{p.Name, p.Usage, p.PrintableType, strconv.FormatBool(p.Required), printableDefaultValue})
+			}
 		}
 	case types.HelmCategory:
 		for _, p := range parameterList {
@@ -558,7 +562,7 @@ func (ref *ParseReference) parseParameters(paraValue cue.Value, paramKey string,
 			if def, ok := val.Default(); ok && def.IsConcrete() {
 				param.Default = velacue.GetDefault(def)
 			}
-			param.Short, param.Usage, param.Alias = velacue.RetrieveComments(val)
+			param.Short, param.Usage, param.Alias, param.Ignore = velacue.RetrieveComments(val)
 			param.Type = val.IncompleteKind()
 			switch val.IncompleteKind() {
 			case cue.StructKind:
diff --git a/vela-templates/internal/cue/webservice.cue b/vela-templates/internal/cue/webservice.cue
index 5c09dc08..3e3231a0 100644
--- a/vela-templates/internal/cue/webservice.cue
+++ b/vela-templates/internal/cue/webservice.cue
@@ -120,7 +120,8 @@ parameter: {
 	// +short=p
 	port: *80 | int
 
-	// If addRevisionLabel is true, the appRevision label will be added to the underlying pods
+	// +ignore
+	// +usage=If addRevisionLabel is true, the appRevision label will be added to the underlying pods
 	addRevisionLabel: *false | bool
 
 	// +usage=Commands to run in the container


Test patch:
diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml
new file mode 100644
index 00000000..42eacbca
--- /dev/null
+++ b/.github/workflows/e2e-test.yml
@@ -0,0 +1,109 @@
+name: E2E Test
+
+on:
+  push:
+    branches:
+      - master
+      - release-*
+  workflow_dispatch: {}
+  pull_request:
+    branches:
+      - master
+      - release-*
+
+env:
+  # Common versions
+  GO_VERSION: '1.16'
+  GOLANGCI_VERSION: 'v1.38'
+  KIND_VERSION: 'v0.7.0'
+
+jobs:
+
+  detect-noop:
+    runs-on: ubuntu-20.04
+    outputs:
+      noop: ${{ steps.noop.outputs.should_skip }}
+    steps:
+      - name: Detect No-op Changes
+        id: noop
+        uses: fkirc/skip-duplicate-actions@v3.3.0
+        with:
+          github_token: ${{ secrets.GITHUB_TOKEN }}
+          paths_ignore: '["**.md", "**.mdx", "**.png", "**.jpg"]'
+          do_not_skip: '["workflow_dispatch", "schedule", "push"]'
+          concurrent_skipping: false
+
+  e2e-tests:
+    runs-on: aliyun
+    needs: detect-noop
+    if: needs.detect-noop.outputs.noop != 'true'
+
+    steps:
+      - name: Check out code into the Go module directory
+        uses: actions/checkout@v2
+
+      - name: Setup Go
+        uses: actions/setup-go@v2
+        with:
+          go-version: ${{ env.GO_VERSION }}
+
+      - name: Get dependencies
+        run: |
+          go get -v -t -d ./...
+
+      - name: Setup Kind
+        uses: engineerd/setup-kind@v0.5.0
+        with:
+          version: ${{ env.KIND_VERSION }}
+          skipClusterCreation: true
+
+      - name: Setup Kind Cluster
+        run: |
+          kind delete cluster
+          kind create cluster --image kindest/node:v1.18.15@sha256:5c1b980c4d0e0e8e7eb9f36f7df525d079a96169c8a8f20d8bd108c0d0889cc4
+          kubectl version
+          kubectl cluster-info
+
+      - name: Load Image to kind cluster
+        run: make kind-load
+
+      - name: Run Make
+        run: make
+
+      - name: Run Make Manager
+        run: make manager
+
+      - name: Prepare for e2e tests
+        run: |
+          make e2e-cleanup
+          make e2e-setup
+          helm lint ./charts/vela-core
+          helm test -n vela-system kubevela --timeout 5m
+
+      - name: Wait for e2e preparation ready
+        run: |
+          timeout 60 bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:38081/api/components)" != "200" ]]; do sleep 5; done' || false
+
+      - name: Run api e2e tests
+        run: make e2e-api-test
+
+      - name: Run e2e tests
+        run: make e2e-test
+
+      - name: Stop kubevela, get profile
+        run: make end-e2e
+
+      - name: Upload coverage report
+        uses: codecov/codecov-action@v1
+        with:
+          token: ${{ secrets.CODECOV_TOKEN }}
+          file: /tmp/e2e-profile.out
+          flags: e2etests
+          name: codecov-umbrella
+
+      - name: Clean e2e profile
+        run: rm /tmp/e2e-profile.out
+
+      - name: Cleanup image
+        if: ${{ always() }}
+        run: make image-cleanup
diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml
new file mode 100644
index 00000000..8d8592d8
--- /dev/null
+++ b/.github/workflows/unit-test.yml
@@ -0,0 +1,81 @@
+name: Unit-Test
+
+on:
+  push:
+    branches:
+      - master
+      - release-*
+  workflow_dispatch: {}
+  pull_request:
+    branches:
+      - master
+      - release-*
+
+env:
+  # Common versions
+  GO_VERSION: '1.16'
+  GOLANGCI_VERSION: 'v1.38'
+  KIND_VERSION: 'v0.7.0'
+
+jobs:
+
+  detect-noop:
+    runs-on: ubuntu-20.04
+    outputs:
+      noop: ${{ steps.noop.outputs.should_skip }}
+    steps:
+      - name: Detect No-op Changes
+        id: noop
+        uses: fkirc/skip-duplicate-actions@v3.3.0
+        with:
+          github_token: ${{ secrets.GITHUB_TOKEN }}
+          paths_ignore: '["**.md", "**.mdx", "**.png", "**.jpg"]'
+          do_not_skip: '["workflow_dispatch", "schedule", "push"]'
+          concurrent_skipping: false
+
+  unit-tests:
+    runs-on: ubuntu-20.04
+    needs: detect-noop
+    if: needs.detect-noop.outputs.noop != 'true'
+
+    steps:
+      - name: Set up Go
+        uses: actions/setup-go@v1
+        with:
+          go-version: ${{ env.GO_VERSION }}
+        id: go
+
+      - name: Check out code into the Go module directory
+        uses: actions/checkout@v2
+        with:
+          submodules: true
+
+      - name: Cache Go Dependencies
+        uses: actions/cache@v2
+        with:
+          path: .work/pkg
+          key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
+          restore-keys: ${{ runner.os }}-pkg-
+
+      - name: Install ginkgo
+        run:  |
+          sudo apt-get install -y golang-ginkgo-dev
+
+      - name: Setup Kind Cluster
+        uses: engineerd/setup-kind@v0.5.0
+        with:
+          version: ${{ env.KIND_VERSION }}
+
+      - name: install Kubebuilder
+        uses: wonderflow/kubebuilder-action@v1.1
+
+      - name: Run Make test
+        run: make test
+
+      - name: Upload coverage report
+        uses: codecov/codecov-action@v1
+        with:
+          token: ${{ secrets.CODECOV_TOKEN }}
+          file: ./coverage.txt
+          flags: unittests
+          name: codecov-umbrella
diff --git a/e2e/application/application_test.go b/e2e/application/application_test.go
index 279674a2..be2faa93 100644
--- a/e2e/application/application_test.go
+++ b/e2e/application/application_test.go
@@ -157,7 +157,7 @@ var ApplicationInitIntercativeCliContext = func(context string, appName string,
 						a: "mysvc",
 					},
 					{
-						q: "what would you configure for parameter 'addRevisionLabel' (optional, default is false):",
+						q: "If addRevisionLabel is true, the appRevision label will be added to the underlying pods (optional, default is false):",
 						a: "N",
 					},
 					{
diff --git a/e2e/plugin/plugin_suit_test.go b/e2e/plugin/plugin_suit_test.go
index 74ebe9db..cb054328 100644
--- a/e2e/plugin/plugin_suit_test.go
+++ b/e2e/plugin/plugin_suit_test.go
@@ -46,6 +46,7 @@ var testShowTdDef v1beta1.TraitDefinition
 var testCdDef v1beta1.ComponentDefinition
 var testCdDefWithHelm v1beta1.ComponentDefinition
 var testCdDefWithKube v1beta1.ComponentDefinition
+var testCdWithDeepCue v1beta1.ComponentDefinition
 var testTdDef v1beta1.TraitDefinition
 var testTdDefWithKube v1beta1.TraitDefinition
 
@@ -88,6 +89,10 @@ var _ = BeforeSuite(func(done Done) {
 	err = k8sClient.Create(ctx, &testCdDefWithKube)
 	Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
 
+	Expect(yaml.Unmarshal([]byte(componentWithDeepCue), &testCdWithDeepCue)).Should(BeNil())
+	err = k8sClient.Create(ctx, &testCdWithDeepCue)
+	Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
+
 	Expect(yaml.Unmarshal([]byte(traitDef), &testTdDef)).Should(BeNil())
 	err = k8sClient.Create(ctx, &testTdDef)
 	Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
@@ -122,6 +127,7 @@ var _ = AfterSuite(func() {
 	Expect(k8sClient.Delete(ctx, &testCdDef)).Should(BeNil())
 	Expect(k8sClient.Delete(ctx, &testCdDefWithHelm)).Should(BeNil())
 	Expect(k8sClient.Delete(ctx, &testCdDefWithKube)).Should(BeNil())
+	Expect(k8sClient.Delete(ctx, &testCdWithDeepCue)).Should(BeNil())
 	Expect(k8sClient.Delete(ctx, &testTdDef)).Should(BeNil())
 	Expect(k8sClient.Delete(ctx, &testTdDefWithKube)).Should(BeNil())
 	Expect(k8sClient.Delete(ctx, &testShowCdDef)).Should(BeNil())
diff --git a/e2e/plugin/plugin_test.go b/e2e/plugin/plugin_test.go
index ef680544..abe3c6d4 100644
--- a/e2e/plugin/plugin_test.go
+++ b/e2e/plugin/plugin_test.go
@@ -146,6 +146,19 @@ var _ = Describe("Test Kubectl Plugin", func() {
 			Expect(err).NotTo(HaveOccurred())
 			Expect(output).Should(ContainSubstring("map[string]string"))
 		})
+		It("Test show webservice def with cue ignore annotation ", func() {
+			tdName := "webservice"
+			output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s", tdName))
+			Expect(err).NotTo(HaveOccurred())
+			Expect(output).ShouldNot(ContainSubstring("addRevisionLabel"))
+		})
+		It("Test show webservice def with cue ignore annotation ", func() {
+			tdName := "mywebservice"
+			output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s", tdName))
+			Expect(err).NotTo(HaveOccurred())
+			Expect(output).ShouldNot(ContainSubstring("addRevisionLabel"))
+			Expect(output).ShouldNot(ContainSubstring("mySecretKey"))
+		})
 	})
 
 	Context("Test kubectl vela comp discover", func() {
@@ -603,6 +616,85 @@ spec:
           description: "target port num for service provider."
 `
 
+var componentWithDeepCue = `
+# Test for deeper parameter in cue Template
+apiVersion: core.oam.dev/v1beta1
+kind: ComponentDefinition
+metadata:
+ name: mywebservice
+ namespace: default
+ annotations:
+   definition.oam.dev/description: "Describes long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers."
+spec:
+ workload:
+   definition:
+     apiVersion: apps/v1
+     kind: Deployment
+ schematic:
+   cue:
+     template: |
+       output: {
+       	apiVersion: "apps/v1"
+       	kind:       "Deployment"
+       	spec: {
+       		selector: matchLabels: {
+       			"app.oam.dev/component": context.name
+       			if parameter.addRevisionLabel {
+       				"app.oam.dev/appRevision": context.appRevision
+       			}
+       		}
+
+       		template: {
+       			metadata: labels: {
+       				"app.oam.dev/component": context.name
+       				if parameter.addRevisionLabel {
+       					"app.oam.dev/appRevision": context.appRevision
+       				}
+       			}
+
+       			spec: {
+       				containers: [{
+       					name:  context.name
+       					image: parameter.image
+
+       					if parameter["env"] != _|_ {
+       						env: parameter.env
+       					}
+       				}]
+       		    }
+       	    }
+           }
+       }
+       parameter: {
+       	// +usage=Which image would you like to use for your service
+       	// +short=i
+       	image: string
+
+       	// +ignore
+       	// +usage=If addRevisionLabel is true, the appRevision label will be added to the underlying pods
+       	addRevisionLabel: *false | bool
+
+       	// +usage=Define arguments by using environment variables
+       	env?: [...{
+       		// +usage=Environment variable name
+       		name: string
+       		// +usage=The value of the environment variable
+       		value?: string
+       		// +usage=Specifies a source the value of this var should come from
+       		valueFrom?: {
+       			// +usage=Selects a key of a secret in the pod's namespace
+       			secretKeyRef: {
+       				// +usage=The name of the secret in the pod's namespace to select from
+       				name: string
+                       // +ignore
+       				// +usage=The key of the secret to select from. Must be a valid secret key
+       				mySecretKey: string
+       			}
+       		}
+       	}]
+       }
+`
+
 var dryRunResult = `---
 # Application(test-vela-app) -- Comopnent(express-server) 
 ---
diff --git a/pkg/cue/convert_test.go b/pkg/cue/convert_test.go
index 9a10b5d2..4634a00f 100644
--- a/pkg/cue/convert_test.go
+++ b/pkg/cue/convert_test.go
@@ -65,4 +65,17 @@ func TestGetParameter(t *testing.T) {
 	assert.NoError(t, err)
 	var exp []types.Parameter
 	assert.Equal(t, exp, params)
+
+	data, _ = ioutil.ReadFile("testdata/workloads/webservice.cue") // test cue parameter with "// +ignore" annotation
+	params, err = GetParameters(string(data))                      // Only test for func RetrieveComments
+	assert.NoError(t, err)
+	var flag bool
+	for _, para := range params {
+		if para.Name == "addRevisionLabel" {
+			flag = true
+			assert.Equal(t, para.Usage, "If addRevisionLabel is true, the appRevision label will be added to the underlying pods")
+			assert.Equal(t, para.Ignore, true)
+		}
+	}
+	assert.Equal(t, flag, true)
 }
diff --git a/pkg/cue/testdata/workloads/webservice.cue b/pkg/cue/testdata/workloads/webservice.cue
new file mode 100644
index 00000000..b0d9b0af
--- /dev/null
+++ b/pkg/cue/testdata/workloads/webservice.cue
@@ -0,0 +1,56 @@
+output: {
+	apiVersion: "apps/v1"
+	kind:       "Deployment"
+	spec: {
+		selector: matchLabels: {
+			"app.oam.dev/component": context.name
+			if parameter.addRevisionLabel {
+				"app.oam.dev/appRevision": context.appRevision
+			}
+		}
+		template: {
+			metadata: labels: {
+				"app.oam.dev/component": context.name
+				if parameter.addRevisionLabel {
+					"app.oam.dev/appRevision": context.appRevision
+				}
+			}
+			spec: {
+				containers: [{
+					name:  context.name
+					image: parameter.image
+					if parameter["env"] != _|_ {
+						env: parameter.env
+					}
+				}]
+		  }
+		}
+	}
+}
+parameter: {
+	// +usage=Which image would you like to use for your service
+	// +short=i
+	image: string
+	// +usage=Define arguments by using environment variables
+	env?: [...{
+		// +usage=Environment variable name
+		name: string
+		// +usage=The value of the environment variable
+		value?: string
+		// +usage=Specifies a source the value of this var should come from
+		valueFrom?: {
+			// +usage=Selects a key of a secret in the pod's namespace
+			secretKeyRef: {
+				// +usage=The name of the secret in the pod's namespace to select from
+				name: string
+				// +ignore
+				// +usage=The key of the secret to select from. Must be a valid secret key
+				secretKey: string
+			}
+		}
+	}]
+  // +ignore
+	// +usage=If addRevisionLabel is true, the appRevision label will be added to the underlying pods
+	addRevisionLabel: *false | bool
+}
+