Skip to content
Merged
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
57 changes: 57 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: CI

on:
pull_request:
branches: [ master ]

# PR validation only reads the repo.
permissions:
contents: read

# Cancel superseded runs for the same PR.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages

jobs:
build-test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: src # global.json (Microsoft.Testing.Platform opt-in) lives here
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # MinVer needs full history + tags to compute the version

- name: Setup .NET (8 + 10)
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
10.0.x
# Build uses the SDK pinned by src/global.json (10.0.100); the 8.0 runtime
# installed here is what actually runs the net8.0 test target.

- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('src/Directory.Packages.props') }}
restore-keys: nuget-${{ runner.os }}-

- name: Restore
run: dotnet restore ExistAll.SimpleConfig.slnx

- name: Build (Release, deterministic)
run: dotnet build ExistAll.SimpleConfig.slnx -c Release --no-restore -p:ContinuousIntegrationBuild=true

- name: Test (net8.0 + net10.0)
run: dotnet test ExistAll.SimpleConfig.slnx -c Release --no-build
21 changes: 0 additions & 21 deletions .github/workflows/dotnet.yml

This file was deleted.

250 changes: 250 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
name: Release

on:
push:
branches: [ master ] # every merge -> fresh alpha prerelease
workflow_dispatch:
inputs:
channel:
description: Release channel
type: choice
options: [ beta, rc, stable ]
default: beta
bump:
description: SemVer part to bump (relative to the latest stable tag; ignored on the first release)
type: choice
options: [ patch, minor, major ]
default: minor
dry_run:
description: Build/pack/upload only - do NOT tag, push to NuGet, or create a Release
type: boolean
default: false

permissions:
contents: read

# Serialize releases; never cancel an in-flight publish.
concurrency:
group: release
cancel-in-progress: false

env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
ARTIFACTS: ${{ github.workspace }}/artifacts
NUGET_SOURCE: https://api.nuget.org/v3/index.json
TAG_PREFIX: v # deliberately NOT "version-": those tags are the dead ExistAll.SimpleConfig line
BASELINE_VERSION: 2.0.0 # first release on the v* line (modernized, breaking vs published 1.0.0)

jobs:
# ---------------------------------------------------------------------------
# A) Automatic ALPHA prerelease on every push to master.
# MinVer stamps a height-based prerelease, e.g. 2.0.0-alpha.0.7.
# ---------------------------------------------------------------------------
prerelease:
if: github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
contents: read # no tag/release here
id-token: write # OIDC for NuGet trusted publishing
defaults:
run:
working-directory: src
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
10.0.x

- uses: actions/cache@v4
with:
path: ${{ github.workspace }}/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('src/Directory.Packages.props') }}
restore-keys: nuget-${{ runner.os }}-

- name: Restore
run: dotnet restore ExistAll.SimpleConfig.slnx

- name: Build
run: dotnet build ExistAll.SimpleConfig.slnx -c Release --no-restore -p:ContinuousIntegrationBuild=true

- name: Test
run: dotnet test ExistAll.SimpleConfig.slnx -c Release --no-build

- name: Pack (MinVer -> height-based alpha)
run: dotnet pack ExistAll.SimpleConfig.slnx -c Release --no-build -p:ContinuousIntegrationBuild=true -o "$ARTIFACTS"

- name: List packages
run: ls -1 "$ARTIFACTS"

