Skip to content
Merged

V7 #13

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
48 changes: 12 additions & 36 deletions .github/actions/push-package/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,22 @@ inputs:
default: 'false'
required: false
config:
description: 'The build configuration (Build or Release)'
description: 'The build configuration (Debug or Release)'
default: Release
required: true
version:
description: 'The package version'
required: true
ghapikey:
description: 'The Git Hub Api Key for package feed'
description: 'The GitHub API Key for package feed'
required: true
nugetapikey:
description: 'The Nuget Api Key for package feed'
description: 'The NuGet API Key for package feed'
required: true
registry:
description: 'The package registry (github or nuget)'
description: 'The package registry (GitHub or NuGet)'
default: github
required: true
outputs:
coverage-line:
description: 'Line coverage percentage'
value: ${{ steps.extract-coverage.outputs.line }}
coverage-branch:
description: 'Branch coverage percentage'
value: ${{ steps.extract-coverage.outputs.branch }}
runs:
using: "composite"
steps:
Expand All @@ -41,51 +34,34 @@ runs:
run: dotnet build ./src/${{ inputs.project }}/${{ inputs.project }}.csproj -c ${{ inputs.config }} --verbosity minimal
shell: bash

# Test
# Test and collect coverage
- name: Test
if: ${{ inputs.skiptest == 'false' }}
run: dotnet test ./test/${{ inputs.project }}.Tests/${{ inputs.project }}.Tests.csproj -c ${{ inputs.config }} --verbosity minimal --collect:"XPlat Code Coverage" --results-directory ./coverage
shell: bash

# Generate coverage report
- name: Coverage Report
if: ${{ inputs.skiptest == 'false' }}
run: reportgenerator -reports:./coverage/**/coverage.cobertura.xml -targetdir:./coverage/report -reporttypes:JsonSummary
shell: bash

# Extract line and branch coverage percentages
- name: Extract Coverage
id: extract-coverage
if: ${{ inputs.skiptest == 'false' }}
run: |
LINE=$(cat ./coverage/report/Summary.json | python3 -c "import sys,json; d=json.load(sys.stdin); print(round(d['summary']['linecoverage']))")
BRANCH=$(cat ./coverage/report/Summary.json | python3 -c "import sys,json; d=json.load(sys.stdin); print(round(d['summary']['branchcoverage']))")
echo "line=$LINE" >> $GITHUB_OUTPUT
echo "branch=$BRANCH" >> $GITHUB_OUTPUT
shell: bash

# Pack
- name: Pack
run: |
echo branch=${{ github.ref_name }} version=${{ inputs.version }}
dotnet pack ./src/${{ inputs.project }}/${{ inputs.project }}.csproj -p:PackageVersion=${{ inputs.version }} --no-build -c ${{ inputs.config }} -o _packages -v minimal
shell: bash

# GITHUB FEED
- name: Github Push
# GitHub Feed
- name: GitHub Push
if: ${{ inputs.registry == 'github' }}
run: dotnet nuget push _packages/${{ inputs.project }}.${{ inputs.version }}.nupkg --skip-duplicate --no-symbols --api-key ${{ inputs.ghapikey }} --source github
shell: bash

# NUGET FEED
# First push to github (for package cadence, Nuget requires package verification)
- name: Github Push
# NuGet Feed
# First push to GitHub (for package cadence, NuGet requires package verification)
- name: GitHub Push
if: ${{ inputs.registry == 'nuget' }}
run: dotnet nuget push _packages/${{ inputs.project }}.${{ inputs.version }}.nupkg --skip-duplicate --no-symbols --api-key ${{ inputs.ghapikey }} --source github
shell: bash

# Second push to Nuget (for package cadence, Github will be used and in production Nuget after package verification)
- name: Nuget Push
# Second push to NuGet (for package cadence, GitHub will be used and in production NuGet after package verification)
- name: NuGet Push
if: ${{ inputs.registry == 'nuget' }}
run: dotnet nuget push _packages/${{ inputs.project }}.${{ inputs.version }}.nupkg --skip-duplicate --no-symbols --api-key ${{ inputs.nugetapikey }} --source nuget.org
shell: bash
4 changes: 4 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copilot Instructions

