Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0b7c14a
refactor: extracted interactions with SQL into repository, commands a…
Aug 15, 2025
7f56cd5
fix tests y removing migration on start in test run
Aug 16, 2025
42a870f
refactor: extracted labels as separate classes, and label manager to …
Aug 16, 2025
816b700
refactor: extracted pull request sub-events logic
Aug 16, 2025
1cfb02f
fix calling OnReviewRequested when synced
Aug 17, 2025
d406e53
refactor: fix test data
Aug 17, 2025
3f14499
merge with master
Aug 17, 2025
64a66cf
refactor: warning as errors + warnings and startup failures fixups
Aug 17, 2025
46cf052
fix missing space for stage of work labels
Aug 17, 2025
6fcbab2
more DynamicallyAccessedMembers marking for safety!
Aug 17, 2025
31a6d35
refactor: introduced fluent migrator, postgresql, docker-compose with…
Aug 19, 2025
fcddee2
refactor: fix missing default database in pipeline
Aug 19, 2025
9880d1a
fix: fix pipeline
Aug 19, 2025
0fcc230
fix: fix pipeline
Aug 19, 2025
517c757
create if not exists
Aug 19, 2025
f55f5b7
merge: merge with master
Aug 21, 2025
8449f5f
refactor: test for pg sql repo
Aug 21, 2025
7b71634
refactor: fix old postgres version, remove sql-related code leftovers
Sep 7, 2025
ddbde08
refactor: revert docker-compose changes
Sep 8, 2025
56a8487
refactor: removed aot related attribute
Sep 8, 2025
320a0ae
merge: merge with master
Sep 29, 2025
55235e8
feat: use ef core migrations
Sep 29, 2025
94125c0
fix: removed migration being executed twice
Sep 29, 2025
038e404
fix: pipeline fixed (please?)
Sep 29, 2025
6fc6fcf
fix: fix pipeline (pretty please)
Sep 29, 2025
128ac4c
fix: fix pipeline prettypretty please
Sep 29, 2025
528b3cf
refactor: fix deploy pipeline using NAOT
Sep 29, 2025
75c14be
fix: registered github retry handler
Oct 9, 2025
33e8fb4
refactor: use aspnet runtime image instead of copy self-contained run…
Oct 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ jobs:
build:
runs-on: ubuntu-latest

services:
postgres:
image: postgres:16 # Or your desired PostgreSQL version
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: labeller
ports:
- 5432:5432 # Expose the PostgreSQL port
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- uses: actions/checkout@v2
- name: Setup .NET
Expand Down
9 changes: 2 additions & 7 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,9 @@ jobs:
with:
dotnet-version: '9.0.x'

- name: Install NativeAOT Dependencies
- name: Publish app
run: |
sudo apt-get update
sudo apt-get install -y clang zlib1g-dev

- name: Publish NativeAOT app
run: |
dotnet publish ./SS14.Labeller -c Release -r ${{ matrix.rid }} --self-contained true /p:PublishAot=true -o publish
dotnet publish ./SS14.Labeller -c Release -r ${{ matrix.rid }} --self-contained true -o publish

- name: Prepare release zip
run: |
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ RUN echo "Building for platform: $TARGETPLATFORM" \
"linux/arm64") export RID=linux-arm64 ;; \
*) echo "Unsupported TARGETPLATFORM: $TARGETPLATFORM" && exit 1 ;; \
esac \
&& dotnet publish -c Release -r $RID --self-contained true /p:PublishAot=true -o /app

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would make the container not run since the dotnet runtime is not in the container

@Fildrance Fildrance Sep 29, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--self-contained true is still there - why would there be problem with that?!
Tested it with this Dockerfile and got no 'missing dotnet' problem :/ am i doing something wrong?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right! Yeah. Though for download size it would probably be best that the Docker image your standard dotnet runtime image since then users won't have to download the runtime twice if they already had a docker image with it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you would better like to have 9.0.9-alpine3.22 here instead of what it currently uses?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, something like that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

&& dotnet publish -c Release -r $RID -o /app

FROM debian:bookworm-slim AS final
FROM mcr.microsoft.com/dotnet/aspnet:9.0.10-bookworm-slim AS final
WORKDIR /app

RUN apt-get update && apt-get install -y --no-install-recommends \
Expand Down
36 changes: 30 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,21 @@

