xref: /aosp_15_r20/external/pigweed/pw_env_setup/py/pw_env_setup/github_visitor.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1# Copyright 2024 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14# pylint: disable=line-too-long
15"""Serializes an Environment into files in a way GitHub Actions understands.
16
17See also:
18
19* https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable
20* https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path
21"""
22# pylint: enable=line-too-long
23
24import contextlib
25import os
26
27
28class GitHubVisitor:
29    """Serializes an Environment into files GitHub Actions understands."""
30
31    def __init__(self, *args, **kwargs):
32        super().__init__(*args, **kwargs)
33        self._replacements = ()
34        self._github_env = None
35        self._github_path = None
36        self._log = None
37
38    def serialize(self, env, root):
39        """Write a shell file based on the given environment.
40
41        Args:
42            env (environment.Environment): Environment variables to use.
43            outs (file): Shell file to write.
44        """
45        try:
46            self._replacements = tuple(
47                (key, env.get(key) if value is None else value)
48                for key, value in env.replacements
49            )
50
51            with contextlib.ExitStack() as stack:
52                github_env = os.environ.get('GITHUB_ENV')
53                mode = 'a'
54                if not github_env:
55                    github_env = os.path.join(root, 'github_env.log')
56                    mode = 'w'
57                self._github_env = stack.enter_context(open(github_env, mode))
58
59                github_path = os.environ.get('GITHUB_PATH')
60                mode = 'a'
61                if not github_path:
62                    github_path = os.path.join(root, 'github_path.log')
63                    mode = 'w'
64                self._github_path = stack.enter_context(open(github_path, mode))
65
66                self._log = stack.enter_context(
67                    open(os.path.join(root, 'github.log'), 'w')
68                )
69
70                env.accept(self)
71
72        finally:
73            self._replacements = ()
74            self._github_env = None
75            self._github_path = None
76            self._log = None
77
78    def visit_set(self, set):  # pylint: disable=redefined-builtin
79        if '\n' in set.value:
80            eof = '__EOF__'
81            assert '\n{}\n'.format(eof) not in set.value
82            print(
83                '{name}=<<{eof}\n{value}\n{eof}'.format(
84                    name=set.name,
85                    value=set.value,
86                    eof=eof,
87                ),
88                file=self._github_env,
89            )
90        else:
91            print(
92                '{name}={value}'.format(name=set.name, value=set.value),
93                file=self._github_env,
94            )
95        print(
96            'setting {name!r} = {value!r}'.format(
97                name=set.name, value=set.value
98            ),
99            file=self._log,
100        )
101
102    def visit_clear(self, clear):
103        print('{}='.format(clear.name), file=self._github_env)
104        print('setting {!r} = ""'.format(clear.name), file=self._log)
105
106    def visit_prepend(self, prepend):
107        if prepend.name == 'PATH':
108            print(prepend.value, file=self._github_path)
109            print('adding {!r} to PATH'.format(prepend.value), file=self._log)
110        else:
111            print(
112                'unsupported prepend: {name!r} += {value!r}'.format(
113                    name=prepend.name, value=prepend.value
114                ),
115                file=self._log,
116            )
117
118    def visit_append(self, append):
119        print(
120            'unsupported append: {name!r} += {value!r}'.format(
121                name=append.name, value=append.value
122            ),
123            file=self._log,
124        )
125
126    def visit_remove(self, remove):
127        pass
128
129    def visit_echo(self, echo):
130        pass
131
132    def visit_comment(self, comment):
133        pass
134
135    def visit_command(self, command):
136        pass
137
138    def visit_doctor(self, doctor):
139        pass
140
141    def visit_blank_line(self, blank_line):
142        pass
143
144    def visit_function(self, function):
145        pass
146