Skip to content

Commit 9e7fff5

Browse files
committed
Adding in release folder, it was missed on last checkin
1 parent 68e4c64 commit 9e7fff5

9 files changed

Lines changed: 548 additions & 0 deletions
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Net.Http;
6+
using System.Net.Http.Headers;
7+
using System.Security.Cryptography;
8+
using System.Threading.Tasks;
9+
using LidarrAPI.Database;
10+
using LidarrAPI.Database.Models;
11+
using LidarrAPI.Release.AppVeyor.Responses;
12+
using LidarrAPI.Update;
13+
using Microsoft.EntityFrameworkCore;
14+
using Microsoft.Extensions.Options;
15+
using Newtonsoft.Json;
16+
using OperatingSystem = LidarrAPI.Update.OperatingSystem;
17+
18+
namespace LidarrAPI.Release.AppVeyor
19+
{
20+
public class AppVeyorReleaseSource : ReleaseSourceBase
21+
{
22+
private const string AccountName = "lidarr";
23+
private const string ProjectSlug = "lidarr";
24+
25+
private static int? _lastBuildId;
26+
27+
private readonly Config _config;
28+
29+
private readonly DatabaseContext _database;
30+
31+
private readonly HttpClient _downloadHttpClient;
32+
33+
private readonly HttpClient _httpClient;
34+
35+
public AppVeyorReleaseSource(DatabaseContext database, IOptions<Config> config)
36+
{
37+
_database = database;
38+
_config = config.Value;
39+
40+
_httpClient = new HttpClient();
41+
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
42+
_httpClient.DefaultRequestHeaders.Authorization =
43+
new AuthenticationHeaderValue("Bearer", _config.AppVeyorApiKey);
44+
45+
_downloadHttpClient = new HttpClient();
46+
}
47+
48+
protected override async Task DoFetchReleasesAsync()
49+
{
50+
if (ReleaseBranch == Branch.Unknown)
51+
{
52+
throw new ArgumentException("ReleaseBranch must not be unknown when fetching releases.");
53+
}
54+
55+
var historyUrl =
56+
$"https://ci.appveyor.com/api/projects/{AccountName}/{ProjectSlug}/history?recordsNumber=10&branch=develop";
57+
58+
var historyData = await _httpClient.GetStringAsync(historyUrl);
59+
var history = JsonConvert.DeserializeObject<AppVeyorProjectHistory>(historyData);
60+
61+
// Store here temporarily so we don't break on not processed builds.
62+
var lastBuild = _lastBuildId;
63+
64+
foreach (var build in history.Builds.Take(5).ToList()) // Only take last 5.
65+
{
66+
if (lastBuild.HasValue &&
67+
lastBuild.Value >= build.BuildId) break;
68+
69+
// Make sure we dont distribute;
70+
// - pull requests,
71+
// - unsuccesful builds,
72+
// - tagged builds (duplicate).
73+
if (build.PullRequestId.HasValue ||
74+
build.IsTag) continue;
75+
76+
var buildExtendedData = await _httpClient.GetStringAsync(
77+
$"https://ci.appveyor.com/api/projects/{AccountName}/{ProjectSlug}/build/{build.Version}");
78+
var buildExtended = JsonConvert.DeserializeObject<AppVeyorProjectLastBuild>(buildExtendedData).Build;
79+
80+
// Filter out incomplete builds
81+
var buildJob = buildExtended.Jobs.FirstOrDefault();
82+
if (buildJob == null ||
83+
buildJob.ArtifactsCount == 0 ||
84+
!buildExtended.Started.HasValue) continue;
85+
86+
// Grab artifacts
87+
var artifactsPath = $"https://ci.appveyor.com/api/buildjobs/{buildJob.JobId}/artifacts";
88+
var artifactsData = await _httpClient.GetStringAsync(artifactsPath);
89+
var artifacts = JsonConvert.DeserializeObject<AppVeyorArtifact[]>(artifactsData);
90+
91+
// Get an updateEntity
92+
var updateEntity = _database.UpdateEntities
93+
.Include(x => x.UpdateFiles)
94+
.FirstOrDefault(x => x.Version.Equals(buildExtended.Version) && x.Branch.Equals(ReleaseBranch));
95+
96+
if (updateEntity == null)
97+
{
98+
// Create update object
99+
updateEntity = new UpdateEntity
100+
{
101+
Version = buildExtended.Version,
102+
ReleaseDate = buildExtended.Started.Value.UtcDateTime,
103+
Branch = ReleaseBranch,
104+
New = new List<string>
105+
{
106+
build.Message
107+
}
108+
};
109+
110+
// Add extra message
111+
if (!string.IsNullOrWhiteSpace(build.MessageExtended))
112+
{
113+
updateEntity.New.Add(build.MessageExtended);
114+
}
115+
116+
// Start tracking this object
117+
await _database.AddAsync(updateEntity);
118+
}
119+
120+
// Process artifacts
121+
foreach (var artifact in artifacts)
122+
{
123+
// Detect target operating system.
124+
OperatingSystem operatingSystem;
125+
126+
if (artifact.FileName.Contains("windows."))
127+
{
128+
operatingSystem = OperatingSystem.Windows;
129+
}
130+
else if (artifact.FileName.Contains("linux."))
131+
{
132+
operatingSystem = OperatingSystem.Linux;
133+
}
134+
else if (artifact.FileName.Contains("osx."))
135+
{
136+
operatingSystem = OperatingSystem.Osx;
137+
}
138+
else
139+
{
140+
continue;
141+
}
142+
143+
// Check if exists in database.
144+
var updateFileEntity = _database.UpdateFileEntities
145+
.FirstOrDefault(x =>
146+
x.UpdateEntityId == updateEntity.UpdateEntityId &&
147+
x.OperatingSystem == operatingSystem);
148+
149+
if (updateFileEntity != null) continue;
150+
151+
// Calculate the hash of the zip file.
152+
var releaseDownloadUrl = $"{artifactsPath}/{artifact.FileName}";
153+
var releaseFileName = artifact.FileName.Split('/').Last();
154+
var releaseZip = Path.Combine(_config.DataDirectory, ReleaseBranch.ToString(), releaseFileName);
155+
string releaseHash;
156+
157+
if (!File.Exists(releaseZip))
158+
{
159+
Directory.CreateDirectory(Path.GetDirectoryName(releaseZip));
160+
File.WriteAllBytes(releaseZip, await _downloadHttpClient.GetByteArrayAsync(releaseDownloadUrl));
161+
}
162+
163+
using (var stream = File.OpenRead(releaseZip))
164+
{
165+
using (var sha = SHA256.Create())
166+
{
167+
releaseHash = BitConverter.ToString(sha.ComputeHash(stream)).Replace("-", "").ToLower();
168+
}
169+
}
170+
171+
File.Delete(releaseZip);
172+
173+
// Add to database.
174+
updateEntity.UpdateFiles.Add(new UpdateFileEntity
175+
{
176+
OperatingSystem = operatingSystem,
177+
Filename = releaseFileName,
178+
Url = releaseDownloadUrl,
179+
Hash = releaseHash
180+
});
181+
}
182+
183+
// Save all changes to the database.
184+
await _database.SaveChangesAsync();
185+
186+
// Make sure we atleast skip this build next time.
187+
if (_lastBuildId == null ||
188+
_lastBuildId.Value < build.BuildId)
189+
{
190+
_lastBuildId = build.BuildId;
191+
}
192+
}
193+
}
194+
}
195+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Newtonsoft.Json;
2+
3+
namespace LidarrAPI.Release.AppVeyor.Responses
4+
{
5+
public class AppVeyorArtifact
6+
{
7+
8+
[JsonProperty("fileName", Required = Required.Always)]
9+
public string FileName;
10+
11+
[JsonProperty("type", Required = Required.Always)]
12+
public string Type;
13+
14+
[JsonProperty("size", Required = Required.Always)]
15+
public int Size;
16+
17+
}
18+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using Newtonsoft.Json;
2+
3+
namespace LidarrAPI.Release.AppVeyor.Responses
4+
{
5+
public class AppVeyorJob
6+
{
7+
8+
[JsonProperty("jobId", Required = Required.Always)]
9+
public string JobId { get; set; }
10+
11+
[JsonProperty("artifactsCount", Required = Required.Always)]
12+
public int ArtifactsCount { get; set; }
13+
14+
[JsonProperty("failedTestsCount", Required = Required.Always)]
15+
public int FailedTestsCount { get; set; }
16+
17+
[JsonProperty("status", Required = Required.Always)]
18+
public string Status { get; set; }
19+
20+
}
21+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using Newtonsoft.Json;
3+
4+
namespace LidarrAPI.Release.AppVeyor.Responses
5+
{
6+
public class AppVeyorProjectBuild
7+
{
8+
9+
[JsonProperty("buildId")]
10+
public int BuildId { get; set; }
11+
12+
[JsonProperty("jobs")]
13+
public AppVeyorJob[] Jobs { get; set; }
14+
15+
[JsonProperty("version", Required = Required.Always)]
16+
public string Version { get; set; }
17+
18+
[JsonProperty("message", Required = Required.Always)]
19+
public string Message { get; set; }
20+
21+
[JsonProperty("messageExtended")]
22+
public string MessageExtended { get; set; }
23+
24+
[JsonProperty("isTag", Required = Required.Always)]
25+
public bool IsTag { get; set; }
26+
27+
[JsonProperty("pullRequestId")]
28+
public int? PullRequestId { get; set; }
29+
30+
[JsonProperty("status", Required = Required.Always)]
31+
public string Status { get; set; }
32+
33+
public DateTimeOffset? Started { get; set; }
34+
35+
}
36+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Collections.Generic;
2+
using Newtonsoft.Json;
3+
4+
namespace LidarrAPI.Release.AppVeyor.Responses
5+
{
6+
public class AppVeyorProjectHistory
7+
{
8+
9+
[JsonProperty("builds")]
10+
public List<AppVeyorProjectBuild> Builds { get; set; }
11+
12+
}
13+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Newtonsoft.Json;
2+
3+
namespace LidarrAPI.Release.AppVeyor.Responses
4+
{
5+
public class AppVeyorProjectLastBuild
6+
{
7+
8+
[JsonProperty("build")]
9+
public AppVeyorProjectBuild Build { get; set; }
10+
11+
}
12+
}

0 commit comments

Comments
 (0)