- name: Upload packages
uses: actions/upload-artifact@v4
with:
name: nuget-prerelease
path: |
${{ env.ARTIFACTS }}/*.nupkg
${{ env.ARTIFACTS }}/*.snupkg

- name: NuGet login (OIDC -> short-lived API key)
uses: NuGet/login@v1
id: login
with:
user: ${{ secrets.NUGET_USER }}

# dotnet nuget push globs natively; each .nupkg also pushes its adjacent .snupkg.
- name: Push to NuGet.org
run: dotnet nuget push "$ARTIFACTS/*.nupkg" --api-key "${{ steps.login.outputs.NUGET_API_KEY }}" --source "$NUGET_SOURCE" --skip-duplicate

# ---------------------------------------------------------------------------
# B) Manual, curated RELEASE (beta.N / rc.N / stable) via workflow_dispatch.
# Tag is created LOCALLY (MinVer reads it), pushed only AFTER a successful
# NuGet push, then a GitHub Release is created.
# ---------------------------------------------------------------------------
release:
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: write # push tag + create GitHub Release
id-token: write # OIDC for NuGet trusted publishing
environment:
name: nuget.org # add a Required reviewer here to gate stable publishes
defaults:
run:
working-directory: src
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # all tags -> correct next-version computation

- uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
10.0.x

- uses: actions/cache@v4
with:
path: ${{ github.workspace }}/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('src/Directory.Packages.props') }}
restore-keys: nuget-${{ runner.os }}-

- name: Compute next version tag
id: ver
env:
BUMP: ${{ inputs.bump }}
CHANNEL: ${{ inputs.channel }}
run: |
set -euo pipefail
PREFIX="$TAG_PREFIX"
git fetch --tags --force --quiet

# Highest STABLE tag under the "v" prefix: vMAJOR.MINOR.PATCH (no pre-release).
# The "v[0-9]" glob excludes the legacy "version-*" tags by construction.
latest_stable="$(
git tag --list "${PREFIX}[0-9]*" \
| grep -E "^${PREFIX}[0-9]+\.[0-9]+\.[0-9]+$" \
| sed "s/^${PREFIX}//" \
| sort -V | tail -n1 || true
)"

if [ -z "$latest_stable" ]; then
# First release on the v* line -> the baseline itself (bump ignored).
TARGET="$BASELINE_VERSION"
echo "No v* stable tag yet -> first release targets baseline $TARGET (bump '$BUMP' ignored)."
else
IFS='.' read -r MA MI PA <<< "$latest_stable"
case "$BUMP" in
major) MA=$((MA+1)); MI=0; PA=0 ;;
minor) MI=$((MI+1)); PA=0 ;;
patch) PA=$((PA+1)) ;;
*) echo "::error::invalid bump '$BUMP'"; exit 1 ;;
esac
TARGET="${MA}.${MI}.${PA}"
echo "Latest stable $latest_stable + $BUMP -> $TARGET"
fi

if [ "$CHANNEL" = "stable" ]; then
NEW_TAG="${PREFIX}${TARGET}"
if git rev-parse -q --verify "refs/tags/${NEW_TAG}" >/dev/null; then
echo "::error::tag ${NEW_TAG} already exists"; exit 1
fi
PRERELEASE=false
else
# Continue the -beta.N / -rc.N series for this target base.
last_n="$(
git tag --list "${PREFIX}${TARGET}-${CHANNEL}.*" \
| sed -E "s/^${PREFIX}${TARGET}-${CHANNEL}\.([0-9]+)$/\1/" \
| grep -E '^[0-9]+$' | sort -n | tail -n1 || true
)"
next_n=$(( ${last_n:-0} + 1 ))
NEW_TAG="${PREFIX}${TARGET}-${CHANNEL}.${next_n}"
PRERELEASE=true
fi

echo "Computed tag: $NEW_TAG (prerelease=$PRERELEASE)"
{
echo "tag=$NEW_TAG"
echo "prerelease=$PRERELEASE"
} >> "$GITHUB_OUTPUT"

- name: Create local tag (MinVer reads it at pack time)
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "${{ steps.ver.outputs.tag }}" -m "Release ${{ steps.ver.outputs.tag }}"

- name: Restore
run: dotnet restore ExistAll.SimpleConfig.slnx

- name: Build
run: dotnet build ExistAll.SimpleConfig.slnx -c Release --no-restore -p:ContinuousIntegrationBuild=true

- name: Test
run: dotnet test ExistAll.SimpleConfig.slnx -c Release --no-build

- name: Pack (MinVer -> exact tagged version)
run: dotnet pack ExistAll.SimpleConfig.slnx -c Release --no-build -p:ContinuousIntegrationBuild=true -o "$ARTIFACTS"

- name: Upload packages
uses: actions/upload-artifact@v4
with:
name: nuget-${{ steps.ver.outputs.tag }}
path: |
${{ env.ARTIFACTS }}/*.nupkg
${{ env.ARTIFACTS }}/*.snupkg

- name: NuGet login (OIDC -> short-lived API key)
if: ${{ !inputs.dry_run }}
uses: NuGet/login@v1
id: login
with:
user: ${{ secrets.NUGET_USER }}

# dotnet nuget push globs natively; each .nupkg also pushes its adjacent .snupkg.
- name: Push to NuGet.org
if: ${{ !inputs.dry_run }}
run: dotnet nuget push "$ARTIFACTS/*.nupkg" --api-key "${{ steps.login.outputs.NUGET_API_KEY }}" --source "$NUGET_SOURCE" --skip-duplicate

- name: Push git tag (only after a successful publish)
if: ${{ !inputs.dry_run }}
run: git push origin "${{ steps.ver.outputs.tag }}"

- name: Create GitHub Release
if: ${{ !inputs.dry_run }}
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ steps.ver.outputs.tag }}
name: ${{ steps.ver.outputs.tag }}
prerelease: ${{ steps.ver.outputs.prerelease }}
generate_release_notes: true
files: |
${{ env.ARTIFACTS }}/*.nupkg
${{ env.ARTIFACTS }}/*.snupkg
Binary file added icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 38 additions & 6 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,52 @@
<PropertyGroup>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<!-- LangVersion intentionally unset: each target framework uses its default
(net8.0 -> C# 12, net10.0 -> C# 14), which is reproducible and idiomatic. -->
</PropertyGroup>

<!-- Shared NuGet package metadata (formerly version.props) -->
<!-- Shared NuGet package metadata (formerly version.props).
Version is intentionally omitted: MinVer derives it from git tags (see below). -->
<PropertyGroup>
<Version>1.0.0</Version>
<Authors>Guy Ludvig</Authors>
<Company>ExistsForAll</Company>
<Description>SimpleSettings allow applications to use configurations values from any data store in the type of interfaces, thus decouples the frameworks from your appliaction and allowing application to inject interfaces and nothing else. This framework should be used instead of .Net Core IOptions&lt;T&gt;.</Description>
<PackageProjectUrl>https://github.com/existall/SimpleConfig</PackageProjectUrl>
<RepositoryUrl>https://github.com/existall/SimpleConfig</RepositoryUrl>
<RepositoryType>GIT</RepositoryType>
<PackageProjectUrl>https://github.com/existall/SimpleSettings</PackageProjectUrl>
<RepositoryUrl>https://github.com/existall/SimpleSettings</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageTags>Configuration, Settings, Ioc, DI, ExistsForAll, Dependency injection, Options, SimpleConfig</PackageTags>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>

<!-- Pack the shared README + icon into every package.
Paths are relative to this file (src/), so ../ resolves to the repo root. -->
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)..\README.md" Pack="true" PackagePath="\" Visible="false" />
<None Include="$(MSBuildThisFileDirectory)..\icon.png" Pack="true" PackagePath="\" Visible="false" />
</ItemGroup>

<!-- Versioning: MinVer, git-tag driven, tag prefix "v".
MinVerMinimumMajorMinor bridges to the modernized 2.x line: until the first v* tag exists,
master builds are stamped 2.0.0-alpha.0.<height> (above the published 1.0.0, below 2.0.0). -->
<PropertyGroup>
<MinVerTagPrefix>v</MinVerTagPrefix>
<MinVerMinimumMajorMinor>2.0</MinVerMinimumMajorMinor>
</PropertyGroup>

<!-- Reproducible, debuggable packages. SourceLink is built into the SDK (>= .NET 8).
ContinuousIntegrationBuild is passed on the CLI in CI (-p:ContinuousIntegrationBuild=true). -->
<PropertyGroup>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<Deterministic>true</Deterministic>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MinVer" PrivateAssets="all" />
</ItemGroup>

</Project>
5 changes: 5 additions & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
</ItemGroup>

<ItemGroup>
<!-- Build / versioning -->
<PackageVersion Include="MinVer" Version="7.0.0" />
</ItemGroup>

</Project>
Loading