5
5
from typing import Dict , Any , Iterable , Self , Optional , List
6
6
import yaml
7
7
from dataclasses import dataclass , fields , field
8
- from enum import Enum
9
8
import logging
10
9
11
10
logger = logging .getLogger (__name__ )
@@ -16,7 +15,7 @@ class CodeQLException(Exception):
16
15
17
16
18
17
@dataclass (kw_only = True , frozen = True , eq = True )
19
- class CodeQLPack :
18
+ class CodeQLPackConfig :
20
19
library : bool = False
21
20
name : str
22
21
version : Version = Version ("0.0.0" )
@@ -53,25 +52,18 @@ def get_pack_name(self) -> str:
53
52
def __hash__ (self ):
54
53
return hash (f"{ self .name } @{ str (self .version )} " )
55
54
56
-
57
- class CodeQLPackKind (Enum ):
58
- QUERY_PACK = 1
59
- LIBRARY_PACK = 2
60
- CUSTOMIZATION_PACK = 3
61
-
62
-
63
55
@dataclass (kw_only = True , frozen = True , eq = True )
64
- class ResolvedCodeQLPack ( CodeQLPack ) :
56
+ class CodeQLPack :
65
57
path : Path
66
- kind : CodeQLPackKind
67
-
68
- def __hash__ (self ):
69
- return CodeQLPack .__hash__ (self )
58
+ config : CodeQLPackConfig
70
59
60
+ def __hash__ (self ) -> int :
61
+ return hash (f"{ self .path } " )
71
62
72
63
class CodeQL :
73
64
def __init__ (self , codeql_path : Path ):
74
65
self .codeql_path = codeql_path
66
+ self ._version = None
75
67
76
68
def _exec (self , command : str , * args : str ) -> subprocess .CompletedProcess [str ]:
77
69
logger .debug (
@@ -80,16 +72,20 @@ def _exec(self, command: str, *args: str) -> subprocess.CompletedProcess[str]:
80
72
return subprocess .run (
81
73
[f"{ self .codeql_path } " , command ] + [arg for arg in args ],
82
74
capture_output = True ,
83
- text = True ,
75
+ text = True
84
76
)
85
77
86
78
def version (self ) -> Version :
87
- cp = self ._exec ("version" , "--format=json" )
88
- if cp .returncode == 0 :
89
- version_info = json .loads (cp .stdout )
90
- return Version (version_info ["version" ])
79
+ if self ._version != None :
80
+ return self ._version
91
81
else :
92
- raise CodeQLException (f"Failed to run { cp .args } command!" )
82
+ cp = self ._exec ("version" , "--format=json" )
83
+ if cp .returncode == 0 :
84
+ version_info = json .loads (cp .stdout )
85
+ self ._version = Version (version_info ["version" ])
86
+ return self ._version
87
+ else :
88
+ raise CodeQLException (f"Failed to run { cp .args } command!" )
93
89
94
90
def unpacked_location (self ) -> Path :
95
91
cp = self ._exec ("version" , "--format=json" )
@@ -102,44 +98,36 @@ def unpacked_location(self) -> Path:
102
98
def supports_qlx (self ) -> bool :
103
99
return self .version () >= Version ("2.11.4" )
104
100
105
- def pack_ls (self , workspace : Path = Path .cwd ()) -> List [ResolvedCodeQLPack ]:
101
+ def pack_ls (self , workspace : Path = Path .cwd ()) -> List [CodeQLPack ]:
106
102
cp = self ._exec ("pack" , "ls" , "--format=json" , str (workspace ))
107
103
if cp .returncode == 0 :
108
104
packs : Iterable [Path ] = map (Path , json .loads (cp .stdout )["packs" ].keys ())
109
105
110
- def load (qlpack : Path ) -> ResolvedCodeQLPack :
111
- with qlpack .open ("r" ) as qlpack_file :
112
- logger .debug (f"Resolving CodeQL pack at { qlpack } ." )
113
- qlpack_spec = yaml .safe_load (qlpack_file )
114
- qlpack_spec ["path" ] = qlpack
115
- if not "library" in qlpack_spec or not qlpack_spec ["library" ]:
116
- qlpack_spec ["kind" ] = CodeQLPackKind .QUERY_PACK
117
- else :
118
- if (
119
- qlpack_spec ["path" ].parent
120
- / qlpack_spec ["name" ].replace ("-" , "_" )
121
- / "Customizations.qll"
122
- ).exists ():
123
- qlpack_spec ["kind" ] = CodeQLPackKind .CUSTOMIZATION_PACK
124
- else :
125
- qlpack_spec ["kind" ] = CodeQLPackKind .LIBRARY_PACK
126
- resolved_pack = ResolvedCodeQLPack .from_dict (qlpack_spec )
106
+ def load (qlpack_yml_path : Path ) -> CodeQLPack :
107
+ with qlpack_yml_path .open ("r" ) as qlpack_yml_file :
108
+ logger .debug (f"Loading CodeQL pack configuration at { qlpack_yml_path } ." )
109
+ qlpack_yml_as_dict : Dict [str , Any ] = yaml .safe_load (qlpack_yml_file )
110
+ qlpack_config = CodeQLPackConfig .from_dict (qlpack_yml_as_dict )
111
+ qlpack = CodeQLPack (path = qlpack_yml_path , config = qlpack_config )
127
112
logger .debug (
128
- f"Resolved { resolved_pack . name } with version { str (resolved_pack . version )} at { resolved_pack .path } with kind { resolved_pack . kind . name } "
113
+ f"Loaded { qlpack . config . name } with version { str (qlpack . config . version )} at { qlpack .path } . "
129
114
)
130
- return resolved_pack
115
+ return qlpack
131
116
132
- logger .debug (f"Resolving CodeQL packs for workspace { workspace } " )
117
+ logger .debug (f"Listing CodeQL packs for workspace { workspace } " )
133
118
return list (map (load , packs ))
134
119
else :
135
120
raise CodeQLException (f"Failed to run { cp .args } command! { cp .stderr } " )
136
121
137
122
def pack_bundle (
138
123
self ,
139
- pack : ResolvedCodeQLPack ,
124
+ pack : CodeQLPack ,
140
125
output_path : Path ,
141
126
* additional_packs : Path ,
142
127
):
128
+ if not pack .config .library :
129
+ raise CodeQLException (f"Cannot bundle non-library pack { pack .config .name } !" )
130
+
143
131
args = ["bundle" , "--format=json" , f"--pack-path={ output_path } " ]
144
132
if len (additional_packs ) > 0 :
145
133
args .append (f"--additional-packs={ ':' .join (map (str ,additional_packs ))} " )
@@ -155,11 +143,14 @@ def pack_bundle(
155
143
156
144
def pack_create (
157
145
self ,
158
- pack : ResolvedCodeQLPack ,
146
+ pack : CodeQLPack ,
159
147
output_path : Path ,
160
148
* additional_packs : Path ,
161
149
):
162
- args = ["create" , "--format=json" , f"--output={ output_path } " , "--threads=0" ]
150
+ if pack .config .library :
151
+ raise CodeQLException (f"Cannot bundle non-query pack { pack .config .name } !" )
152
+
153
+ args = ["create" , "--format=json" , f"--output={ output_path } " , "--threads=0" , "--no-default-compilation-cache" ]
163
154
if self .supports_qlx ():
164
155
args .append ("--qlx" )
165
156
if len (additional_packs ) > 0 :
0 commit comments