Repository: cekit/cekit

Issue:
Invalid 'execute' key not caught by 'image.yaml' validation
**Describe the bug**
Adding invalid `execute` key in image descriptor isn't caught by 'image.yaml' validation.

**To reproduce**
Execute a script directly from `image.yaml`.

**Expected behavior**
Expected CEKit to fail fast due to invalid YAML and inform the reason.

**Logs**
```
STEP 1: FROM registry.redhat.io/ubi8/go-toolset AS builder
STEP 2: USER root
STEP 3: COPY             unpacked_remote_sources             /workspace/
STEP 4: USER 
Error: error building at STEP "USER ": USER requires exactly one argument
2020-12-02 08:04:41,859 cekit        ERROR    Image build failed, see logs above.
```

**Environment information:**
 - Fedora 33
 - CEKit version: 3.7.0

**Additional context**
The invalid `execute` key was added to the first stage of a multi-stage build.


Discussion:

Patch:
diff --git a/cekit/descriptor/base.py b/cekit/descriptor/base.py
index 8c07fda..52a1b77 100644
--- a/cekit/descriptor/base.py
+++ b/cekit/descriptor/base.py
@@ -77,9 +77,9 @@ class Descriptor(collectionsAbc.MutableMapping):
 
     def merge(self, descriptor):
         """ Merges two descriptors in a way, that arrays are appended
-        and duplicit values are kept
+        and duplicate values are kept
         Args:
-          descriptor - a cekit descritor
+          descriptor - a cekit descriptor
         """
         try:
             _merge_descriptors(self, descriptor)
diff --git a/cekit/descriptor/image.py b/cekit/descriptor/image.py
index 4a9d93b..e576f63 100644
--- a/cekit/descriptor/image.py
+++ b/cekit/descriptor/image.py
@@ -19,7 +19,6 @@ map:
   description: {type: text}
   labels: {type: any}
   envs:  {type: any}
-  execute: {type: any}
   ports: {type: any}
   run: {type: any}
   artifacts: {type: any}
diff --git a/cekit/descriptor/module.py b/cekit/descriptor/module.py
index befa799..b8fbce4 100644
--- a/cekit/descriptor/module.py
+++ b/cekit/descriptor/module.py
@@ -1,6 +1,8 @@
 from cekit.descriptor import Image, Execute
 from cekit.descriptor.image import get_image_schema
 
