# yaml-language-server: $schema=https://json.schemastore.org/github-action.json # permissions: # - packages: write # Required for publishing to GitHub Packages # - contents: read # Required for checking out repository --- name: C# Publish description: 'Publishes a C# project to GitHub Packages.' author: 'Ismo Vuorinen' branding: icon: package color: blue inputs: dotnet-version: description: 'Version of .NET SDK to use.' required: false namespace: description: 'GitHub namespace for the package.' required: true default: 'ivuorinen' token: description: 'GitHub token with package write permissions' required: false max-retries: description: 'Maximum number of retry attempts for dependency restoration' required: false default: '3' outputs: publish_status: description: 'Overall publish status (success/failure)' value: ${{ steps.set-status.outputs.status }} package_version: description: 'Version of the published package' value: ${{ steps.extract-version.outputs.version }} package_url: description: 'URL of the published package' value: ${{ steps.publish-package.outputs.package_url }} runs: using: composite steps: - name: Mask Secrets shell: sh env: API_KEY: ${{ inputs.token || github.token }} run: | echo "::add-mask::$API_KEY" - name: Checkout Repository uses: actions/checkout@71cf2267d89c5cb81562390fa70a37fa40b1305e # v6-beta with: token: ${{ inputs.token || github.token }} - name: Validate Inputs id: validate uses: ivuorinen/actions/validate-inputs@5cc7373a22402ee8985376bc713f00e09b5b2edb with: action-type: 'csharp-publish' token: ${{ inputs.token }} namespace: ${{ inputs.namespace }} dotnet-version: ${{ inputs.dotnet-version }} - name: Detect .NET SDK Version id: detect-dotnet-version shell: sh env: DEFAULT_VERSION: '7.0' run: | set -eu # Function to validate version format validate_version() { version=$1 case "$version" in [0-9]* | [0-9]*\.[0-9]* | [0-9]*\.[0-9]*\.[0-9]*) return 0 ;; *) return 1 ;; esac } # Function to clean version string clean_version() { printf '%s' "$1" | sed 's/^[vV]//' | tr -d ' \n\r' } detected_version="" # Parse .tool-versions file if [ -f .tool-versions ]; then echo "Checking .tool-versions for dotnet..." >&2 version=$(awk '/^dotnet[[:space:]]/ {gsub(/#.*/, ""); print $2; exit}' .tool-versions 2>/dev/null || echo "") if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found .NET version in .tool-versions: $version" >&2 detected_version="$version" fi fi fi # Parse Dockerfile if [ -z "$detected_version" ] && [ -f Dockerfile ]; then echo "Checking Dockerfile for dotnet..." >&2 version=$(grep -iF "FROM" Dockerfile | grep -F "dotnet:" | head -1 | \ sed -n -E "s/.*dotnet:([0-9]+(\.[0-9]+)*)(-[^:]*)?.*/\1/p" || echo "") if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found .NET version in Dockerfile: $version" >&2 detected_version="$version" fi fi fi # Parse devcontainer.json if [ -z "$detected_version" ] && [ -f .devcontainer/devcontainer.json ]; then echo "Checking devcontainer.json for dotnet..." >&2 if command -v jq >/dev/null 2>&1; then version=$(jq -r '.image // empty' .devcontainer/devcontainer.json 2>/dev/null | sed -n -E "s/.*dotnet:([0-9]+(\.[0-9]+)*)(-[^:]*)?.*/\1/p" || echo "") if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found .NET version in devcontainer: $version" >&2 detected_version="$version" fi fi else echo "jq not found; skipping devcontainer.json parsing" >&2 fi fi # Parse global.json if [ -z "$detected_version" ] && [ -f global.json ]; then echo "Checking global.json..." >&2 if command -v jq >/dev/null 2>&1; then version=$(jq -r '.sdk.version // empty' global.json 2>/dev/null || echo "") if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found .NET version in global.json: $version" >&2 detected_version="$version" fi fi else echo "jq not found; skipping global.json parsing" >&2 fi fi # Use default version if nothing detected if [ -z "$detected_version" ]; then detected_version="$DEFAULT_VERSION" echo "Using default .NET version: $detected_version" >&2 fi # Set output printf 'detected-version=%s\n' "$detected_version" >> "$GITHUB_OUTPUT" echo "Final detected .NET version: $detected_version" >&2 - name: Setup .NET SDK uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1 with: dotnet-version: ${{ inputs.dotnet-version || steps.detect-dotnet-version.outputs.detected-version }} cache: true cache-dependency-path: '**/packages.lock.json' - name: Restore Dependencies uses: step-security/retry@e1d59ce1f574b32f0915e3a8df055cfe9f99be5d # v3.0.4 with: timeout_minutes: 10 max_attempts: ${{ inputs.max-retries }} command: | echo "Restoring .NET dependencies..." dotnet restore --verbosity normal - name: Build Solution shell: sh run: | set -eu dotnet build --configuration Release --no-restore - name: Pack Solution shell: sh run: | set -eu dotnet pack --configuration Release --no-build --no-restore --output ./artifacts - name: Extract Package Version id: extract-version shell: sh run: | set -eu # Find the newest .nupkg file by modification time and extract version PACKAGE_FILE=$(find ./artifacts -name "*.nupkg" -type f -printf '%T@ %p\n' | sort -rn | head -n 1 | cut -d' ' -f2-) if [ -n "$PACKAGE_FILE" ]; then # Extract version from filename (assumes standard naming: PackageName.Version.nupkg) VERSION=$(basename "$PACKAGE_FILE" .nupkg | sed 's/.*\.\([0-9]\+\.[0-9]\+\.[0-9]\+.*\)$/\1/') printf '%s\n' "version=$VERSION" >> "$GITHUB_OUTPUT" printf '%s\n' "package_file=$PACKAGE_FILE" >> "$GITHUB_OUTPUT" else printf '%s\n' "version=unknown" >> "$GITHUB_OUTPUT" printf '%s\n' "package_file=" >> "$GITHUB_OUTPUT" fi - name: Publish Package id: publish-package shell: sh env: API_KEY: ${{ inputs.token || github.token }} NAMESPACE: ${{ inputs.namespace }} run: | set -eu PACKAGE_URL="https://github.com/$NAMESPACE/packages/nuget" printf '%s\n' "package_url=$PACKAGE_URL" >> "$GITHUB_OUTPUT" # First attempt if ! dotnet nuget push ./artifacts/*.nupkg \ --api-key "$API_KEY" \ --source "https://nuget.pkg.github.com/$NAMESPACE/index.json" \ --skip-duplicate \ --no-symbols; then echo "::warning::First publish attempt failed, retrying after 5 seconds..." sleep 5 dotnet nuget push ./artifacts/*.nupkg \ --api-key "$API_KEY" \ --source "https://nuget.pkg.github.com/$NAMESPACE/index.json" \ --skip-duplicate \ --no-symbols fi - name: Set publish status output if: always() id: set-status shell: sh env: PUBLISH_STATUS: ${{ steps.publish-package.outcome == 'success' && 'success' || 'failure' }} run: |- printf '%s\n' "status=$PUBLISH_STATUS" >> "$GITHUB_OUTPUT"