 105d1f9cfc
			
		
	
	
		105d1f9cfc
		
			
		
	
	
	
	
		
			
			An annotation is pretty useless if you don't specify a file to place the annotation on, so let's just require it.
		
			
				
	
	
		
			90 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			90 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # typed: true
 | |
| # frozen_string_literal: true
 | |
| 
 | |
| require "utils/tty"
 | |
| 
 | |
| module GitHub
 | |
|   # Helper functions for interacting with GitHub Actions.
 | |
|   #
 | |
|   # @api private
 | |
|   module Actions
 | |
|     extend T::Sig
 | |
| 
 | |
|     sig { params(string: String).returns(String) }
 | |
|     def self.escape(string)
 | |
|       # See https://github.community/t/set-output-truncates-multiline-strings/16852/3.
 | |
|       string.gsub("%", "%25")
 | |
|             .gsub("\n", "%0A")
 | |
|             .gsub("\r", "%0D")
 | |
|     end
 | |
| 
 | |
|     # Helper class for formatting annotations on GitHub Actions.
 | |
|     class Annotation
 | |
|       extend T::Sig
 | |
| 
 | |
|       ANNOTATION_TYPES = [:notice, :warning, :error].freeze
 | |
| 
 | |
|       sig { params(path: T.any(String, Pathname)).returns(T.nilable(Pathname)) }
 | |
|       def self.path_relative_to_workspace(path)
 | |
|         workspace = Pathname(ENV.fetch("GITHUB_WORKSPACE", Dir.pwd)).realpath
 | |
|         path = Pathname(path)
 | |
|         return path unless path.exist?
 | |
| 
 | |
|         path.realpath.relative_path_from(workspace)
 | |
|       end
 | |
| 
 | |
|       sig {
 | |
|         params(
 | |
|           type:       Symbol,
 | |
|           message:    String,
 | |
|           file:       T.any(String, Pathname),
 | |
|           title:      T.nilable(String),
 | |
|           line:       T.nilable(Integer),
 | |
|           end_line:   T.nilable(Integer),
 | |
|           column:     T.nilable(Integer),
 | |
|           end_column: T.nilable(Integer),
 | |
|         ).void
 | |
|       }
 | |
|       def initialize(type, message, file:, title: nil, line: nil, end_line: nil, column: nil, end_column: nil)
 | |
|         raise ArgumentError, "Unsupported type: #{type.inspect}" if ANNOTATION_TYPES.exclude?(type)
 | |
| 
 | |
|         @type = type
 | |
|         @message = Tty.strip_ansi(message)
 | |
|         @file = self.class.path_relative_to_workspace(file)
 | |
|         @title = Tty.strip_ansi(title) if title
 | |
|         @line = Integer(line) if line
 | |
|         @end_line = Integer(end_line) if end_line
 | |
|         @column = Integer(column) if column
 | |
|         @end_column = Integer(end_column) if end_column
 | |
|       end
 | |
| 
 | |
|       sig { returns(String) }
 | |
|       def to_s
 | |
|         metadata = @type.to_s
 | |
|         metadata << " file=#{Actions.escape(@file.to_s)}"
 | |
| 
 | |
|         if @line
 | |
|           metadata << ",line=#{@line}"
 | |
|           metadata << ",endLine=#{@end_line}" if @end_line
 | |
| 
 | |
|           if @column
 | |
|             metadata << ",col=#{@column}"
 | |
|             metadata << ",endColumn=#{@end_column}" if @end_column
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         metadata << ",title=#{Actions.escape(@title)}" if @title
 | |
| 
 | |
|         "::#{metadata}::#{Actions.escape(@message)}"
 | |
|       end
 | |
| 
 | |
|       # An annotation is only relevant if the corresponding `file` is relative to
 | |
|       # the `GITHUB_WORKSPACE` directory or if no `file` is specified.
 | |
|       sig { returns(T::Boolean) }
 | |
|       def relevant?
 | |
|         @file.descend.next.to_s != ".."
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |