Skip to content

[Spike] Add GitHub Pages static hosting and bundle generation - #20924

Draft
Mitch Denny (mitchdenny) wants to merge 2 commits into
mainfrom
mitchdenny-github-pages-hosting
Draft

Mitch Denny (mitchdenny) wants to merge 2 commits into
mainfrom
mitchdenny-github-pages-hosting

Conversation

@mitchdenny

Copy link
Copy Markdown
Member

Description

Explore GitHub Pages as a static compute environment for Aspire frontends, so applications can use the existing image-build pipeline to produce a complete static website bundle without requiring GitHub credentials or publishing to a real Pages site.

This draft contains Stage 1 of the spike: the experimental Aspire.Hosting.GitHubPages integration, intended site addresses, Vite base-path configuration, and independently selectable bundle generation. Local Vite development behavior remains unchanged.

The environment supports a public project site at https://owner.github.io/repository/, an account-root site, or an explicitly declared canonical hostname. These addresses are declared rather than verified against GitHub. One repository receives one static website.

User-facing usage

C# AppHost:

var pages = builder.AddGitHubPagesEnvironment("pages", "owner/repository");

builder.AddViteApp("frontend", "../frontend")
       .WithComputeEnvironment(pages);

TypeScript AppHost (exported API; full generated-SDK validation is deferred):

const pages = await builder.addGitHubPagesEnvironment("pages", "owner/repository");

await builder.addViteApp("frontend", "../frontend")
    .withComputeEnvironment(pages);

Use .WithCustomDomain("app.example.com") to declare an existing canonical hostname and switch the content base path to /. Without it, Vite's generated build uses --base=/repository/ for a project site.

Generate the frontend bundle and its build prerequisites:

aspire do bundle-frontend --output-path ./artifacts

The generated output is github-pages/pages/dist/ plus site.json. aspire publish also generates the bundle. This is a source-only, unpublished integration; evaluating its C# APIs requires suppressing the experimental diagnostics.

Implementation

  • Preserve Vite identity through its existing publish-mode container transformation and attach the Pages deployment target and standard container-file consumer edge.
  • Add IContainerRuntime.CopyFilesFromImageAsync with stopped-container extraction for Docker/Podman and an explicit unsupported default for other implementations. Extraction does not start, pull, or push the source image.
  • Validate source selection, build-only Vite publishing, container paths, a root index.html, and symbolic-link rejection. Prepare replacement output on the output filesystem, with rollback and independent container cleanup.
  • Add focused model/pipeline tests, generated-output snapshots, a real credential-free static image fixture, and integration documentation.
  • Add both projects to the solution. Existing ProjectGraph edges and trigger-map conventions cover the new tests; no new curated runtime-only routing edge is needed.

Dependencies are the existing Aspire.Hosting and Aspire.Hosting.JavaScript projects. No new external package dependency is introduced.

Remaining scope

This is not ready to ship. Backend CORS wiring, late-bound backend URLs for frontend builds, a Pages publisher, simulator/browser coverage, provider preflight, and full generated-SDK/preview readiness remain follow-up stages. aspire deploy fails explicitly rather than reporting a skipped Pages deployment as successful. There are no live Pages, Azure, DNS, certificate, or repository-settings changes.

Validation

Command Result
./restore.sh Passed
dotnet test --project tests/Aspire.Hosting.GitHubPages.Tests/Aspire.Hosting.GitHubPages.Tests.csproj --no-launch-profile -- --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true" Passed: 48 tests, including a native scratch-image build and bundle extraction
dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-launch-profile -- --filter-class "*.ContainerRuntimeBaseTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true" Passed: 25 tests
dotnet test --project tests/Aspire.Hosting.JavaScript.Tests/Aspire.Hosting.JavaScript.Tests.csproj --no-launch-profile -- --filter-class "*.AddViteAppTests" --filter-class "*.AddViteAppWithPnpmTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true" Passed: 46 tests
dotnet pack src/Aspire.Hosting.GitHubPages/Aspire.Hosting.GitHubPages.csproj --nologo -v:q Passed
git diff --check Passed

Bundle and generated Vite Dockerfile snapshots were reviewed. The native fixture image and export containers were cleaned up. Real Vite compilation, live Pages compatibility, full generated polyglot SDK validation, and the full repository build/test suite were not run for this checkpoint.

Security considerations

The integration invokes the selected container runtime and copies image-provided files into temporary and caller-selected output directories. Export commands use argument lists, the export container is not started, bundle validation rejects symbolic links, and output replacement uses same-filesystem staging. No GitHub credentials, cloud permissions, or new network listeners are introduced. Security review is still needed for path handling, image-file extraction, replacement/rollback, and cleanup behavior; no threat model or formal security review has been completed.