+overrides_schema = get_image_schema()
+overrides_schema['map']['execute'] = {'type': 'any'}
 
 class Module(Image):
     """Represents a module.
@@ -12,7 +14,7 @@ class Module(Image):
     def __init__(self, descriptor, path, artifact_dir):
         self._artifact_dir = artifact_dir
         self.path = path
-        self.schema = get_image_schema().copy()
+        self.schema = overrides_schema.copy()
         # calling Descriptor constructor only here (we don't want Image() to mess with schema)
         super(Image, self).__init__(descriptor)
         self.skip_merging = ['description',
diff --git a/docs/handbook/ci.rst b/docs/handbook/ci.rst
new file mode 100644
index 0000000..05f6cd0
--- /dev/null
+++ b/docs/handbook/ci.rst
@@ -0,0 +1,37 @@
+CI Integration
+==============
+
+It is also possible to integrate CEKit into your continuous integration via a GitHub Action. The action is available  `here <https://github.com/manusa/actions-setup-cekit>`_.
+
+An example of its usage (from `jkube-images <https://github.com/jkubeio/jkube-images/blob/main/.github/workflows/build-images.yml>`_ is:
+
+.. code-block:: yaml
+
+    name: Build Images
+
+    on:
+      pull_request:
+      push:
+        branches:
+          - main
+
+    env:
+      TAG: latest
+
+    jobs:
+      build-images:
+        name: Build Images
+        runs-on: ubuntu-latest
+        strategy:
+          fail-fast: false
+          matrix:
+            image: ['jkube-java-binary-s2i', 'jkube-jetty9-binary-s2i', 'jkube-karaf-binary-s2i', 'jkube-tomcat9-binary-s2i']
+        steps:
+          - name: Checkout
+            uses: actions/checkout@v2
+          - name: Install CEKit
+            uses: manusa/actions-setup-cekit@v1.1.1
+          - name: Build java-binary-s2i
+            run: |
+              echo "Building quay.io/jkube/${{ matrix.image }}:${TAG}"
+              cekit --descriptor ${{ matrix.image }}.yaml build docker --tag="quay.io/jkube/${{ matrix.image }}:${TAG}"
diff --git a/docs/handbook/index.rst b/docs/handbook/index.rst
index db8801c..70caa0e 100644
--- a/docs/handbook/index.rst
+++ b/docs/handbook/index.rst
@@ -16,3 +16,4 @@ This chapter will guide you through the CEKit usage covering all important topic
     overrides
     configuration
     redhat
+    ci


Test patch:
diff --git a/tests/test_dockerfile.py b/tests/test_dockerfile.py
index eaa6f56..c4996fc 100644
--- a/tests/test_dockerfile.py
+++ b/tests/test_dockerfile.py
@@ -1,6 +1,5 @@
 import os
 import re
-import subprocess
 
 import pytest
 import yaml
@@ -10,7 +9,6 @@ from cekit.config import Config
 from cekit.descriptor import Repository
 from cekit.version import version as cekit_version
 from cekit.tools import Chdir
-from cekit.template_helper import TemplateHelper
 
 from click.testing import CliRunner
 
@@ -55,13 +53,6 @@ def print_test_name(value):
                              'example': 'example_value',
                              'description': 'This is a description'}]},
      r' \\\s+COMBINED_ENV=\"set_value\" \\\s+JBOSS_IMAGE_NAME=\"testimage\" \\\s+JBOSS_IMAGE_VERSION=\"1\" \n'),
-    ('test_execute',
-     {'execute': [{'script': 'foo_script'}]},
-     r'.*RUN [ "bash", "-x", "/tmp/scripts/testimage/foo_script" ].*'),
-    ('test_execute_user',
-     {'execute': [{'script': 'bar_script',
-                   'user': 'bar_user'}]},
-     r'.*USER bar_user\n\s+RUN [ "bash", "-x", "/tmp/scripts/testimage/foo_script" ].*'),
     ('test_cekit_label_version',
      {},
      r'.*io.cekit.version="%s".*' % cekit_version)],
diff --git a/tests/test_unit_descriptor.py b/tests/test_unit_descriptor.py
index 4ccfced..f045efa 100644
--- a/tests/test_unit_descriptor.py
+++ b/tests/test_unit_descriptor.py
@@ -238,3 +238,16 @@ def test_image_modules_git_repo(caplog):
     assert image['name'] == 'test/foo'
     assert "No value found for 'name' in '{\"git\": {\"ref\": \"release-3.1.0\", \"url\": \"https://github.com/company/foobar-project-modules\"}}' artifact; using auto-generated value of 'foobar-project-modules'" \
            in caplog.text
+
+
+def test_image_descriptor_with_execute(caplog):
+    with pytest.raises(CekitError) as excinfo:
+        image = Image(yaml.safe_load("""
+        from: foo
+        name: test/foo
+        version: 1.9
+        execute:
+              - script: build.sh
+        """), 'foo')
+
+    assert "Cannot validate schema: Image" in excinfo.value.message
diff --git a/tests/test_validate.py b/tests/test_validate.py
index aa5f731..cc5be12 100644
--- a/tests/test_validate.py
+++ b/tests/test_validate.py
@@ -1093,7 +1093,6 @@ def test_package_related_commands_packages_in_module(tmpdir, mocker):
     copy_repos(image_dir)
 
     img_desc = image_descriptor.copy()
-    img_desc['execute'] = [{'script': 'configure.sh', 'user': 'someuser'}]
     img_desc['modules']['install'] = [{'name': 'packages_module'}, {'name': 'packages_module_1'}]
     img_desc['modules']['repositories'] = [{'name': 'modules',
                                             'path': 'tests/modules/repo_packages'}]