Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

"""Wrappers for the dependency configuration files.""" 

 

import os 

import logging 

 

import yorm 

from yorm.types import String, SortedList 

 

from .. import common 

from .. import shell 

from . import Source 

 

log = logging.getLogger(__name__) 

 

 

@yorm.attr(location=String) 

@yorm.attr(sources=SortedList.of_type(Source)) 

@yorm.attr(sources_locked=SortedList.of_type(Source)) 

@yorm.sync("{self.root}/{self.filename}", auto_save=False) 

class Config(yorm.ModelMixin): 

"""A dictionary of dependency configuration options.""" 

 

LOG = "gitman.log" 

 

def __init__(self, root=None, 

filename="gitman.yml", location="gitman_sources"): 

super().__init__() 

self.root = root or os.getcwd() 

self.filename = filename 

self.location = location 

self.sources = [] 

self.sources_locked = [] 

 

@property 

def config_path(self): 

"""Get the full path to the configuration file.""" 

return os.path.normpath(os.path.join(self.root, self.filename)) 

path = config_path 

 

@property 

def log_path(self): 

"""Get the full path to the log file.""" 

return os.path.normpath(os.path.join(self.location_path, self.LOG)) 

 

@property 

def location_path(self): 

"""Get the full path to the dependency storage location.""" 

return os.path.normpath(os.path.join(self.root, self.location)) 

 

def get_path(self, name=None): 

"""Get the full path to a dependency or internal file.""" 

base = self.location_path 

if name == '__config__': 

return self.path 

elif name == '__log__': 

return self.log_path 

elif name: 

return os.path.normpath(os.path.join(base, name)) 

else: 

return base 

 

def install_dependencies(self, *names, depth=None, 

update=True, recurse=False, 

force=False, fetch=False, clean=True): 

"""Download or update the specified dependencies.""" 

if depth == 0: 

log.info("Skipped directory: %s", self.location_path) 

return 0 

 

sources = self._get_sources(use_locked=False if update else None) 

71 ↛ exitline 71 didn't run the list comprehension on line 71 sources_filter = list(names) if names else [s.name for s in sources] 

 

73 ↛ 74line 73 didn't jump to line 74, because the condition on line 73 was never true if not os.path.isdir(self.location_path): 

shell.mkdir(self.location_path) 

shell.cd(self.location_path) 

common.newline() 

common.indent() 

 

count = 0 

80 ↛ 81line 80 didn't jump to line 81, because the loop on line 80 never started for source in sources: 

if source.name in sources_filter: 

sources_filter.remove(source.name) 

else: 

log.info("Skipped dependency: %s", source.name) 

continue 

 

source.update_files(force=force, fetch=fetch, clean=clean) 

source.create_link(self.root, force=force) 

common.newline() 

count += 1 

 

config = load_config() 

if config: 

common.indent() 

count += config.install_dependencies( 

depth=None if depth is None else max(0, depth - 1), 

update=update and recurse, 

recurse=recurse, 

force=force, 

fetch=fetch, 

clean=clean, 

) 

common.dedent() 

 

shell.cd(self.location_path, _show=False) 

 

common.dedent() 

108 ↛ 112line 108 didn't jump to line 112, because the condition on line 108 was never false if sources_filter: 

log.error("No such dependency: %s", ' '.join(sources_filter)) 

return 0 

 

return count 

 

def run_scripts(self, *names, depth=None, force=False): 

"""Run scripts for the specified dependencies.""" 

if depth == 0: 

log.info("Skipped directory: %s", self.location_path) 

return 0 

 

sources = self._get_sources() 

sources_filter = list(names) if names else [s.name for s in sources] 

 

shell.cd(self.location_path) 

common.newline() 

common.indent() 

 

count = 0 

for source in sources: 

if source.name in sources_filter: 

source.run_scripts(force=force) 

count += 1 

 

config = load_config() 

if config: 

common.indent() 

count += config.run_scripts( 

depth=None if depth is None else max(0, depth - 1), 

force=force, 

) 

common.dedent() 

 

shell.cd(self.location_path, _show=False) 

 

common.dedent() 

 

return count 

 

def lock_dependencies(self, *names, obey_existing=True): 

"""Lock down the immediate dependency versions.""" 

sources = self._get_sources(use_locked=obey_existing).copy() 

sources_filter = list(names) if names else [s.name for s in sources] 

 

shell.cd(self.location_path) 

common.newline() 

common.indent() 

 

count = 0 

for source in sources: 

if source.name not in sources_filter: 

log.info("Skipped dependency: %s", source.name) 

continue 

 

try: 

index = self.sources_locked.index(source) 

except ValueError: 

self.sources_locked.append(source.lock()) 

else: 

self.sources_locked[index] = source.lock() 

count += 1 

 

shell.cd(self.location_path, _show=False) 

 

if count: 

self.save() 

 

return count 

 

def uninstall_dependencies(self): 

"""Delete the dependency storage location.""" 

shell.cd(self.root) 

shell.rm(self.location_path) 

common.newline() 

 

def get_dependencies(self, depth=None, allow_dirty=True): 

"""Yield the path, repository URL, and hash of each dependency.""" 

if not os.path.exists(self.location_path): 

return 

 

shell.cd(self.location_path) 

common.newline() 

common.indent() 

 

for source in self.sources: 

 

if depth == 0: 

log.info("Skipped dependency: %s", source.name) 

continue 

 

yield source.identify(allow_dirty=allow_dirty) 

 

config = load_config() 

if config: 

common.indent() 

yield from config.get_dependencies( 

depth=None if depth is None else max(0, depth - 1), 

allow_dirty=allow_dirty, 

) 

common.dedent() 

 

shell.cd(self.location_path, _show=False) 

 

common.dedent() 

 

def log(self, message="", *args): 

"""Append a message to the log file.""" 

with open(self.log_path, 'a') as outfile: 

outfile.write(message.format(*args) + '\n') 

 

def _get_sources(self, *, use_locked=None): 

"""Merge source lists using the requested section as the base.""" 

221 ↛ 222line 221 didn't jump to line 222, because the condition on line 221 was never true if use_locked is True: 

if self.sources_locked: 

return self.sources_locked 

else: 

log.info("No locked sources, defaulting to none...") 

return [] 

 

sources = [] 

229 ↛ 232line 229 didn't jump to line 232, because the condition on line 229 was never false if use_locked is False: 

sources = self.sources 

else: 

if self.sources_locked: 

log.info("Defaulting to locked sources...") 

sources = self.sources_locked 

else: 

log.info("No locked sources, using latest...") 

sources = self.sources 

 

extras = [] 

240 ↛ 241line 240 didn't jump to line 241, because the loop on line 240 never started for source in self.sources + self.sources_locked: 

if source not in sources: 

log.info("Source %r missing from selected section", source.name) 

extras.append(source) 

 

return sources + extras 

 

 

def load_config(root=None): 

"""Load the configuration for the current project.""" 

if root is None: 

root = os.getcwd() 

 

for filename in os.listdir(root): 

if _valid_filename(filename): 

config = Config(root, filename) 

log.debug("Loaded config: %s", config.path) 

return config 

 

log.debug("No config found in: %s", root) 

return None 

 

 

def _valid_filename(filename): 

name, ext = os.path.splitext(filename.lower()) 

if name.startswith('.'): 

name = name[1:] 

return name in ['gitman', 'gdm'] and ext in ['.yml', '.yaml']