• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
24from __future__ import print_function
25
26import contextlib
27import os
28
29# Disable Python 2-related warnings since this file must be Python 2
30# compatible.
31# pylint: disable=super-with-arguments, useless-object-inheritance
32
33
34class GitHubVisitor(object):
35    """Serializes an Environment into files GitHub Actions understands."""
36
37    def __init__(self, *args, **kwargs):
38        super(GitHubVisitor, self).__init__(*args, **kwargs)
39        self._replacements = ()
40        self._github_env = None
41        self._github_path = None
42        self._log = None
43
44    def serialize(self, env, root):
45        """Write a shell file based on the given environment.
46
47        Args:
48            env (environment.Environment): Environment variables to use.
49            outs (file): Shell file to write.
50        """
51        try:
52            self._replacements = tuple(
53                (key, env.get(key) if value is None else value)
54                for key, value in env.replacements
55            )
56
57            with contextlib.ExitStack() as stack:
58                github_env = os.environ.get('GITHUB_ENV')
59                mode = 'a'
60                if not github_env:
61                    github_env = os.path.join(root, 'github_env.log')
62                    mode = 'w'
63                self._github_env = stack.enter_context(open(github_env, mode))
64
65                github_path = os.environ.get('GITHUB_PATH')
66                mode = 'a'
67                if not github_path:
68                    github_path = os.path.join(root, 'github_path.log')
69                    mode = 'w'
70                self._github_path = stack.enter_context(open(github_path, mode))
71
72                self._log = stack.enter_context(
73                    open(os.path.join(root, 'github.log'), 'w')
74                )
75
76                env.accept(self)
77
78        finally:
79            self._replacements = ()
80            self._github_env = None
81            self._github_path = None
82            self._log = None
83
84    def visit_set(self, set):  # pylint: disable=redefined-builtin
85        if '\n' in set.value:
86            eof = '__EOF__'
87            assert '\n{}\n'.format(eof) not in set.value
88            print(
89                '{name}=<<{eof}\n{value}\n{eof}'.format(
90                    name=set.name,
91                    value=set.value,
92                    eof=eof,
93                ),
94                file=self._github_env,
95            )
96        else:
97            print(
98                '{name}={value}'.format(name=set.name, value=set.value),
99                file=self._github_env,
100            )
101        print(
102            'setting {name!r} = {value!r}'.format(
103                name=set.name, value=set.value
104            ),
105            file=self._log,
106        )
107
108    def visit_clear(self, clear):
109        print('{}='.format(clear.name), file=self._github_env)
110        print('setting {!r} = ""'.format(clear.name), file=self._log)
111
112    def visit_prepend(self, prepend):
113        if prepend.name == 'PATH':
114            print(prepend.value, file=self._github_path)
115            print('adding {!r} to PATH'.format(prepend.value), file=self._log)
116        else:
117            print(
118                'unsupported prepend: {name!r} += {value!r}'.format(
119                    name=prepend.name, value=prepend.value
120                ),
121                file=self._log,
122            )
123
124    def visit_append(self, append):
125        print(
126            'unsupported append: {name!r} += {value!r}'.format(
127                name=append.name, value=append.value
128            ),
129            file=self._log,
130        )
131
132    def visit_remove(self, remove):
133        pass
134
135    def visit_echo(self, echo):
136        pass
137
138    def visit_comment(self, comment):
139        pass
140
141    def visit_command(self, command):
142        pass
143
144    def visit_doctor(self, doctor):
145        pass
146
147    def visit_blank_line(self, blank_line):
148        pass
149
150    def visit_function(self, function):
151        pass
152