Simple ASP.NET NativeAOT application for labelling our [content repository](https://github.com/space-wizards/space-station-14)

## Usage
## Features

Main features of application are related to reaction to github events:
* Event of issue creation will lead to issue getting marked as ```S: Untriaged``` label
* Event of leaving review under pull request will lead to marking it with ```S: Approved``` or ```S: Awaiting Changes``` labels
* Events, related to pull request support following labels:
* marked with ```size/XS```, ```size/S```, ```size/M```, ```size/L``` or ```size/XL``` for size (depending on a total lines changed in PR)
* marked with ```Changes: Audio```, ```Changes: Map```, ```Changes: NoCSharp```, ```Changes: Shaders```, ```Changes: Sprites```, ```Changes: Ui``` labels based on extensions and routes of files that were affected by PR
* marked ```Branch: Stable``` or ```Branch: Staging``` if PR is targeting specific branch
* marked with ```S: Untriaged``` on creation
* marked ```S: Approved``` if created by maintainer
* can create discourse thread when PR is labelled with ```S: Undergoing Discussion``` by gh users (will post link to discussion in comment of PR)
* marks with either ```S: Needs Review``` or ```S: Awaiting Changes``` depending on review state that maintainers leave on PR (set to ```S: Needs Review``` on opening)

## Usage

Create the a file called appsettings.json like so:
```json
Expand Down Expand Up @@ -37,12 +51,12 @@ To set the port, use the `ASPNETCORE_URLS` environment variable, e.g. `ASPNETCOR
*DiscussionCategoryId*: What category to send new discussion Topics in. You can get this by opening the Topic in your browser and the number in the URL is the category ID.\
*Url*: The Forum URL. Must end with a trailing slash.

## Building
## Building and setting up hooks

To build the application, use the following command:
To build application for release and deployment, use the following command:

```bash
dotnet publish ./SS14.Labeller -c Release -r <platform ex. win-x64> --self-contained true /p:PublishAot=true
dotnet publish ./SS14.Labeller -c Release -r <platform ex. win-x64> --self-contained true
```

Running the application is just like any other executable. On Unix systems, you may need to set the executable bit on the binary.
Expand All @@ -54,17 +68,27 @@ The token must have the `Issues` and `Pull requests` scopes enabled for read and

## Testing and debug

To run application locally you can launch run it as any other dotnet application. To set up its dependencies (database) locally you can run docker-compose:
```
docker compose -f docker-compose-debug.yml up -d
```
This will run local postgres to which labeller will try attach upon launching and when running integration tests.

### Forwarding github events for local debugging
You will need set up proxy for messages from GitHub to your local machine. For that you can use https://smee.io
1. Install Smee cli using npm (if you don't have it - follow those instructions [here](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm))\
``` npm install --global smee-client ```
2. Visit https://smee.io, click 'Start a new channel' and copy the link that will be generated.
3. In your console use Smee cli to start proxy forwarding to your local machine\
```smee -u https://smee.io/{place-you-channel-code-here} -t http://127.0.0.1:5000/webhook```
3. In your console use Smee cli to start proxy forwarding to your local machine: ``` smee -u https://smee.io/{place-you-channel-code-here} -t http://127.0.0.1:5000/webhook ```


Upon launching, it will output line like


```
Forwarding https://smee.io/{place-you-channel-code-here} to http://127.0.0.1:5000/webhook
```

That means that every message it receives, including
* Its payload
* Its headers
Expand Down
12 changes: 3 additions & 9 deletions SS14.Labeller.Tests/CustomWebApplicationFactory.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using NSubstitute;
using SS14.Labeller.Database;
using SS14.Labeller.DiscourseApi;
using SS14.Labeller.GitHubApi;
using SS14.Labeller.Repository;
using SS14.Labeller.Tests.IntegrationTests;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.

namespace SS14.Labeller.Tests;

Expand All @@ -35,11 +34,6 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
sp.Replace(new ServiceDescriptor(typeof(IGitHubApiClient), GitHubApiClient));
sp.Replace(new ServiceDescriptor(typeof(IDiscourseClient), DiscourseClient));
sp.Replace(new ServiceDescriptor(typeof(IDiscourseTopicsRepository), TopicsRepository));
var hostedServiceDescriptor = sp.First(d =>
d.ServiceType == typeof(IHostedService) &&
d.ImplementationType == typeof(DatabaseMigrationApplyingBackgroundService)); // Replace YourHostedService with the actual type

sp.Remove(hostedServiceDescriptor);
}).ConfigureAppConfiguration((context, configurationBuilder) =>
{
configurationBuilder.AddInMemoryCollection(new Dictionary<string, string?>
Expand All @@ -48,7 +42,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
{ "Discourse:Username", "aw" },
{ "Discourse:DiscussionCategoryId", "42" },
{ "Discourse:Url", "http://wa.wa" },
{ "GitHub:WebhookSecret", IntegrationTests.HookSecret },
{ "GitHub:WebhookSecret", HandlersTests.HookSecret },
{ "GitHub:Token", "test-test" },
});
});
Expand Down
12 changes: 6 additions & 6 deletions SS14.Labeller.Tests/GitHubApi/GithubRetryHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@
_mockInnerHandler.Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>())
.Returns(Task.FromResult(new HttpResponseMessage { StatusCode = HttpStatusCode.OK }));

