Skip to content

Commit

Permalink
Add static frontend support for azd buildpacks (#2900)
Browse files Browse the repository at this point in the history
When a containerized static frontend (with no Dockerfile) specifies an output directory `dist`, azd will configure Oryx builder to serve static content from the output directory using nginx.

The azure.yaml generation logic in simplified init is updated to include better `dist` defaults for specific web frameworks, supporting angular, react, and vue at minimum.

Finally, Oryx has surfaced `ORYX_RUNTIME_PORT` as a configuration knob. azd will always set this to port 80 for consistency and avoid dealing with language-specific ports.

Fixes #2750
  • Loading branch information
weikanglim committed Oct 27, 2023
1 parent 81cbc77 commit 29093c6
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 13 deletions.
21 changes: 21 additions & 0 deletions cli/azd/internal/repository/app_init.go
Expand Up @@ -234,6 +234,27 @@ func prjConfigFromDetect(
}
}

if prj.HasWebUIFramework() {
// By default, use 'dist'. This is common for frameworks such as:
// - TypeScript
// - Vue.js
svc.OutputPath = "dist"

loop:
for _, dep := range prj.Dependencies {
switch dep {
case appdetect.JsReact:
// react uses 'build'
svc.OutputPath = "build"
break loop
case appdetect.JsAngular:
// angular uses dist/<project name>
svc.OutputPath = "dist/" + filepath.Base(rel)
break loop
}
}
}

name := filepath.Base(rel)
if name == "." {
name = config.Name
Expand Down
9 changes: 2 additions & 7 deletions cli/azd/internal/repository/infra_confirm.go
Expand Up @@ -97,13 +97,8 @@ func (i *Initializer) infraSpecFromDetect(
}

if svc.Docker == nil || svc.Docker.Path == "" {
// Match target ports from the default builder image:
// - python: 80
// - other: 8080
serviceSpec.Port = 8080
if svc.Language == appdetect.Python {
serviceSpec.Port = 80
}
// default builder always specifies port 80
serviceSpec.Port = 80
}

for _, framework := range svc.Dependencies {
Expand Down
8 changes: 4 additions & 4 deletions cli/azd/internal/repository/infra_confirm_test.go
Expand Up @@ -35,7 +35,7 @@ func TestInitializer_infraSpecFromDetect(t *testing.T) {
Services: []scaffold.ServiceSpec{
{
Name: "dotnet",
Port: 8080,
Port: 80,
Backend: &scaffold.Backend{},
},
},
Expand All @@ -59,7 +59,7 @@ func TestInitializer_infraSpecFromDetect(t *testing.T) {
Services: []scaffold.ServiceSpec{
{
Name: "js",
Port: 8080,
Port: 80,
Frontend: &scaffold.Frontend{},
},
},
Expand Down Expand Up @@ -126,7 +126,7 @@ func TestInitializer_infraSpecFromDetect(t *testing.T) {
},
{
Name: "js",
Port: 8080,
Port: 80,
Frontend: &scaffold.Frontend{
Backends: []scaffold.ServiceReference{
{
Expand Down Expand Up @@ -185,7 +185,7 @@ func TestInitializer_infraSpecFromDetect(t *testing.T) {
},
{
Name: "js",
Port: 8080,
Port: 80,
Frontend: &scaffold.Frontend{
Backends: []scaffold.ServiceReference{
{
Expand Down
27 changes: 25 additions & 2 deletions cli/azd/pkg/project/framework_service_docker.go
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"
"log"
"os"
"path"
"path/filepath"
"strings"

Expand Down Expand Up @@ -273,8 +274,8 @@ func (p *dockerProject) Package(
}

// Default builder image to produce container images from source
const DefaultBuilderImage = "mcr.microsoft.com/oryx/builder:debian-bullseye-20230830.1"
const DefaultDotNetBuilderImage = "mcr.microsoft.com/oryx/builder:debian-buster-20230830.1"
const DefaultBuilderImage = "mcr.microsoft.com/oryx/builder:debian-bullseye-20231004.1"
const DefaultDotNetBuilderImage = "mcr.microsoft.com/oryx/builder:debian-buster-20231004.1"

func (p *dockerProject) packBuild(
ctx context.Context,
Expand All @@ -297,6 +298,28 @@ func (p *dockerProject) packBuild(
userDefinedImage = true
}

if !userDefinedImage {
// Always default to port 80 for consistency across languages
environ = append(environ, "ORYX_RUNTIME_PORT=80")

if svc.OutputPath != "" && (svc.Language == ServiceLanguageTypeScript || svc.Language == ServiceLanguageJavaScript) {
inDockerOutputPath := path.Join("/workspace", svc.OutputPath)
// A dist folder has been set.
// We assume that the service is a front-end service, configuring a nginx web server to serve the static content
// produced.
environ = append(environ,
"ORYX_RUNTIME_IMAGE=nginx:1.25.2-bookworm",
fmt.Sprintf(
//nolint:lll
"ORYX_RUNTIME_SCRIPT=[ -d \"%s\" ] || { echo \"error: directory '%s' does not exist. ensure the 'dist' path in azure.yaml is specified correctly.\"; exit 1; } && "+
"rm -rf /usr/share/nginx/html && ln -sT %s /usr/share/nginx/html && "+
"nginx -g 'daemon off;'",
inDockerOutputPath,
svc.OutputPath,
inDockerOutputPath))
}
}

previewer := p.console.ShowPreviewer(ctx,
&input.ShowPreviewerOptions{
Prefix: " ",
Expand Down

0 comments on commit 29093c6

Please sign in to comment.