## Project Guidelines
- Never add section separator comments (e.g., // ── SectionName ──────) in code.
10 changes: 10 additions & 0 deletions .github/workflows/build-test-cross.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v6

# Read env
- name: Read Env
run: cat .github/package_version.env >> $GITHUB_ENV
continue-on-error: false

# Setup .Net with global.json
- name: Setup .NET
uses: actions/setup-dotnet@v5
Expand Down Expand Up @@ -99,6 +104,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v6

# Read env
- name: Read Env
run: cat .github/package_version.env >> $GITHUB_ENV
continue-on-error: false

# Setup .Net with global.json
- name: Setup .NET
uses: actions/setup-dotnet@v5
Expand Down
33 changes: 22 additions & 11 deletions .github/workflows/build-test-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
name: build-test-push

# 1 release at a time
concurrency: shared
concurrency: ${{ github.event.repository.name }}

# CICD. The rc and release are a manual trigger
on:
Expand Down Expand Up @@ -116,14 +116,13 @@ jobs:
run: dotnet tool install --global dotnet-reportgenerator-globaltool
continue-on-error: false

# Setup local
# OneImlx.Shared is the first package so we use the local project reference to avoid cyclic Nuget dependency resolution.
# OneImlx.Shared is the first package so we use the cross project reference to avoid cyclic Nuget dependency resolution.
# See OneImlx.Shared.Test.csproj for more info.
- name: Force local
- name: Force cross
run: echo "PI_CI_REFERENCE=cross" >> $GITHUB_ENV
continue-on-error: false

# Publish OneImlx.Shared
# Publish OneImlx.Shared (deposits coverage into ./coverage)
- name: OneImlx.Shared
id: oneimlx-shared
uses: ./.github/actions/push-package
Expand All @@ -135,7 +134,7 @@ jobs:
nugetapikey: ${{ env.PI_NUGET_PAT_ENV }}
registry: ${{ env.PI_PUBLISH_REGISTRY }}

# Setup package
# Switch to package references for downstream packages
- name: Force package
run: echo "PI_CI_REFERENCE=package" >> $GITHUB_ENV
continue-on-error: false
Expand All @@ -146,13 +145,25 @@ jobs:
uses: ./.github/actions/push-package
with:
project: OneImlx.Test
skiptest: 'true'
config: Release
version: ${{ env.PI_CI_PACKAGE_VERSION }}
ghapikey: ${{ env.PI_GITHUB_PAT_ENV }}
nugetapikey: ${{ env.PI_NUGET_PAT_ENV }}
registry: ${{ env.PI_PUBLISH_REGISTRY }}

# Aggregate coverage report across all packages deposited into ./coverage
- name: Coverage Report
run: reportgenerator -reports:./coverage/**/coverage.cobertura.xml -targetdir:./coverage/report -reporttypes:JsonSummary
shell: bash

# Extract aggregate line and branch coverage
- name: Extract Coverage
id: coverage
run: |
echo "line=$(jq '.summary.linecoverage' ./coverage/report/Summary.json | xargs printf '%.0f')" >> $GITHUB_OUTPUT
echo "branch=$(jq '.summary.branchcoverage' ./coverage/report/Summary.json | xargs printf '%.0f')" >> $GITHUB_OUTPUT
shell: bash

# Update line coverage badge in Gist
- name: Line Coverage Badge
uses: Schneegans/dynamic-badges-action@v1.8.0
Expand All @@ -161,8 +172,8 @@ jobs:
gistID: ${{ secrets.PI_GITHUB_GIST_ID }}
filename: coverage_${{ github.event.repository.name }}_line.json
label: coverage-line
message: ${{ steps.oneimlx-shared.outputs.coverage-line }}%
valColorRange: ${{ steps.oneimlx-shared.outputs.coverage-line }}
message: ${{ steps.coverage.outputs.line }}%
valColorRange: ${{ steps.coverage.outputs.line }}
maxColorRange: 100
minColorRange: 0

Expand All @@ -174,7 +185,7 @@ jobs:
gistID: ${{ secrets.PI_GITHUB_GIST_ID }}
filename: coverage_${{ github.event.repository.name }}_branch.json
label: coverage-branch
message: ${{ steps.oneimlx-shared.outputs.coverage-branch }}%
valColorRange: ${{ steps.oneimlx-shared.outputs.coverage-branch }}
message: ${{ steps.coverage.outputs.branch }}%
valColorRange: ${{ steps.coverage.outputs.branch }}
maxColorRange: 100
minColorRange: 0
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ This repository contains the shared components for our cross-platform frameworks
[![NuGet](https://img.shields.io/nuget/vpre/OneImlx.Test?label=OneImlx.Test)](https://www.nuget.org/packages/OneImlx.Test)

## Status
[![Cross-Platform Build and Test](https://github.com/perpetualintelligence/shared/actions/workflows/build-test-cross.yml/badge.svg)](https://github.com/perpetualintelligence/shared/actions/workflows/build-test-cross-manual.yml)
[![Push Build and Test](https://github.com/perpetualintelligence/shared/actions/workflows/build-test-push.yml/badge.svg)](https://github.com/perpetualintelligence/shared/actions/workflows/build-test-push.yml)
[![build-test-cross](https://github.com/perpetualintelligence/shared/actions/workflows/build-test-cross.yml/badge.svg)](https://github.com/perpetualintelligence/shared/actions/workflows/build-test-cross.yml)
[![build-test-push](https://github.com/perpetualintelligence/shared/actions/workflows/build-test-push.yml/badge.svg)](https://github.com/perpetualintelligence/shared/actions/workflows/build-test-push.yml)

![coverage-line](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/perpetualintelligencegit/141903832ee52f1e9e7913300a92d507/raw/coverage_shared_line.json)
![coverage-branch](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/perpetualintelligencegit/141903832ee52f1e9e7913300a92d507/raw/coverage_shared_branch.json)
Expand Down
1 change: 1 addition & 0 deletions Shared.All.Solution.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
</Folder>
<Folder Name="/test/">
<Project Path="test/OneImlx.Shared.Tests/OneImlx.Shared.Tests.csproj" />
<Project Path="test/OneImlx.Test.Tests/OneImlx.Test.Tests.csproj" Id="e9609456-eb72-4575-84bf-18dc91bd4883" />
</Folder>
</Solution>
14 changes: 7 additions & 7 deletions build/README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
# Build

## Local Machine
Follow the steps to set up the `pi-cli` repository on your local development machine.
Follow the steps to set up the `shared` repository on your local development machine.

1. Download and install [Visual Studio 2022](https://visualstudio.microsoft.com/vs/)
2. Clone the [protocols](https://github.com/perpetualintelligence/protocols) GitHub repo
1. Download and install [Visual Studio 2026](https://visualstudio.microsoft.com/vs/)
2. Clone the [shared](https://github.com/perpetualintelligence/shared) GitHub repo
3. Set PI_CI_REFERENCE environment variable to `cross`

## CICD
This workflow folder contains the build and deployment pipelines for generating and publishing [Nuget](https://www.nuget.org/profiles/perpetualintelligencellc) and [GitHub](https://github.com/orgs/perpetualintelligence/packages?repo_name=data) packages.

- *build-test-cross-manual*: The manual action that builds and tests the code changes on Windows, Linux and macOS.
- *build-test-publish*: The automated action that publishes the packages to [Nuget](https://www.nuget.org/profiles/perpetualintelligencellc) and [GitHub](https://github.com/orgs/perpetualintelligence/packages?repo_name=data), see [releases](https://github.com/perpetualintelligence/cli/releases)
- *build-test-cross*: The manual action that builds and tests the code changes on Windows, Linux and macOS.
- *build-test-push*: The automated action that publishes the packages to [Nuget](https://www.nuget.org/profiles/perpetualintelligencellc) and [GitHub](https://github.com/orgs/perpetualintelligence/packages?repo_name=data), see [releases](https://github.com/perpetualintelligence/shared/releases)
- *delete-packages*: The automated action cleans the packages every week and keeps the latest working version. For stable versions, refer to [Nuget](https://www.nuget.org/profiles/perpetualintelligencellc) packages.

> ***Note: The `build-test-publish` release to Nuget pipeline triggers a deployment approval.***
> ***Note: The `build-test-push` release to Nuget pipeline triggers a deployment approval.***

## Versioning
All packages follow [sematic](https://semver.org/) versioning schemes. The env file *package_version.env* defines the package versions.

## Project Dependencies
The *PI_CI_REFERENCE* environment variable defines how *.csproj* references the dependencies for CI and local development. It supportes the following values:
The *PI_CI_REFERENCE* environment variable defines how *.csproj* references the dependencies for CI and local development. It supports the following values:
- *local*: Project references for local development within the same repo
- *cross*: Project references for local development across repos
- *package*: Package references for CI/CD and deployment
Expand Down
26 changes: 26 additions & 0 deletions src/OneImlx.Test/Extentions/ObjectExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved.
// For license, terms, and data policies, go to:
// https://terms.perpetualintelligence.com/articles/intro.html

using FluentAssertions;

namespace OneImlx.Test.Extentions
{
/// <summary>
/// <see cref="object"/> extension methods for testing.
/// </summary>
public static class ObjectExtensions
{
/// <summary>
/// Asserts that the source object is not null and returns the source object for chaining.
/// </summary>
/// <typeparam name="T">The type of the source object.</typeparam>
/// <param name="source">The source object to check for null.</param>
/// <returns>The source object if it is not null.</returns>
public static T NotNull<T>(this T? source)
{
source.Should().NotBeNull();
return source;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ public static AndConstraint<AssemblyAssertions> HaveTypesInRootNamespace(this As
{
var assembly = assertions.Subject;
var actualNamespace = assembly.GetName().Name ?? throw new InvalidOperationException("Assembly name null");
actualNamespace.Should().Be(rootNamespace, $"Assembly '{assembly.GetName().Name}' should have root namespace '{rootNamespace}'");
if (actualNamespace != rootNamespace)
{
throw new AssertionFailedException($"Assembly '{assembly.GetName().Name}' should have root namespace '{rootNamespace}'.");
}

var types = assembly.GetTypes().Where(e => !IsCompilerGenerated(e));
var invalidTypes = types.Where(e =>
Expand Down
9 changes: 3 additions & 6 deletions test/OneImlx.Shared.Tests/AssemblyTests.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
/*
Copyright (c) 2023 Perpetual Intelligence L.L.C. All Rights Reserved.

For license, terms, and data policies, go to:
https://terms.perpetualintelligence.com/articles/intro.html
*/
// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved.
// For license, terms, and data policies, go to:
// https://terms.perpetualintelligence.com/articles/intro.html

using FluentAssertions;
using OneImlx.Test.FluentAssertions;
Expand Down
42 changes: 42 additions & 0 deletions test/OneImlx.Test.Tests/Extensions/ObjectExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved.
// For license, terms, and data policies, go to:
// https://terms.perpetualintelligence.com/articles/intro.html

using FluentAssertions;
using System;
using Xunit;

namespace OneImlx.Test.Extentions
{
public class ObjectExtensionsTests
{
[Fact]
public void NotNullShouldReturnSourceWhenNotNull()
{
string result = "hello".NotNull();
result.Should().Be("hello");
}

[Fact]
public void NotNullShouldThrowWhenNull()
{
string? value = null;
FluentActions.Invoking(() => value.NotNull()).Should().Throw<Exception>();
}

[Fact]
public void NotNullShouldReturnSameReferenceForObject()
{
var obj = new object();
var result = obj.NotNull();
result.Should().BeSameAs(obj);
}

[Fact]
public void NotNullShouldWorkWithValueType()
{
int result = 42.NotNull();
result.Should().Be(42);
}
}
}
Loading
Loading