Skip to content

Commit a78c977

Browse files
authored
Merge pull request #14 from ProRedCat/ro/crash-reporting-page
Crash Reporting page
2 parents 3585efd + 72304d5 commit a78c977

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1318
-156
lines changed

.github/workflows/deploy.yml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
name: Deploy to Amazon Linux EC2
2+
3+
on:
4+
push:
5+
branches:
6+
- "**"
7+
8+
jobs:
9+
deploy:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout code
14+
uses: actions/checkout@v4
15+
16+
- name: Set outputs
17+
id: vars
18+
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
19+
20+
- name: Set up .NET Core
21+
uses: actions/setup-dotnet@v4
22+
with:
23+
dotnet-version: "9.0.100"
24+
25+
- name: Install bun
26+
uses: oven-sh/setup-bun@v2
27+
28+
- name: Template appsettings.json
29+
run: |
30+
sed -i 's|#{RAYGUN_API_KEY}|'"$RAYGUN_API_KEY"'|g' src/Minigun/appsettings.json
31+
env:
32+
RAYGUN_API_KEY: ${{ secrets.RAYGUN_API_KEY }}
33+
34+
- name: Publish .NET Core app
35+
run: dotnet publish Minigun.sln -c Release -o ./publish -p:PublishEnvironment=CI
36+
37+
- name: Stop Application on EC2
38+
env:
39+
EC2_HOST: ${{ secrets.EC2_HOST }}
40+
EC2_USER: ${{ secrets.EC2_USER }}
41+
EC2_SSH_KEY: ${{ secrets.EC2_SSH_KEY }}
42+
run: |
43+
echo "$EC2_SSH_KEY" > ./ec2_key.pem
44+
chmod 600 ./ec2_key.pem
45+
46+
# Disable host key checking
47+
mkdir -p ~/.ssh
48+
echo -e "Host *\n\tStrictHostKeyChecking no" > ~/.ssh/config
49+
50+
ssh -i ./ec2_key.pem $EC2_USER@$EC2_HOST << 'EOF'
51+
sudo pkill Minigun || true
52+
EOF
53+
54+
sleep 2
55+
56+
- name: Upload files to EC2
57+
env:
58+
EC2_HOST: ${{ secrets.EC2_HOST }}
59+
EC2_USER: ${{ secrets.EC2_USER }}
60+
EC2_SSH_KEY: ${{ secrets.EC2_SSH_KEY }}
61+
DEPLOY_DIR: /var/www/minigun
62+
run: |
63+
# Upload files to EC2
64+
scp -i ./ec2_key.pem -r ./publish/* $EC2_USER@$EC2_HOST:$DEPLOY_DIR
65+
66+
- name: Create Raygun Deployment
67+
uses: MindscapeHQ/raygun-deployments-action@v1
68+
with:
69+
personal-access-token: ${{ secrets.RAYGUN_PAT }}
70+
api-key: ${{ secrets.RAYGUN_API_KEY }}
71+
version: ${{ steps.vars.outputs.sha_short }}
72+
comment: ${{ github.event.head_commit.message }}
73+
74+
- name: Start Application on EC2
75+
env:
76+
EC2_HOST: ${{ secrets.EC2_HOST }}
77+
EC2_USER: ${{ secrets.EC2_USER }}
78+
EC2_SSH_KEY: ${{ secrets.EC2_SSH_KEY }}
79+
DEPLOY_DIR: /var/www/minigun
80+
run: |
81+
ssh -i ./ec2_key.pem $EC2_USER@$EC2_HOST << EOF
82+
set -x
83+
84+
cd $DEPLOY_DIR
85+
sudo nohup ./Minigun > minigun.log 2>&1 &
86+
87+
ps aux | grep Minigun
88+
tail -n 20 minigun.log
89+
EOF
90+
91+
- name: Clean up
92+
run: rm -f ./ec2_key.pem

Directory.Build.props

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,19 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
77
<LangVersion>latest</LangVersion>
8-
<InvariantGlobalization>false</InvariantGlobalization>
8+
<InvariantGlobalization>true</InvariantGlobalization>
99
</PropertyGroup>
1010

1111
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
12+
<SelfContained>true</SelfContained>
1213
<PublishSingleFile>true</PublishSingleFile>
1314
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
1415

1516
<DebugSymbols>true</DebugSymbols>
1617
<DebugType>embedded</DebugType>
1718
</PropertyGroup>
19+
20+
<PropertyGroup Condition="'$(PublishEnvironment)' == 'CI'">
21+
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
22+
</PropertyGroup>
1823
</Project>

Directory.Packages.props

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
44
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
55
</PropertyGroup>
6-
76
<ItemGroup>
8-
<PackageVersion Include="Mindscape.Raygun4Net.AspNetCore" Version="11.1.1" />
9-
<PackageVersion Include="Mindscape.Raygun4Net.NetCore.Common" Version="11.1.1"/>
7+
<PackageVersion Include="Mindscape.Raygun4Net.AspNetCore" Version="11.2.1" />
8+
<PackageVersion Include="Mindscape.Raygun4Net.NetCore.Common" Version="11.2.1" />
109
</ItemGroup>
1110
</Project>

Minigun.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".stuff", ".stuff", "{90AA5B
77
.gitignore = .gitignore
88
Directory.Build.props = Directory.Build.props
99
Directory.Packages.props = Directory.Packages.props
10+
.github\workflows\deploy.yml = .github\workflows\deploy.yml
1011
EndProjectSection
1112
EndProject
1213
Global
Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using Microsoft.AspNetCore.Mvc;
2+
using Minigun.Areas.CrashReporting.Models;
3+
using Minigun.Models;
24
using Minigun.Services;
35

46
namespace Minigun.Areas.CrashReporting.Controllers;
@@ -16,35 +18,72 @@ public CrashReportingController(ILogger<CrashReportingController> logger, IRaygu
1618
}
1719

1820
[HttpGet("/crashreporting")]
19-
public async Task<IActionResult> Index()
21+
public async Task<IActionResult> Index([FromQuery] string? applicationIdentifier)
2022
{
21-
var applications = await _raygunApiService.ListApplicationsAsync(100);
22-
23-
if (applications == null || !applications.Any())
23+
if (applicationIdentifier == null)
2424
{
25-
return NotFound("No applications found.");
26-
}
25+
var applications = await _raygunApiService.ListApplicationsAsync(100);
2726

28-
var firstApplication = applications.First();
27+
if (applications.Count == 0)
28+
{
29+
return NotFound("No applications found.");
30+
}
2931

30-
return RedirectToAction("FullCrashPage", new { applicationIdentifier = firstApplication.Identifier });
32+
var firstApplication = applications.First();
33+
34+
return RedirectToAction("FullCrashPage", new { applicationIdentifier = firstApplication.Identifier });
35+
}
36+
37+
Response.Headers.Append("HX-Push", $"/crashreporting/{applicationIdentifier}/");
38+
39+
var viewModel = new CrashReportingViewModel(
40+
new List<ErrorGroup>(),
41+
new List<TimeseriesData>()
42+
);
43+
44+
return PartialView("Index", viewModel);
3145
}
3246

3347
[HttpGet("/crashreporting/{applicationIdentifier}")]
34-
public async Task<IActionResult> FullCrashPage(string applicationIdentifier)
48+
public IActionResult FullCrashPage(string applicationIdentifier)
3549
{
36-
// TODO: See if there is a nicer way to handle this, as this method is identical to the one below
37-
// this may be handled once we move to OnLoad fetching of partials rather than full page load
38-
var errorGroups = await _raygunApiService.ListErrorGroupsAsync(applicationIdentifier) ?? [];
50+
var viewModel = new CrashReportingViewModel(
51+
new List<ErrorGroup>(),
52+
new List<TimeseriesData>()
53+
);
54+
55+
return View("Index", viewModel);
56+
}
57+
58+
[HttpGet("/crashreporting/error-groups")]
59+
public async Task<IActionResult> ErrorGroupsPartial(
60+
[FromQuery] string applicationIdentifier,
61+
[FromQuery] DateTime startTime,
62+
[FromQuery] DateTime endTime
63+
)
64+
{
65+
var errorGroups = await _raygunApiService.ListErrorGroupsAsync(applicationIdentifier, orderby: ["lastOccurredAt desc"]);
3966

40-
return View("Index", errorGroups);
67+
var filteredGroups = errorGroups
68+
.Where(e => e.LastOccurredAt > startTime)
69+
.ToList();
70+
71+
Response.Headers.Append("HX-Push", $"/crashreporting/{applicationIdentifier}/");
72+
73+
return PartialView("_ErrorGroups", filteredGroups);
4174
}
4275

43-
[HttpGet("/crashreporting/{applicationIdentifier}/error-groups")]
44-
public async Task<IActionResult> ErrorGroupsPartial(string applicationIdentifier)
76+
[HttpGet("/crashreporting/error-timeseries")]
77+
public async Task<IActionResult> ErrorTimeseriesPartial(
78+
[FromQuery] string applicationIdentifier,
79+
[FromQuery] DateTime startTime,
80+
[FromQuery] DateTime endTime
81+
)
4582
{
46-
var errorGroups = await _raygunApiService.ListErrorGroupsAsync(applicationIdentifier) ?? [];
83+
var errorTimeseries = await _raygunApiService.GetErrorTimeseriesAsync(applicationIdentifier, startTime, endTime);
4784

48-
return PartialView("_ErrorGroups", errorGroups);
85+
Response.Headers.Append("HX-Push", $"/crashreporting/{applicationIdentifier}/");
86+
87+
return PartialView("_ErrorTimeseries", errorTimeseries);
4988
}
5089
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Minigun.Models;
2+
3+
namespace Minigun.Areas.CrashReporting.Models;
4+
5+
public class CrashReportingViewModel
6+
{
7+
public CrashReportingViewModel(IEnumerable<ErrorGroup> errorGroups, IEnumerable<TimeseriesData> errorsTimeseries)
8+
{
9+
ErrorGroups = errorGroups;
10+
ErrorsTimeseries = errorsTimeseries;
11+
}
12+
13+
public IEnumerable<ErrorGroup> ErrorGroups { get; set; }
14+
public IEnumerable<TimeseriesData> ErrorsTimeseries { get; set; }
15+
}
Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
1-
@model IEnumerable<Minigun.Models.ErrorGroup>
1+
@model Minigun.Areas.CrashReporting.Models.CrashReportingViewModel
22

33
@{
44
ViewData["Title"] = "Crash Reporting";
55
}
66

7-
<div id="crash-reporting-content">
8-
@await Html.PartialAsync("_ErrorGroups", Model)
9-
</div>
107

8+
<div id="crashreporting-content" class="space-y-4">
9+
<div id="error-timeseries-wrapper"
10+
hx-get="@Url.Action("ErrorTimeseriesPartial", "CrashReporting")"
11+
hx-include="#selected-application, #startTime, #endTime"
12+
hx-target="#error-timeseries-content"
13+
hx-indicator="#error-timeseries-content"
14+
hx-swap="outerHTML"
15+
hx-trigger="load once, applicationSelected from:document, dateRangeSelected from:document">
16+
@await Html.PartialAsync("_ErrorTimeseries", Model.ErrorsTimeseries)
17+
</div>
18+
19+
<div id="error-groups-wrapper"
20+
hx-get="@Url.Action("ErrorGroupsPartial", "CrashReporting")"
21+
hx-include="#selected-application, #startTime, #endTime"
22+
hx-target="#error-groups-content"
23+
hx-indicator="#error-groups-content"
24+
hx-swap="outerHTML"
25+
hx-trigger="load once, applicationSelected from:document, dateRangeSelected from:document">
26+
@await Html.PartialAsync("_ErrorGroups", Model.ErrorGroups)
27+
</div>
28+
</div>

0 commit comments

Comments
 (0)