var handler = new GithubRetryHandler(_mockInnerHandler, _config, _logger);
var handler = new GithubRetryHandler(_config, _logger) { InnerHandler = _mockInnerHandler };
var httpClient = new HttpClient(handler);

// Act
var result = httpClient.SendAsync(_httpRequestMessage, default).Result;

// Assert
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);

Check warning on line 49 in SS14.Labeller.Tests/GitHubApi/GithubRetryHandlerTests.cs

View workflow job for this annotation

GitHub Actions / build

Consider using the constraint model, Assert.That(actual, Is.EqualTo(expected)), instead of the classic model, Assert.AreEqual(expected, actual) (https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit2005.md)

Check warning on line 49 in SS14.Labeller.Tests/GitHubApi/GithubRetryHandlerTests.cs

View workflow job for this annotation

GitHub Actions / build

Consider using the constraint model, Assert.That(actual, Is.EqualTo(expected)), instead of the classic model, Assert.AreEqual(expected, actual) (https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit2005.md)
}

[Test]
Expand All @@ -55,18 +55,18 @@
// Arrange
_mockInnerHandler.Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>())
.Returns(
_=> throw new HttpRequestException(HttpRequestError.ConnectionError),
_=> Task.FromResult(new HttpResponseMessage { StatusCode = HttpStatusCode.OK })
_ => throw new HttpRequestException(HttpRequestError.ConnectionError),
_ => Task.FromResult(new HttpResponseMessage { StatusCode = HttpStatusCode.OK })
);

var handler = new GithubRetryHandler(_mockInnerHandler, _config, _logger);
var handler = new GithubRetryHandler(_config, _logger) { InnerHandler = _mockInnerHandler };
var httpClient = new HttpClient(handler);

// Act
var result = httpClient.SendAsync(_httpRequestMessage, default).Result;

// Assert
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);

Check warning on line 69 in SS14.Labeller.Tests/GitHubApi/GithubRetryHandlerTests.cs

View workflow job for this annotation

GitHub Actions / build

Consider using the constraint model, Assert.That(actual, Is.EqualTo(expected)), instead of the classic model, Assert.AreEqual(expected, actual) (https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit2005.md)

Check warning on line 69 in SS14.Labeller.Tests/GitHubApi/GithubRetryHandlerTests.cs

View workflow job for this annotation

GitHub Actions / build

Consider using the constraint model, Assert.That(actual, Is.EqualTo(expected)), instead of the classic model, Assert.AreEqual(expected, actual) (https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit2005.md)
}

[Test]
Expand All @@ -87,7 +87,7 @@
_mockInnerHandler.Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>())
.Returns(response1, response2);

var handler = new GithubRetryHandler(_mockInnerHandler, _config, _logger);
var handler = new GithubRetryHandler(_config, _logger) { InnerHandler = _mockInnerHandler };
var httpClient = new HttpClient(handler);

// Act
Expand All @@ -108,7 +108,7 @@

_gitHubConfig.MaxRetryAttempt = 2;

var handler = new GithubRetryHandler(_mockInnerHandler, _config, _logger);
var handler = new GithubRetryHandler(_config, _logger) { InnerHandler = _mockInnerHandler };
var httpClient = new HttpClient(handler);

// Act & Assert
Expand All @@ -128,7 +128,7 @@
return Send(request, cancellationToken);
}

public virtual Task<HttpResponseMessage> Send(HttpRequestMessage request, CancellationToken cancellationToken)

Check warning on line 131 in SS14.Labeller.Tests/GitHubApi/GithubRetryHandlerTests.cs

View workflow job for this annotation

GitHub Actions / build

'MockHttpMessageHandler.Send(HttpRequestMessage, CancellationToken)' hides inherited member 'HttpMessageHandler.Send(HttpRequestMessage, CancellationToken)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.

Check warning on line 131 in SS14.Labeller.Tests/GitHubApi/GithubRetryHandlerTests.cs

View workflow job for this annotation

GitHub Actions / build

