diff --git a/demos/base-example-consumer/snap/snapcraft.yaml b/demos/base-example-consumer/snap/snapcraft.yaml
new file mode 100644
index 0000000000..3ca425142d
--- /dev/null
+++ b/demos/base-example-consumer/snap/snapcraft.yaml
@@ -0,0 +1,18 @@
+name: base-example-consumer
+version: 1.0
+summary: Example snap that consumes a different base snap
+description:
+ This example snap uses the base-example base snap instead of
+ the regular "core" snap.
+confinement: strict
+base: base-example
+
+apps:
+ hello:
+ command: usr/bin/hello
+
+parts:
+ libc:
+ plugin: dump
+ source: .
+ stage-packages: [hello]
diff --git a/demos/base-example/snap/snapcraft.yaml b/demos/base-example/snap/snapcraft.yaml
new file mode 100644
index 0000000000..42eea5cfe4
--- /dev/null
+++ b/demos/base-example/snap/snapcraft.yaml
@@ -0,0 +1,17 @@
+name: base-example
+version: 1.0
+summary: Example base snap
+description:
+ A base snap is a special kind of snap that can be used by
+ other snaps as an alternative base. This base will then
+ be used as the root filesystem instead of the traditional
+ core snap.
+
+ This example base snap only contains a basic libc6.
+type: base
+
+parts:
+ libc:
+ plugin: dump
+ source: .
+ stage-packages: [libc6]
diff --git a/schema/snapcraft.yaml b/schema/snapcraft.yaml
index dc8af58049..6f1567a02d 100644
--- a/schema/snapcraft.yaml
+++ b/schema/snapcraft.yaml
@@ -80,6 +80,7 @@ properties:
description: the snap type, the implicit type is 'app'
enum:
- app
+ - base
- gadget
- kernel
- os
@@ -104,6 +105,9 @@ properties:
enum:
- stable
- devel
+ base:
+ type: string
+ description: the base snap to use
epoch:
description: the snap epoch, used to specify upgrade paths
format: epoch
diff --git a/snapcraft/internal/meta.py b/snapcraft/internal/meta.py
index 592a166037..c0b2313282 100644
--- a/snapcraft/internal/meta.py
+++ b/snapcraft/internal/meta.py
@@ -53,6 +53,7 @@
_OPTIONAL_PACKAGE_KEYS = [
'architectures',
'assumes',
+ 'base',
'environment',
'type',
'plugs',
diff --git a/snapcraft/tests/commands/test_snap.py b/snapcraft/tests/commands/test_snap.py
index 5962520585..0110b32420 100644
--- a/snapcraft/tests/commands/test_snap.py
+++ b/snapcraft/tests/commands/test_snap.py
@@ -100,7 +100,8 @@ def test_snap_fails_with_bad_type(self):
self.run_command, ['snap'])
self.assertThat(str(raised), Contains(
- "bad-type' is not one of ['app', 'gadget', 'kernel', 'os']"))
+ "bad-type' is not one of ['app', 'base', 'gadget', "
+ "'kernel', 'os']"))
def test_snap_is_the_default(self):
self.make_snapcraft_yaml()
diff --git a/snapcraft/tests/test_project_loader.py b/snapcraft/tests/test_project_loader.py
index 248bb3cb0d..d5e4fa40f2 100644
--- a/snapcraft/tests/test_project_loader.py
+++ b/snapcraft/tests/test_project_loader.py
@@ -2063,7 +2063,7 @@ def test_invalid_types(self):
expected_message = (
"The 'type' property does not match the required "
"schema: '{}' is not one of "
- "['app', 'gadget', 'kernel', 'os']").format(self.type_)
+ "['app', 'base', 'gadget', 'kernel', 'os']").format(self.type_)
self.assertEqual(raised.message, expected_message,
message=data)
diff --git a/snaps_tests/demos_tests/test_base-example.py b/snaps_tests/demos_tests/test_base-example.py
new file mode 100644
index 0000000000..e24b749da0
--- /dev/null
+++ b/snaps_tests/demos_tests/test_base-example.py
@@ -0,0 +1,29 @@
+# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
+#
+# Copyright (C) 2017 Canonical Ltd
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import snaps_tests
+
+
+class BaseExampleTestCase(snaps_tests.SnapsTestCase):
+
+ snap_content_dir = 'base-example'
+
+ def test_base_example(self):
+ # Build snap will raise an exception in case of error.
+ snap_path = self.build_snap(self.snap_content_dir)
+ # Install snap will raise an exception in case of error.
+ self.install_snap(snap_path, 'base-example', '1.0')
+ # more testing is done in test_base-consumer
diff --git a/snaps_tests/demos_tests/test_base-example_consumer.py b/snaps_tests/demos_tests/test_base-example_consumer.py
new file mode 100644
index 0000000000..272b819de6
--- /dev/null
+++ b/snaps_tests/demos_tests/test_base-example_consumer.py
@@ -0,0 +1,33 @@
+# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
+#
+# Copyright (C) 2017 Canonical Ltd
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import snaps_tests
+
+
+class BaseExampleConsumerTestCase(snaps_tests.SnapsTestCase):
+
+ base_content_dir = 'base-example'
+ base_consumer_content_dir = 'base-example-consumer'
+
+ def test_base_example(self):
+ # build/install the alternative base
+ snap_path = self.build_snap(self.base_content_dir)
+ self.install_snap(snap_path, 'base-example', '1.0')
+ # build the consumer
+ snap_path = self.build_snap(self.base_consumer_content_dir)
+ self.install_snap(snap_path, 'base-example-consumer', '1.0')
+ # FIXME: once snapd supports bases fully run hello against
+ # the alternative base