Skip to content

Commit ed37788

Browse files
authored
Merge pull request #13 from saadmk11/update-set-output
Update `save_state` and `set_output` Implementation [Previous Implementation is Deprecated]
2 parents ecb6e9f + 3bbb31f commit ed37788

File tree

3 files changed

+89
-76
lines changed

3 files changed

+89
-76
lines changed

README.md

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -210,36 +210,30 @@ GitHub Actions Docs: [error](https://docs.github.com/en/actions/using-workflows/
210210
# ::error title=test title,file=abc.py,col=1,endColumn=2,line=4,endLine=5::test message
211211
```
212212

213-
### **`set_output(name, value, use_subprocess=False)`**
213+
### **`set_output(name, value)`**
214214

215-
Sets an action's output parameter for the running workflow.
215+
Sets a step's output parameter by writing to `GITHUB_OUTPUT` environment file. Note that the step will need an `id` to be defined to later retrieve the output value.
216216
GitHub Actions Docs: [set_output](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter)
217217

218218
**example:**
219219

220220
```python
221221
>> from github_action_utils import set_output
222222
223-
>> set_output("test_name", "test_value",)
224-
225-
# Output:
226-
# ::set-output name=test_name::test_value
223+
>> set_output("my_output", "test value")
227224
```
228225

229-
### **`save_state(name, value, use_subprocess=False)`**
226+
### **`save_state(name, value)`**
230227

231-
Creates environment variable for sharing state with workflow's pre: or post: actions.
228+
Creates an environment variable by writing this to the `GITHUB_STATE` environment file which is available to workflow's pre: or post: actions.
232229
GitHub Actions Docs: [save_state](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#sending-values-to-the-pre-and-post-actions)
233230

234231
**example:**
235232

236233
```python
237234
>> from github_action_utils import save_state
238235
239-
>> save_state("test_name", "test_value",)
240-
241-
# Output:
242-
# ::save-state name=test_name::test_value
236+
>> save_state("my_state", "test value")
243237
```
244238

245239
### **`get_state(name)`**

github_action_utils.py

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from contextlib import contextmanager
77
from functools import lru_cache
88
from typing import Any, Dict, Generator, Union
9+
from warnings import warn
910

1011
if sys.version_info >= (3, 8):
1112
from typing import Literal
@@ -136,24 +137,32 @@ def _build_options_string(**kwargs: Any) -> str:
136137
)
137138

138139

139-
def set_output(name: str, value: Any, use_subprocess: bool = False) -> None:
140-
"""
141-
Sets an action's output parameter.
140+
def _build_file_input(name: str, value: Any) -> bytes:
141+
return (
142+
f"{_escape_property(name)}"
143+
f"<<{ACTION_ENV_DELIMITER}\n"
144+
f"{_escape_data(value)}\n"
145+
f"{ACTION_ENV_DELIMITER}\n".encode("utf-8")
146+
)
142147

143-
Template: ::set-output name={name}::{value}
144-
Example: echo "::set-output name=action_fruit::strawberry"
148+
149+
def set_output(name: str, value: Any, use_subprocess: Union[bool, None] = None) -> None:
150+
"""
151+
sets out for your workflow using GITHUB_OUTPUT file.
145152
146153
:param name: name of the output
147154
:param value: value of the output
148-
:param use_subprocess: use subprocess module to echo command
149155
:returns: None
150156
"""
151-
_print_command(
152-
"set-output",
153-
value,
154-
options_string=_build_options_string(name=name),
155-
use_subprocess=use_subprocess,
156-
)
157+
if use_subprocess is not None:
158+
warn(
159+
"Argument `use_subprocess` for `set_output()` is deprecated and "
160+
"going to be removed in the next version.",
161+
DeprecationWarning,
162+
)
163+
164+
with open(os.environ["GITHUB_OUTPUT"], "ab") as f:
165+
f.write(_build_file_input(name, value))
157166

158167

159168
def echo(message: Any, use_subprocess: bool = False) -> None:
@@ -317,24 +326,24 @@ def error(
317326
)
318327

319328

320-
def save_state(name: str, value: Any, use_subprocess: bool = False) -> None:
329+
def save_state(name: str, value: Any, use_subprocess: Union[bool, None] = None) -> None:
321330
"""
322-
creates environment variable for sharing with your workflow's pre: or post: actions.
323-
324-
Template: ::save-state name={name}::{value}
325-
Example: echo "::save-state name=processID::12345"
331+
sets state for your workflow using $GITHUB_STATE file
332+
for sharing it with your workflow's pre: or post: actions.
326333
327334
:param name: Name of the state environment variable (e.g: STATE_{name})
328335
:param value: value of the state environment variable
329-
:param use_subprocess: use subprocess module to echo command
330336
:returns: None
331337
"""
332-
_print_command(
333-
"save-state",
334-
value,
335-
options_string=_build_options_string(name=name),
336-
use_subprocess=use_subprocess,
337-
)
338+
if use_subprocess is not None:
339+
warn(
340+
"Argument `use_subprocess` for `save_state()` is deprecated and "
341+
"going to be removed in the next version.",
342+
DeprecationWarning,
343+
)
344+
345+
with open(os.environ["GITHUB_STATE"], "ab") as f:
346+
f.write(_build_file_input(name, value))
338347

339348

340349
def get_state(name: str) -> Union[str, None]:
@@ -484,12 +493,7 @@ def set_env(name: str, value: Any) -> None:
484493
:returns: None
485494
"""
486495
with open(os.environ["GITHUB_ENV"], "ab") as f:
487-
f.write(
488-
f"{_escape_property(name)}"
489-
f"<<{ACTION_ENV_DELIMITER}\n"
490-
f"{_escape_data(value)}\n"
491-
f"{ACTION_ENV_DELIMITER}\n".encode("utf-8")
492-
)
496+
f.write(_build_file_input(name, value))
493497

494498

495499
def get_workflow_environment_variables() -> Dict[str, Any]:

tests/test_github_action_utils.py

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ def test__build_options_string(input_kwargs: Any, expected: str) -> None:
117117
assert gha_utils._build_options_string(**input_kwargs) == expected
118118

119119

120+
def test__build_file_input() -> None:
121+
assert (
122+
gha_utils._build_file_input("test", "value")
123+
== b"test<<__ENV_DELIMITER__\nvalue\n__ENV_DELIMITER__\n"
124+
)
125+
126+
120127
@pytest.mark.parametrize(
121128
"test_input,expected",
122129
[
@@ -235,42 +242,50 @@ def test_error(
235242
assert out == expected
236243

237244

238-
@pytest.mark.parametrize(
239-
"input_args,expected",
240-
[
241-
(["abc", 123], "::set-output name=abc::123\n"),
242-
(["abc", "test"], "::set-output name=abc::test\n"),
243-
(["abc", {"k": "v"}], '::set-output name=abc::{"k": "v"}\n'),
244-
],
245-
)
246-
def test_set_output(
247-
capfd: Any,
248-
input_args: Any,
249-
expected: str,
250-
) -> None:
251-
gha_utils.set_output(*input_args)
252-
out, err = capfd.readouterr()
253-
print(out)
254-
assert out == expected
245+
def test_set_output(tmpdir: Any) -> None:
246+
file = tmpdir.join("output_file")
255247

248+
with mock.patch.dict(os.environ, {"GITHUB_OUTPUT": file.strpath}):
249+
gha_utils.set_output("test", "test")
250+
gha_utils.set_output("another", 2)
256251

257-
@pytest.mark.parametrize(
258-
"input_args,expected",
259-
[
260-
(["abc", 123], "::save-state name=abc::123\n"),
261-
(["abc", "test"], "::save-state name=abc::test\n"),
262-
(["abc", {"k": "v"}], '::save-state name=abc::{"k": "v"}\n'),
263-
],
264-
)
265-
def test_save_state(
266-
capfd: Any,
267-
input_args: Any,
268-
expected: str,
269-
) -> None:
270-
gha_utils.save_state(*input_args)
271-
out, err = capfd.readouterr()
272-
print(out)
273-
assert out == expected
252+
assert file.read() == (
253+
"test<<__ENV_DELIMITER__\n"
254+
"test\n__ENV_DELIMITER__\n"
255+
"another<<__ENV_DELIMITER__\n2\n"
256+
"__ENV_DELIMITER__\n"
257+
)
258+
259+
260+
def test_set_output_deprecation_warning(tmpdir: Any) -> None:
261+
file = tmpdir.join("output_file")
262+
263+
with pytest.deprecated_call():
264+
with mock.patch.dict(os.environ, {"GITHUB_OUTPUT": file.strpath}):
265+
gha_utils.set_output("test", "test", use_subprocess=True)
266+
267+
268+
def test_save_state(tmpdir: Any) -> None:
269+
file = tmpdir.join("state_file")
270+
271+
with mock.patch.dict(os.environ, {"GITHUB_STATE": file.strpath}):
272+
gha_utils.save_state("test", "test")
273+
gha_utils.save_state("another", 2)
274+
275+
assert file.read() == (
276+
"test<<__ENV_DELIMITER__\n"
277+
"test\n__ENV_DELIMITER__\n"
278+
"another<<__ENV_DELIMITER__\n2\n"
279+
"__ENV_DELIMITER__\n"
280+
)
281+
282+
283+
def test_save_state_deprecation_warning(tmpdir: Any) -> None:
284+
file = tmpdir.join("state_file")
285+
286+
with pytest.deprecated_call():
287+
with mock.patch.dict(os.environ, {"GITHUB_STATE": file.strpath}):
288+
gha_utils.save_state("test", "test", use_subprocess=True)
274289

275290

276291
@mock.patch.dict(os.environ, {"STATE_test_state": "test", "abc": "another test"})

0 commit comments

Comments
 (0)