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
36 changes: 36 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Git
.git
.gitignore
.gitattributes

# Build outputs
bin/
obj/
*.dll
*.exe
*.pdb

# IDE
.vs/
.vscode/
*.user
*.sln.iml
.idea/

# CI/CD
.github/
Jenkinsfile

# Package managers
*.lock

# OS
.DS_Store
Thumbs.db

# Documentation
*.md
LICENSE

# Test results
TestResults/
109 changes: 109 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Copilot Instructions for MyNumber.NET

## Project Overview
MyNumber.NET is a .NET 8.0 library for validating and generating Japanese My Number (Social Security/Tax ID). The solution contains four projects:
- **MyNumberNET**: Core validation/generation library
- **MyNumberNET_ApiServer**: ASP.NET Core REST API wrapper
- **MyNumberNET_CLI**: Command-line interface with NLog logging
- **MyNumberNET_Test**: xUnit unit tests

## Architecture & Key Patterns

### Core Validation Logic
The `MyNumber` static class in [MyNumberNET/MyNumber.cs](MyNumberNET/MyNumber.cs) implements the My Number algorithm:
- **VerifyNumber()**: Validates 12-digit arrays using check digit algorithm (positions 1-6 use weight 2-7, positions 7-11 use weight 1-6)
- **CalculateCheckDigits()**: Computes 12th digit from first 11 (modulo 11 formula: if sum%11 ≤ 1, checkdigit=0; else checkdigit=11-sum%11)
- Throws `MyNumberMalformedException` for invalid input (null, wrong length, non-digits)

### MyNumberValue - Type-Safe Wrapper
`MyNumberValue` readonly struct (in [MyNumberNET/MyNumber.cs](MyNumberNET/MyNumber.cs)) wraps validated numbers:
- Immutable and always valid on construction
- Multiple constructors: from int[], from 12 individual digits, or `FromFirstElevenDigits()` (auto-calculates check digit)
- `TryParse(string)` handles separator formats (spaces, hyphens, underscores)
- `ToString(format)` supports: "N" (plain), "S" (spaced), "H" (hyphenated), "G" (grouped)

### API Layer Pattern
[MyNumberNET_ApiServer/Controllers/MyNumberController.cs](MyNumberNET_ApiServer/Controllers/MyNumberController.cs) duplicates input validation before calling business logic - this is intentional to prevent malformed requests from reaching the library.

## Critical Workflows

### Building & Testing
```powershell
dotnet build # Build solution
dotnet test MyNumberNET_Test # Run tests
dotnet run --project MyNumberNET_ApiServer # Start API on port 5000+
dotnet run --project MyNumberNET_CLI -- generate 10 # CLI with args
```

### CLI Commands
Located in [MyNumberNET_CLI/Program.cs](MyNumberNET_CLI/Program.cs) switch statement:
- `generate [count]` - Random valid My Numbers
- `check [number]` - Validate single number
- `complete [11digits]` - Calculate check digit
- `rangen [min] [max]` - Numerical range generation
- `ranges [min] [max]` - Sequential range generation

### Version Management
All versions defined in [Directory.Build.props](Directory.Build.props):
- Update `MyNumberNETVersion`, `MyNumberNETCliVersion`, `MyNumberNETTestVersion` for releases
- Target framework: .NET 8.0 (specified in [global.json](global.json))

## Integration Points & Dependencies

### Exception Hierarchy
- Base: `MyNumber.MyNumberException(message)`
- Specific: `MyNumber.MyNumberMalformedException(message)` - thrown on validation failure
- API returns `BadRequest` + error message on exceptions; never propagates exceptions

### NLog Configuration
CLI project uses [MyNumberNET_CLI/nlog.config](MyNumberNET_CLI/nlog.config) for logging. Logger accessed via `LogManager.GetCurrentClassLogger()`.

### No External Dependencies
Core library only uses System.* namespaces - no NuGet dependencies. Keep it minimal.

## Code Patterns

### Error Handling
1. Validate inputs early (null, length, digit range)
2. Throw `MyNumberMalformedException` with descriptive messages
3. In API: catch exceptions and return `BadRequest(ex.Message)`