'MockHttpMessageHandler.Send(HttpRequestMessage, CancellationToken)' hides inherited member 'HttpMessageHandler.Send(HttpRequestMessage, CancellationToken)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
{
throw new NotImplementedException();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using NSubstitute;
using NUnit.Framework;
using SS14.Labeller.Models;
using System.Net;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using NSubstitute;
using NUnit.Framework;
using SS14.Labeller.Labelling.Labels;
using SS14.Labeller.Models;

namespace SS14.Labeller.Tests;
namespace SS14.Labeller.Tests.IntegrationTests;

public partial class IntegrationTests
public partial class HandlersTests
{
[Test]
public async Task Issue_Created_AddedUntriagedLabel()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using NSubstitute;
using NUnit.Framework;
using SS14.Labeller.Models;
using System.Net;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using NSubstitute;
using NUnit.Framework;
using SS14.Labeller.Labelling.Labels;
using SS14.Labeller.Models;

namespace SS14.Labeller.Tests;
namespace SS14.Labeller.Tests.IntegrationTests;

public partial class IntegrationTests
public partial class HandlersTests
{
[Test]
public async Task PullRequest()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using NSubstitute;
using NUnit.Framework;
using SS14.Labeller.Models;
using System.Net;
using System.Threading.Tasks;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using NSubstitute;
using NUnit.Framework;
using SS14.Labeller.Labelling.Labels;
using SS14.Labeller.Models;

namespace SS14.Labeller.Tests;
namespace SS14.Labeller.Tests.IntegrationTests;

public partial class IntegrationTests
public partial class HandlersTests
{

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
using NUnit.Framework;
using SS14.Labeller.Helpers;

namespace SS14.Labeller.Tests;
namespace SS14.Labeller.Tests.IntegrationTests;

[ExcludeFromCodeCoverage]
public partial class IntegrationTests
public partial class HandlersTests
{
public const string HookSecret = "asdasdasdasdasdasdasdadsadad";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System.Threading.Tasks;
using Dapper;
using Microsoft.Extensions.Configuration;
using Npgsql;
using NUnit.Framework;
using SS14.Labeller.Repository;

namespace SS14.Labeller.Tests.IntegrationTests.Repository;

public class DiscourseTopicsRepositoryTests
{
private IDiscourseTopicsRepository _repository;

[SetUp]
public void Setup()
{
_repository = new DiscourseTopicsRepository(TestSetup.Configuration);

CleanUpDb();
}

[Test]
public async Task HasTopic_NoSuchParamsCombination_IsFalse()
{
// Arrange

// Act
var actual = await _repository.HasTopic("some-random-owner", "some-random-name", 54353, default);

// Assert
Assert.That(actual, Is.False);
}


[Test]
public async Task Add_DoesNotExist_IsAdded()
{
// Arrange
const string owner = "some-random-owner";
const string repoName = "some-random-name";
const int issueNumber = 54353;
var before = await _repository.HasTopic(owner, repoName, issueNumber, default);

// Act
await _repository.Add(owner, repoName, issueNumber, 1231, default);

// Assert
var after = await _repository.HasTopic(owner, repoName, issueNumber, default);

Assert.That(before, Is.False);
Assert.That(after, Is.True);
}

[TearDown]
public void TearDown()
{
CleanUpDb();
}

private static void CleanUpDb()
{
var connectionString = TestSetup.Configuration.GetConnectionString("Default");
using var con = new NpgsqlConnection(connectionString);
con.Open();
con.Execute("TRUNCATE TABLE discourse.discussions");
}
}
21 changes: 21 additions & 0 deletions SS14.Labeller.Tests/TestSetup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.IO;
using Microsoft.Extensions.Configuration;
using NUnit.Framework;

namespace SS14.Labeller.Tests;

[SetUpFixture]
public class TestSetup
{
public static IConfiguration Configuration { get; private set; }

[OneTimeSetUp]
public void RunBeforeAnyTests()
{
Configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
}
}
5 changes: 4 additions & 1 deletion SS14.Labeller.sln
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35818.85 d17.13
VisualStudioVersion = 17.13.35818.85

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pepeHands when will M$ release xml-based sln files, ffs

MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SS14.Labeller", "SS14.Labeller\SS14.Labeller.csproj", "{23A01977-7FE4-491F-BBE8-E8C412D8D753}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
ProjectSection(SolutionItems) = preProject
docker-compose-debug.yml = docker-compose-debug.yml
docker-compose.yml = docker-compose.yml
Dockerfile = Dockerfile
README.md = README.md
EndProjectSection
EndProject
Expand Down
Loading
Loading