Create file/api.symlink.
Add a new method for creating symlinks inside recipes.
BUG=chromium:783517
Change-Id: I43bf6263fe2a6f1a270216d610032803e138af01
Reviewed-on: https://chromium-review.googlesource.com/776077
Commit-Queue: Don Garrett <dgarrett@chromium.org>
Reviewed-by: Robbie Iannucci <iannucci@chromium.org>
Reviewed-by: Nodir Turakulov <nodir@chromium.org>
diff --git a/README.recipes.md b/README.recipes.md
index d292e448..d1dfee4 100644
--- a/README.recipes.md
+++ b/README.recipes.md
@@ -58,6 +58,7 @@
* [file:examples/glob](#recipes-file_examples_glob)
* [file:examples/listdir](#recipes-file_examples_listdir)
* [file:examples/raw_copy](#recipes-file_examples_raw_copy)
+ * [file:examples/symlink](#recipes-file_examples_symlink)
* [generator_script:examples/full](#recipes-generator_script_examples_full)
* [json:examples/full](#recipes-json_examples_full)
* [json:tests/add_json_log](#recipes-json_tests_add_json_log)
@@ -476,6 +477,19 @@
Raises file.Error.
+— **def [symlink](/recipe_modules/file/api.py#338)(self, name, source, link):**
+
+Creates a symlink from link to source on the local filesystem.
+
+Behaves identically to os.symlink.
+
+Args:
+ * name (str) - The name of the step.
+ * source (Path|Placeholder) - The path to link too.
+ * link (Path|Placeholder) - The link to create.
+
+Raises file.Error
+
— **def [write\_raw](/recipe_modules/file/api.py#131)(self, name, dest, data):**
Write the given `data` to `dest`.
@@ -1466,6 +1480,11 @@
[DEPS](/recipe_modules/file/examples/raw_copy.py#5): [file](#recipe_modules-file), [json](#recipe_modules-json), [path](#recipe_modules-path)
— **def [RunSteps](/recipe_modules/file/examples/raw_copy.py#12)(api):**
+### *recipes* / [file:examples/symlink](/recipe_modules/file/examples/symlink.py)
+
+[DEPS](/recipe_modules/file/examples/symlink.py#5): [file](#recipe_modules-file), [json](#recipe_modules-json), [path](#recipe_modules-path)
+
+— **def [RunSteps](/recipe_modules/file/examples/symlink.py#12)(api):**
### *recipes* / [generator\_script:examples/full](/recipe_modules/generator_script/examples/full.py)
[DEPS](/recipe_modules/generator_script/examples/full.py#7): [generator\_script](#recipe_modules-generator_script), [json](#recipe_modules-json), [path](#recipe_modules-path), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
diff --git a/recipe_modules/file/api.py b/recipe_modules/file/api.py
index f61796a..b2e11a5 100644
--- a/recipe_modules/file/api.py
+++ b/recipe_modules/file/api.py
@@ -334,3 +334,20 @@
assert p.startswith(src), (src, p)
return fnmatch.fnmatch(p[len(src)+1:].split(os.path.sep)[0], pattern)
self.m.path.mock_remove_paths(str(source), filt)
+
+ def symlink(self, name, source, link):
+ """Creates a symlink from link to source on the local filesystem.
+
+ Behaves identically to os.symlink.
+
+ Args:
+ * name (str) - The name of the step.
+ * source (Path|Placeholder) - The path to link to.
+ * link (Path|Placeholder) - The link to create.
+
+ Raises file.Error
+ """
+ self._assert_absolute_path_or_placeholder(source)
+ self._assert_absolute_path_or_placeholder(link)
+ self._run(name, ['symlink', source, link])
+ self.m.path.mock_copy_paths(source, link)
diff --git a/recipe_modules/file/examples/symlink.expected/basic.json b/recipe_modules/file/examples/symlink.expected/basic.json
new file mode 100644
index 0000000..b714074
--- /dev/null
+++ b/recipe_modules/file/examples/symlink.expected/basic.json
@@ -0,0 +1,49 @@
+[
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "Here is some text data",
+ "[START_DIR]/some file"
+ ],
+ "infra_step": true,
+ "name": "write a file"
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "symlink",
+ "[START_DIR]/some file",
+ "[START_DIR]/new path"
+ ],
+ "infra_step": true,
+ "name": "symlink it"
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "[START_DIR]/new path",
+ "/path/to/tmp/"
+ ],
+ "infra_step": true,
+ "name": "read it"
+ },
+ {
+ "name": "$result",
+ "recipe_result": null,
+ "status_code": 0
+ }
+]
\ No newline at end of file
diff --git a/recipe_modules/file/examples/symlink.py b/recipe_modules/file/examples/symlink.py
new file mode 100644
index 0000000..e311b9c
--- /dev/null
+++ b/recipe_modules/file/examples/symlink.py
@@ -0,0 +1,25 @@
+# Copyright 2017 The LUCI Authors. All rights reserved.
+# Use of this source code is governed under the Apache License, Version 2.0
+# that can be found in the LICENSE file.
+
+DEPS = [
+ 'file',
+ 'path',
+ 'json',
+]
+
+
+def RunSteps(api):
+ dest = api.path['start_dir'].join('some file')
+ data = 'Here is some text data'
+
+ api.file.write_text('write a file', dest, data)
+ api.file.symlink('symlink it', dest, api.path['start_dir'].join('new path'))
+ read_data = api.file.read_text(
+ 'read it', api.path['start_dir'].join('new path'), test_data=data)
+
+ assert read_data == data, (read_data, data)
+
+
+def GenTests(api):
+ yield api.test('basic')
diff --git a/recipe_modules/file/resources/fileutil.py b/recipe_modules/file/resources/fileutil.py
index 461ad96..d35cb52 100755
--- a/recipe_modules/file/resources/fileutil.py
+++ b/recipe_modules/file/resources/fileutil.py
@@ -258,6 +258,14 @@
func=lambda opts: print('\n'.join(str(os.stat(f).st_size)
for f in opts.file)))
+ # Subcommand: filesizes
+ subparser = subparsers.add_parser('symlink',
+ help='Creates a symlink. Behaves like os.symlink.')
+ subparser.add_argument('source', help='The thing to link to.')
+ subparser.add_argument('link', help='The link to create.')
+ subparser.set_defaults(
+ func=lambda opts: os.symlink(opts.source, opts.link))
+
# Parse arguments.
opts = parser.parse_args(args)