### Digit Array Conventions
- 12-digit arrays: indices 0-10 are data, 11 is check digit
- Use `Truncate()` private method to extract first 11 digits
- Always clone arrays when storing (immutability)

### Testing Strategy
Test files: [MyNumberNET_Test/MyNumberValueTests.cs](MyNumberNET_Test/MyNumberValueTests.cs), [MyNumberNET_Test/MyNumberControllerTests.cs](MyNumberNET_Test/MyNumberControllerTests.cs)
- Test both valid and malformed inputs
- Verify exception messages and types
- Test all format options for ToString()## Containerization & Deployment

### Docker
[MyNumberNET_ApiServer/Dockerfile](MyNumberNET_ApiServer/Dockerfile) uses multi-stage build:
1. **Build stage**: Restores, builds, and publishes using .NET 8.0 SDK
2. **Runtime stage**: Runs on .NET 8.0 ASP.NET Core runtime (smaller image)
3. **Port**: Exposes 5000 (configurable via `ASPNETCORE_URLS` env var)
4. **Health check**: Built-in liveness probe every 30 seconds

Local Docker usage:
```bash
docker build -f MyNumberNET_ApiServer/Dockerfile -t mynumberapi:latest .
docker run -p 5000:5000 mynumberapi:latest
```

### GitHub Container Registry
Images pushed to `ghcr.io/hsaito/mynumberapi` on main branch pushes and tags (v* releases).
Tags: branch name, semantic version (v1.0.0), git SHA.

## CI/CD

### GitHub Actions Workflows
- **[.github/workflows/dotnet-ci.yml](.github/workflows/dotnet-ci.yml)**: Cross-platform build/test (Windows, Linux, macOS)
- **[.github/workflows/docker-build.yml](.github/workflows/docker-build.yml)**: Docker image build and push to ghcr.io
- Automatically tags releases (semantic versioning supported)
- PR builds test image without pushing
- Uses Buildx for efficient multi-stage builds

### Legacy CI
[Jenkinsfile](Jenkinsfile) provides additional build/test/package orchestration. Deterministic builds enabled via [Directory.Build.props](Directory.Build.props) for reproducible artifacts.
75 changes: 75 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Docker Build & Push

permissions:
contents: read
packages: write

on:
push:
branches: [ "main" ]
tags: [ "v*" ]
pull_request:
branches: [ "main" ]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/mynumberapi

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./MyNumberNET_ApiServer/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

test-docker-image:
needs: build-and-push
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./MyNumberNET_ApiServer/Dockerfile
push: false
cache-from: type=gha
30 changes: 0 additions & 30 deletions Jenkinsfile

This file was deleted.

43 changes: 43 additions & 0 deletions MyNumberNET_ApiServer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# Copy project files
COPY ["MyNumberNET/MyNumberNET.csproj", "MyNumberNET/"]
COPY ["MyNumberNET_ApiServer/MyNumberNET_ApiServer.csproj", "MyNumberNET_ApiServer/"]
COPY ["Directory.Build.props", "."]
COPY ["global.json", "."]

# Restore dependencies
RUN dotnet restore "MyNumberNET_ApiServer/MyNumberNET_ApiServer.csproj"

# Copy source files
COPY ["MyNumberNET/", "MyNumberNET/"]
COPY ["MyNumberNET_ApiServer/", "MyNumberNET_ApiServer/"]

# Build application
RUN dotnet build "MyNumberNET_ApiServer/MyNumberNET_ApiServer.csproj" -c Release --no-restore

# Publish application
RUN dotnet publish "MyNumberNET_ApiServer/MyNumberNET_ApiServer.csproj" -c Release -o /app/publish --no-build

# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app

# Copy published application from build stage
COPY --from=build /app/publish .

# Set environment variables for ASP.NET Core
ENV ASPNETCORE_URLS=http://+:5000
ENV ASPNETCORE_ENVIRONMENT=Production

# Expose port
EXPOSE 5000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD dotnet /app/MyNumberNET_ApiServer.dll || exit 1

# Start application
ENTRYPOINT ["dotnet", "MyNumberNET_ApiServer.dll"]
Loading