Fixes # (issue)

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3f065579-78f8-48d8-8f2c-35c77892a1f5
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 20924

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 20924"

@aspire-repo-bot
aspire-repo-bot Bot requested a balanced review from Copilot October 12, 2026 12:00
@github-actions github-actions Bot added the area-integrations Issues pertaining to Aspire Integrations packages label Oct 12, 2026
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Parent-symlink handling, cleanup behavior, rollback coverage, and package publication intent need resolution.

3 open findings
What changed in this PR

Adds an experimental GitHub Pages hosting integration that builds static frontend images and extracts deployable website bundles without GitHub credentials.

Changes:

  • Adds GitHub Pages environment/site resources and bundle pipeline.
  • Extends Vite metadata and container-image file extraction.
  • Adds integration tests, snapshots, documentation, and solution entries.
File Description
Aspire.slnx Registers new source and test projects.
src/​Aspire.Hosting.GitHubPages/​Aspire.Hosting.GitHubPages.csproj Defines the integration package.
src/​Aspire.Hosting.GitHubPages/​GitHubPagesEnvironmentResource.cs Models Pages addresses and endpoints.
src/​Aspire.Hosting.GitHubPages/​GitHubPagesHostingExtensions.cs Implements configuration and bundle generation.
src/​Aspire.Hosting.GitHubPages/​GitHubPagesSiteResource.cs Models the static-site deployment target.
src/​Aspire.Hosting.GitHubPages/​README.md Documents usage and limitations.
src/​Aspire.Hosting.JavaScript/​JavaScriptHostingExtensions.cs Marks Vite resources for publish transformations.
src/​Aspire.Hosting.JavaScript/​ViteAppAnnotation.cs Adds persistent Vite identity metadata.
src/​Aspire.Hosting/​Publishing/​ContainerRuntimeBase.cs Implements stopped-container extraction.
src/​Aspire.Hosting/​Publishing/​IContainerRuntime.cs Adds the image-file copy contract.
tests/​Aspire.Hosting.GitHubPages.Tests/​Aspire.Hosting.GitHubPages.Tests.csproj Defines integration tests.
tests/​Aspire.Hosting.GitHubPages.Tests/​GitHubPagesHostingTests.cs Tests modeling, validation, and bundling.
tests/​Aspire.Hosting.GitHubPages.Tests/​Snapshots/​...ViteDockerfile....txt Verifies generated Vite Dockerfile.
tests/​Aspire.Hosting.GitHubPages.Tests/​Snapshots/​.../​site.verified.json Verifies bundle metadata.
tests/​Aspire.Hosting.GitHubPages.Tests/​Snapshots/​.../​index.verified.html Verifies generated HTML output.
tests/​Aspire.Hosting.GitHubPages.Tests/​Snapshots/​.../​app.verified.js Verifies generated JavaScript asset.
tests/​Aspire.Hosting.Tests/​Publishing/​ContainerRuntimeBaseTests.cs Tests extraction and cleanup behavior.
tests/​Aspire.Hosting.TestUtilities/​Publishing/​FakeContainerRuntime.cs Supports extraction tests.

🧠 Review effort: Balanced

Comment on lines +278 to +281
Directory.CreateDirectory(outputDirectory);
if (Directory.Exists(directory) && (File.GetAttributes(directory) & FileAttributes.ReparsePoint) != 0)
{
throw new DistributedApplicationException($"GitHub Pages output directory '{directory}' cannot be a symbolic link.");

<PropertyGroup>
<TargetFramework>$(DefaultTargetFramework)</TargetFramework>
<IsPackable>true</IsPackable>
Comment on lines +141 to +143
await ExecuteContainerCommandForOutputAsync(
["rm", "--force", "--volumes", containerName],
"remove file-export container", imageName, cleanup.Token).ConfigureAwait(false);
@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3f065579-78f8-48d8-8f2c-35c77892a1f5
@github-actions

Copy link
Copy Markdown
Contributor

Tests selector

Selects the full PR test matrix + all PR-gated jobs (ALL) — a rule matching 'Aspire.slnx' selects ALL


Selection computed for commit 1dab266.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Cancellation can be converted into failure, and the experimental package is not configured to remain prerelease.

4 open findings

🧠 Review effort: Balanced

Comment on lines +145 to +147
catch (Exception cleanupError) when (copyError is not null)
{
throw new AggregateException("Copying image files and removing the temporary container both failed.", copyError, cleanupError);
@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-integrations Issues pertaining to Aspire Integrations packages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants