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