-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgerrit_cache.py
More file actions
140 lines (122 loc) · 4.64 KB
/
Copy pathgerrit_cache.py
File metadata and controls
140 lines (122 loc) · 4.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# Copyright (c) 2025 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import contextlib
import json
import os
import re
import tempfile
import gclient_utils
import lockfile
import scm
@contextlib.contextmanager
def _AtomicFileWriter(path, mode='w'):
"""Atomic file writer context manager."""
# Create temp file in same directory to ensure atomic rename works
fd, temp_path = tempfile.mkstemp(dir=os.path.dirname(path),
text='b' not in mode)
try:
with os.fdopen(fd, mode) as f:
yield f
# Atomic rename
gclient_utils.safe_replace(temp_path, path)
except Exception:
# Cleanup on failure
if os.path.exists(temp_path):
os.remove(temp_path)
raise
class GerritCache(object):
"""Simple JSON file-based cache for Gerrit API results."""
def __init__(self, root_dir):
self.root_dir = root_dir
self.cache_path = self._get_cache_path()
@staticmethod
def get_repo_code_owners_enabled_key(host, project):
return 'code-owners.%s.%s.enabled' % (host, project)
@staticmethod
def get_host_code_owners_enabled_key(host):
return 'code-owners.%s.enabled' % host
def _get_cache_path(self):
path = os.environ.get('DEPOT_TOOLS_GERRIT_CACHE_PATH')
if path:
return path
try:
path = scm.GIT.GetConfig(self.root_dir,
'depot-tools.gerrit-cache-path')
if path:
return path
except Exception:
pass
try:
if self.root_dir:
# Use a deterministic name based on the repo path
sanitized_path = re.sub(r'[^a-zA-Z0-9]', '_',
os.path.abspath(self.root_dir))
filename = 'depot_tools_gerrit_cache_%s.json' % sanitized_path
path = os.path.join(tempfile.gettempdir(), filename)
else:
fd, path = tempfile.mkstemp(prefix='depot_tools_gerrit_cache_',
suffix='.json')
os.close(fd)
if not os.path.exists(path):
# Initialize with empty JSON object
with open(path, 'w') as f:
json.dump({}, f)
try:
scm.GIT.SetConfig(self.root_dir,
'depot-tools.gerrit-cache-path', path)
except Exception:
# If we can't set config (e.g. not a git repo and no env var
# set), just return the temp path. It will be a per-process
# cache in that case.
pass
return path
except Exception:
# Fallback to random temp file if everything else fails
fd, path = tempfile.mkstemp(prefix='gerrit_cache_', suffix='.json')
os.close(fd)
return path
def get(self, key):
if not self.cache_path:
return None
try:
with lockfile.lock(self.cache_path, timeout=1):
if os.path.exists(self.cache_path):
with open(self.cache_path, 'r') as f:
try:
data = json.load(f)
return data.get(key)
except ValueError:
# Corrupt cache file, treat as miss
return None
except Exception:
# Ignore cache errors
return None
def set(self, key, value):
if not self.cache_path:
return
try:
with lockfile.lock(self.cache_path, timeout=1):
data = {}
if os.path.exists(self.cache_path):
with open(self.cache_path, 'r') as f:
try:
data = json.load(f)
except ValueError:
# Corrupt cache, start fresh
data = {}
data[key] = value
with _AtomicFileWriter(self.cache_path, 'w') as f:
json.dump(data, f)
except Exception:
# Ignore cache errors
pass
def getBoolean(self, key):
"""Returns the value for key as a boolean, or None if missing."""
val = self.get(key)
if val is None:
return None
return bool(val)
def setBoolean(self, key, value):
"""Sets the value for key as a boolean."""
self.set(key, bool(value))