Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 24 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
version: 2
updates:
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10

- package-ecosystem: "nuget"
directory: "/FeedlySharp"
schedule:
interval: "weekly"
open-pull-requests-limit: 10

- package-ecosystem: "nuget"
directory: "/FeedlySharp.Tests"
schedule:
interval: "weekly"
open-pull-requests-limit: 10

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
68 changes: 68 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# GitHub Actions Workflows

This directory contains GitHub Actions workflows for CI/CD and package publishing.

## Workflows

### CI (`ci.yml`)
Runs on every push and pull request to main/master/develop branches.

**Features:**
- Builds the project
- Runs all tests
- Uploads test results and coverage reports
- Uses Codecov for coverage reporting

### Pull Request (`pr.yml`)
Runs on pull requests to main/master/develop branches.

**Features:**
- Cross-platform testing (Ubuntu, Windows, macOS)
- Build verification
- Test execution
- Code formatting verification

### Release (`release.yml`)
Publishes NuGet packages when:
- A tag matching `v*.*.*` is pushed (e.g., `v3.0.0`)
- Manual workflow dispatch with version input

**Features:**
- Builds and tests the project
- Creates NuGet package
- Publishes to NuGet.org
- Uploads artifacts

**Required Secrets:**
- `NUGET_API_KEY` - Your NuGet.org API key

### CodeQL Analysis (`codeql.yml`)
Security analysis using GitHub's CodeQL.

**Features:**
- Automated security scanning
- Runs on pushes, PRs, and weekly schedule
- C# code analysis

## Setting Up NuGet Publishing

1. Get your NuGet API key from https://www.nuget.org/account/apikeys
2. Add it as a secret in your GitHub repository:
- Go to Settings → Secrets and variables → Actions
- Click "New repository secret"
- Name: `NUGET_API_KEY`
- Value: Your NuGet API key
3. Create a release tag:
```bash
git tag v3.0.0
git push origin v3.0.0
```
Or use the workflow dispatch feature in the Actions tab.

## Dependabot

Dependabot is configured to automatically update:
- NuGet packages (weekly)
- GitHub Actions (weekly)

See `.github/dependabot.yml` for configuration.
49 changes: 49 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: CI

on:
push:
branches: [ main, master, develop ]
pull_request:
branches: [ main, master, develop ]

jobs:
build:
name: Build and Test
runs-on: ubuntu-latest

strategy:
matrix:
dotnet-version: ['10.0.x']

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet-version }}

- name: Restore dependencies
run: dotnet restore

- name: Build
run: dotnet build --no-restore --configuration Release

- name: Run tests
run: dotnet test --no-build --configuration Release --verbosity normal --logger "trx;LogFileName=test-results.trx" --collect:"XPlat Code Coverage"
continue-on-error: false

- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-${{ matrix.dotnet-version }}
path: '**/test-results.trx'

- name: Upload coverage reports
uses: codecov/codecov-action@v4
if: always()
with:
files: '**/coverage.cobertura.xml'
fail_ci_if_error: false
46 changes: 46 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: CodeQL Analysis

on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
schedule:
- cron: '0 0 * * 0' # Weekly on Sunday

jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write

strategy:
fail-fast: false
matrix:
language: [ 'csharp' ]

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'

- name: Restore dependencies
run: dotnet restore

- name: Build
run: dotnet build --no-restore --configuration Release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
36 changes: 36 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Pull Request

on:
pull_request:
branches: [ main, master, develop ]

jobs:
build:
name: Build and Test
runs-on: ubuntu-latest

strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
dotnet-version: ['10.0.x']

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet-version }}

- name: Restore dependencies
run: dotnet restore

- name: Build
run: dotnet build --no-restore --configuration Release

- name: Run tests
run: dotnet test --no-build --configuration Release --verbosity normal

- name: Check code formatting
run: dotnet format --verify-no-changes --verbosity diagnostic
69 changes: 69 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Release

on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
version:
description: 'Version to publish (e.g., 3.0.0)'
required: true
type: string

jobs:
build-and-publish:
name: Build and Publish to NuGet
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'

- name: Determine version
id: version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
VERSION="${{ github.event.inputs.version }}"
else
VERSION=${GITHUB_REF#refs/tags/v}
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Determined version: $VERSION"

- name: Restore dependencies
run: dotnet restore

- name: Build
run: dotnet build --no-restore --configuration Release -p:Version=${{ steps.version.outputs.version }}

- name: Run tests
run: dotnet test --no-build --configuration Release --verbosity normal

- name: Pack
run: dotnet pack FeedlySharp/FeedlySharp.csproj --no-build --configuration Release -p:Version=${{ steps.version.outputs.version }} -p:PackageVersion=${{ steps.version.outputs.version }} --output ./artifacts

- name: Publish to NuGet
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
if [ -z "$NUGET_API_KEY" ]; then
echo "NUGET_API_KEY secret is not set. Skipping NuGet publish."
exit 0
fi
dotnet nuget push ./artifacts/*.nupkg --api-key $NUGET_API_KEY --source https://api.nuget.org/v3/index.json --skip-duplicate

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: nuget-packages
path: ./artifacts/*.nupkg
retention-days: 30
48 changes: 32 additions & 16 deletions FeedlySharp.Tests/Authentication_Tests.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,37 @@
using FeedlySharp.Models;
using FeedlySharp.Models;
using FluentAssertions;
using Xunit;

namespace FeedlySharp.Tests
namespace FeedlySharp.Tests;

public class Authentication_Tests
{
public class Authentication_Tests
[Fact(Skip = "Requires invalid token to test")]
public async Task Should_throw_feedly_exception_unauthorized()
{
var options = new FeedlyOptions { AccessToken = "__invalid__", Domain = "https://cloud.feedly.com/" };
var feedlySharp = new FeedlySharpHttpClient(options);

await Assert.ThrowsAsync<FeedlyException>(() => feedlySharp.GetProfileAsync());
}

[Fact]
public void Should_throw_when_access_token_is_whitespace()
{
var options = new FeedlyOptions { AccessToken = " ", Domain = "https://cloud.feedly.com/" };

Action act = () => new FeedlySharpHttpClient(options);

act.Should().Throw<ArgumentException>();
}

[Fact]
public void Should_throw_when_access_token_is_empty()
{
[Fact]
public void Should_throw_feedly_exception_unauthorized()
{
var feedlySharp = Mocks.MockFeedlyHttpClient;

feedlySharp.Options.AccessToken = "__invalid__";

Assert.Throws<FeedlyException>(() =>
{
feedlySharp.GetProfile().GetAwaiter().GetResult();
});
}
var options = new FeedlyOptions { AccessToken = string.Empty, Domain = "https://cloud.feedly.com/" };

Action act = () => new FeedlySharpHttpClient(options);

act.Should().Throw<ArgumentException>();
}
}
}
Loading
Loading