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