Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d78d8af
👷 modernize build system to .slnx and centralized props
aicia-bot Jul 31, 2026
558759a
⚙️ migrate to .net 10 minimal hosting
aicia-bot Jul 31, 2026
90a28b0
🏗️ add configuration infrastructure
aicia-bot Jul 31, 2026
fe7fac4
🏗️ add hosting infrastructure
aicia-bot Jul 31, 2026
a5e1055
✅ add comprehensive unit and functional tests
aicia-bot Jul 31, 2026
fa9d009
👷 add github ci workflows
aicia-bot Jul 31, 2026
c0fea68
💬 add repository docs and governance
aicia-bot Jul 31, 2026
8da58a6
💬 add changelog history from 1.0.0 to 2.0.0
aicia-bot Jul 31, 2026
24267b0
✨ add case-insensitive path lookup via provider decorator
aicia-bot Aug 4, 2026
be9e56f
💬 update changelog and readme for case-insensitive path support
aicia-bot Aug 4, 2026
75734c5
👷 add multi-os test matrix to ci pipeline
aicia-bot Aug 4, 2026
fffbbae
🔧 add test environment configuration
aicia-bot Aug 4, 2026
7d50725
✅ modernize functional tests to xunit v3 patterns
aicia-bot Aug 9, 2026
ea03809
⚙️ refactor app bootstrapping to MinimalWebProgram pattern
aicia-bot Aug 9, 2026
8f8f227
✨ add reusable container build and publish actions
aicia-bot Aug 9, 2026
23bdfed
👷 expand ci pipeline with multi-arch and container publishing
aicia-bot Aug 9, 2026
2270c97
💬 document ci pipeline and container promotion workflow
aicia-bot Aug 9, 2026
11ec051
✨ add docker tagging actions and multi-image save support
aicia-bot Aug 9, 2026
7147fa6
👷 add docker hub promotion workflow with semver and trunkver
aicia-bot Aug 9, 2026
8b9d927
💬 document docker hub promotion and semver tagging strategy
aicia-bot Aug 9, 2026
c832834
🧪 make static content case-insensitive on every os
gimlichael Aug 9, 2026
8ce760e
✨ add symlink resolution and case-insensitive cache prefix matching
aicia-bot Aug 9, 2026
54cd0f7
🧹 remove case-insensitive physical file provider
gimlichael Aug 9, 2026
c051615
🧪housekeeping
gimlichael Aug 9, 2026
9d4db09
📝 update 2.0.0 changelog
gimlichael Aug 9, 2026
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: 14 additions & 22 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
# Docker build context is the repository root. Keep it small and free of build artifacts.
**/bin
**/obj
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
.git
.github
.bot
test
docs
**/*.user
**/.dockerignore
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
**/docker-compose*
**/coverage*
**/*.md
LICENSE.md
92 changes: 92 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true

#### Core EditorConfig Options ####

[*]
charset = utf-8
end_of_line = lf
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[*.{cs,csx,vb,vbx}]
indent_size = 4
tab_width = 4

[*.{xml,csproj,props,targets,config,nuspec,slnx}]
indent_size = 2
tab_width = 2

[*.{json,yml,yaml}]
indent_size = 2
tab_width = 2

[*.md]
trim_trailing_whitespace = false

#### .NET Coding Conventions ####

[*.cs]

# Organize usings
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false

# File-scoped namespaces (required)
csharp_style_namespace_declarations = file_scoped:error

# No top-level statements (required)
csharp_style_prefer_top_level_statements = false:error

# this. and Me. preferences
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion

# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion

# var preferences
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = false:suggestion

# Expression-bodied members
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
csharp_style_expression_bodied_constructors = false:suggestion
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_accessors = true:suggestion
csharp_style_expression_bodied_lambdas = true:suggestion

# Null-checking preferences
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion

# Prefer explicit guard clauses over conditional expressions in returns and assignments
dotnet_style_prefer_conditional_expression_over_return = false
dotnet_style_prefer_conditional_expression_over_assignment = false

# Pattern matching
csharp_style_prefer_pattern_matching = true:suggestion
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion

# Code block preferences
csharp_prefer_braces = true:suggestion

# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true

# Indentation preferences
csharp_indent_case_contents = true
csharp_indent_switch_labels = true

# Space preferences
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
33 changes: 33 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Contributing to Static Content Provider

Thank you for your interest in contributing!

## How to Contribute

1. Fork the repository.
2. Create a feature branch.
3. Commit your changes following the project's commit conventions.
4. Push to your branch and open a pull request against `main`.

## Development Setup

```bash
dotnet restore Codebelt.Cdn.Origin.slnx
dotnet build Codebelt.Cdn.Origin.slnx
dotnet test Codebelt.Cdn.Origin.slnx
```

## Code Standards

- Target `net10.0` and use modern minimal hosting.
- Prefer ASP.NET Core and the BCL over new dependencies. New dependencies require justification (see `AGENTS.md`).
- Use file-scoped namespaces and follow the existing code style (enforced via `.editorconfig`; verify with `dotnet format --severity info --verify-no-changes`).
- All public APIs must have XML documentation comments.
- Behavioural changes to the HTTP contract require functional tests and a `README.md` update.

## Pull Request Guidelines

- Keep pull requests focused on a single concern.
- Maintain 100% line and branch coverage for application-owned decision logic.
- Update `CHANGELOG.md`.
- Ensure all CI checks pass before requesting review.
20 changes: 20 additions & 0 deletions .github/actions/container-attest-provenance/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Attest container provenance
description: Create and registry-publish a GitHub build-provenance attestation for a container image.

inputs:
subject-name:
description: Fully qualified image repository without a tag.
required: true
subject-digest:
description: Immutable image digest in sha256 form.
required: true

runs:
using: composite
steps:
- name: Attest image provenance
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-name: ${{ inputs.subject-name }}
subject-digest: ${{ inputs.subject-digest }}
push-to-registry: true
24 changes: 24 additions & 0 deletions .github/actions/container-attest-sbom/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Attest container SBOM
description: Create and registry-publish a GitHub SBOM attestation for a container image.

inputs:
subject-name:
description: Fully qualified image repository without a tag.
required: true
subject-digest:
description: Immutable image digest in sha256 form.
required: true
sbom-path:
description: SPDX JSON SBOM path.
required: true

runs:
using: composite
steps:
- name: Attest image SBOM
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-name: ${{ inputs.subject-name }}
subject-digest: ${{ inputs.subject-digest }}
sbom-path: ${{ inputs.sbom-path }}
push-to-registry: true
28 changes: 28 additions & 0 deletions .github/actions/container-sbom/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Generate container SBOM
description: Generate an SPDX JSON SBOM for a local Docker image.

inputs:
image:
description: Fully qualified local image reference, including its tag.
required: true
sbom-path:
description: Output path for the SPDX JSON SBOM.
required: true

outputs:
sbom-path:
description: The generated SBOM path.
value: ${{ inputs.sbom-path }}

runs:
using: composite
steps:
- name: Generate SPDX JSON SBOM
uses: anchore/sbom-action@e11c554f704a0b820cbf8c51673f6945e0731532 # v0.20.0
with:
image: ${{ inputs.image }}
format: spdx-json
output-file: ${{ inputs.sbom-path }}
upload-artifact: false
upload-release-assets: false
dependency-snapshot: false
39 changes: 39 additions & 0 deletions .github/actions/docker-build/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Build Docker image
description: Build a Docker image into the local Docker daemon without pushing it.

inputs:
context:
description: Docker build context.
required: false
default: .
file:
description: Dockerfile path.
required: false
default: src/Codebelt.Cdn.Origin/Dockerfile
image:
description: Fully qualified local image reference, including its tag.
required: true

outputs:
image-id:
description: The local Docker image ID.
value: ${{ steps.build.outputs.image-id }}

runs:
using: composite
steps:
- id: build
name: Build image
shell: bash
env:
BUILD_CONTEXT: ${{ inputs.context }}
DOCKERFILE: ${{ inputs.file }}
IMAGE: ${{ inputs.image }}
run: |
set -euo pipefail

test -n "$IMAGE"
docker build --pull --file "$DOCKERFILE" --tag "$IMAGE" "$BUILD_CONTEXT"

image_id="$(docker image inspect --format '{{.Id}}' "$IMAGE")"
printf 'image-id=%s\n' "$image_id" >> "$GITHUB_OUTPUT"
34 changes: 34 additions & 0 deletions .github/actions/docker-load/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Load Docker image
description: Load a Docker image tar archive into the local Docker daemon.

inputs:
image:
description: Fully qualified image reference expected after loading.
required: true
tarball:
description: Docker image tar archive path.
required: true

outputs:
image-id:
description: The loaded Docker image ID.
value: ${{ steps.load.outputs.image-id }}

runs:
using: composite
steps:
- id: load
name: Load image archive
shell: bash
env:
IMAGE: ${{ inputs.image }}
TARBALL: ${{ inputs.tarball }}
run: |
set -euo pipefail

test -n "$IMAGE"
test -f "$TARBALL"
docker image load --input "$TARBALL"

image_id="$(docker image inspect --format '{{.Id}}' "$IMAGE")"
printf 'image-id=%s\n' "$image_id" >> "$GITHUB_OUTPUT"
30 changes: 30 additions & 0 deletions .github/actions/docker-login/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Log in to Docker registry
description: Authenticate the local Docker daemon to one registry using password-stdin.

inputs:
registry:
description: Registry host, without a repository path.
required: true
username:
description: Registry username.
required: true
password:
description: Registry password or token.
required: true

runs:
using: composite
steps:
- name: Authenticate Docker daemon
shell: bash
env:
REGISTRY: ${{ inputs.registry }}
USERNAME: ${{ inputs.username }}
PASSWORD: ${{ inputs.password }}
run: |
set -euo pipefail

test -n "$REGISTRY"
test -n "$USERNAME"
test -n "$PASSWORD"
printf '%s' "$PASSWORD" | docker login "$REGISTRY" --username "$USERNAME" --password-stdin
47 changes: 47 additions & 0 deletions .github/actions/docker-push/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Push Docker image
description: Push a loaded Docker image and expose the registry digest.

inputs:
image:
description: Fully qualified image reference, including its tag.
required: true

outputs:
image-digest:
description: Immutable registry digest returned by Docker after the push.
value: ${{ steps.push.outputs.image-digest }}

runs:
using: composite
steps:
- id: push
name: Push image
shell: bash
env:
IMAGE: ${{ inputs.image }}
PUSH_LOG: ${{ runner.temp }}/codebelt-docker-push.log
run: |
set -euo pipefail

test -n "$IMAGE"
docker image inspect "$IMAGE" >/dev/null

set +e
docker image push "$IMAGE" 2>&1 | tee "$PUSH_LOG"
push_status=${PIPESTATUS[0]}
set -e

if [[ "$push_status" -ne 0 ]]; then
exit "$push_status"
fi

image_digest="$(grep -oE 'digest: sha256:[0-9a-f]{64}' "$PUSH_LOG" | tail -n 1 | sed 's/.*digest: //')"
if [[ -z "$image_digest" ]]; then
repository_digest="$(docker image inspect --format '{{index .RepoDigests 0}}' "$IMAGE" 2>/dev/null || true)"
if [[ "$repository_digest" == *@* ]]; then
image_digest="${repository_digest##*@}"
fi
fi

test -n "$image_digest"
printf 'image-digest=%s\n' "$image_digest" >> "$GITHUB_OUTPUT"
Loading
Loading