Skip to content

Commit ddb4233

Browse files
committed
feat: annotation helpers
1 parent e7a1cf2 commit ddb4233

File tree

1 file changed

+84
-11
lines changed
  • gha-helper/gha_helper

1 file changed

+84
-11
lines changed

gha-helper/gha_helper/gh.py

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,40 @@
11
import json
22
import os
33
from datetime import datetime, timezone
4-
from typing import Any
5-
from zoneinfo import ZoneInfo
4+
from pathlib import Path
5+
from typing import Any, Optional
66

77

8-
class CICDInputError(Exception): pass
8+
class CICDInputError(Exception):
9+
pass
910

1011

1112
Unset = object()
1213

1314

15+
def _escape_data(s) -> str:
16+
return str(s).replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A")
17+
18+
19+
def _escape_property(s) -> str:
20+
return (
21+
str(s)
22+
.replace("%", "%25")
23+
.replace("\r", "%0D")
24+
.replace("\n", "%0A")
25+
.replace(":", "%3A")
26+
.replace(",", "%2C")
27+
)
28+
29+
1430
class Input:
1531
def get(self, name: str, default=Unset):
1632
env = os.getenv(name, default)
1733

1834
if env is Unset:
1935
raise CICDInputError(f"Cannot find input {name}. Previous jobs have probably failed.")
20-
21-
return env
36+
37+
return env
2238

2339
def get_timestamp(self, name: str) -> datetime:
2440
env = os.environ[name]
@@ -28,12 +44,11 @@ def get_timestamp(self, name: str) -> datetime:
2844
except:
2945
return datetime.strptime(env, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
3046

31-
32-
def get_json(self, name: str, default=Unset):
33-
env = os.getenv(name,)
34-
35-
if not env:
36-
if default is Unset:
47+
def get_json(self, name: str, default=Unset):
48+
env = os.getenv(name)
49+
50+
if not env:
51+
if default is Unset:
3752
raise CICDInputError(f"Cannot find input {name}. Previous jobs have probably failed.")
3853
else:
3954
return default
@@ -52,5 +67,63 @@ def save_json(self, name: str, value: Any):
5267
f.write(f"{name}={data}\n")
5368

5469

70+
class Annotation:
71+
def error(
72+
self,
73+
message: str,
74+
file: Optional[Path | str] = None,
75+
line: Optional[int] = None,
76+
col: Optional[int] = None,
77+
):
78+
self._print("error", message, file, line, col)
79+
80+
def warning(
81+
self,
82+
message: str,
83+
file: Optional[Path | str] = None,
84+
line: Optional[int] = None,
85+
col: Optional[int] = None,
86+
):
87+
self._print("warning", message, file, line, col)
88+
89+
def notice(
90+
self,
91+
message: str,
92+
file: Optional[Path | str] = None,
93+
line: Optional[int] = None,
94+
col: Optional[int] = None,
95+
):
96+
self._print("notice", message, file, line, col)
97+
98+
def _print(
99+
self,
100+
level: str,
101+
message: str,
102+
file: Optional[Path | str] = None,
103+
line: Optional[int] = None,
104+
col: Optional[int] = None,
105+
):
106+
if file is not None:
107+
print(f"In file {file}:{line}:{col}:")
108+
109+
props = []
110+
111+
if file is not None:
112+
props.append(f"file={_escape_property(file)}")
113+
114+
if line is not None:
115+
props.append(f"line={line}")
116+
117+
if col is not None:
118+
props.append(f"col={col}")
119+
120+
props_str = ",".join(props)
121+
if props_str:
122+
props_str = " " + props_str
123+
124+
print(f"::{level}{props_str}::{_escape_data(message)}")
125+
126+
55127
input = Input()
56128
output = Output()
129+
annotation = Annotation()

0 commit comments

Comments
 (0)