Merge pull request #8106 from dokku/8007-jsonc-app-json
Support inline comments for app.json
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# 0.36.0 Migration Guide
|
||||
|
||||
## Changes
|
||||
|
||||
- The `app.json` file format is now parsed as JSONC and supports inline comments.
|
||||
|
||||
## Removals
|
||||
|
||||
- Support for the `DOKKU_DOCKERFILE_CACHE_BUILD` environment variable has been removed. Use the `docker-options` plugin to set build arguments for your app instead.
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
"github.com/tailscale/hujson"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
@@ -262,8 +263,14 @@ func ReadAppJSON(path string) (AppJSON, error) {
|
||||
return AppJSON{}, nil
|
||||
}
|
||||
|
||||
ast, err := hujson.Parse(b)
|
||||
if err != nil {
|
||||
return AppJSON{}, fmt.Errorf("Cannot parse app.json as jsonc: %v", err)
|
||||
}
|
||||
ast.Standardize()
|
||||
|
||||
var appJSON AppJSON
|
||||
if err = json.Unmarshal(b, &appJSON); err != nil {
|
||||
if err = json.Unmarshal(ast.Pack(), &appJSON); err != nil {
|
||||
return AppJSON{}, fmt.Errorf("Cannot parse app.json: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -133,11 +133,7 @@ func cleanupDeploymentContainer(containerID string, phase string) error {
|
||||
}
|
||||
|
||||
func executeScript(appName string, image string, imageTag string, phase string) error {
|
||||
phaseName := phase
|
||||
if phase == "heroku.postdeploy" {
|
||||
phaseName = "first deploy postdeploy"
|
||||
}
|
||||
common.LogInfo1(fmt.Sprintf("Checking for %s task", phaseName))
|
||||
common.LogInfo1(fmt.Sprintf("Checking for %s task", phase))
|
||||
command := ""
|
||||
phaseSource := ""
|
||||
if phase == "release" {
|
||||
@@ -152,14 +148,14 @@ func executeScript(appName string, image string, imageTag string, phase string)
|
||||
}
|
||||
|
||||
if command == "" {
|
||||
common.LogVerbose(fmt.Sprintf("No %s task found, skipping", phaseName))
|
||||
common.LogVerbose(fmt.Sprintf("No %s task found, skipping", phase))
|
||||
return nil
|
||||
}
|
||||
|
||||
if phase == "predeploy" {
|
||||
common.LogVerbose(fmt.Sprintf("Executing %s task from %s: %s", phaseName, phaseSource, command))
|
||||
common.LogVerbose(fmt.Sprintf("Executing %s task from %s: %s", phase, phaseSource, command))
|
||||
} else {
|
||||
common.LogVerbose(fmt.Sprintf("Executing %s task from %s in ephemeral container: %s", phaseName, phaseSource, command))
|
||||
common.LogVerbose(fmt.Sprintf("Executing %s task from %s in ephemeral container: %s", phase, phaseSource, command))
|
||||
}
|
||||
|
||||
isHerokuishImage := common.IsImageHerokuishBased(image, appName)
|
||||
@@ -271,15 +267,15 @@ func executeScript(appName string, image string, imageTag string, phase string)
|
||||
defer cleanupDeploymentContainer(containerID, phase)
|
||||
|
||||
if !waitForExecution(containerID) {
|
||||
common.LogInfo2Quiet(fmt.Sprintf("Start of %s %s task (%s) output", appName, phaseName, containerID[0:9]))
|
||||
common.LogInfo2Quiet(fmt.Sprintf("Start of %s %s task (%s) output", appName, phase, containerID[0:9]))
|
||||
common.LogVerboseQuietContainerLogs(containerID)
|
||||
common.LogInfo2Quiet(fmt.Sprintf("End of %s %s task (%s) output", appName, phaseName, containerID[0:9]))
|
||||
return fmt.Errorf("Execution of %s task failed: %s", phaseName, command)
|
||||
common.LogInfo2Quiet(fmt.Sprintf("End of %s %s task (%s) output", appName, phase, containerID[0:9]))
|
||||
return fmt.Errorf("Execution of %s task failed: %s", phase, command)
|
||||
}
|
||||
|
||||
common.LogInfo2Quiet(fmt.Sprintf("Start of %s %s task (%s) output", appName, phaseName, containerID[0:9]))
|
||||
common.LogInfo2Quiet(fmt.Sprintf("Start of %s %s task (%s) output", appName, phase, containerID[0:9]))
|
||||
common.LogVerboseQuietContainerLogs(containerID)
|
||||
common.LogInfo2Quiet(fmt.Sprintf("End of %s %s task (%s) output", appName, phaseName, containerID[0:9]))
|
||||
common.LogInfo2Quiet(fmt.Sprintf("End of %s %s task (%s) output", appName, phase, containerID[0:9]))
|
||||
|
||||
if phase != "predeploy" {
|
||||
return nil
|
||||
|
||||
@@ -6,6 +6,7 @@ require (
|
||||
github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a
|
||||
k8s.io/utils v0.0.0-20240102154912-e7106e64919e
|
||||
)
|
||||
|
||||
@@ -23,6 +24,7 @@ require (
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/sftp v1.13.5 // indirect
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible // indirect
|
||||
github.com/stretchr/testify v1.8.4 // indirect
|
||||
golang.org/x/crypto v0.43.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
|
||||
@@ -39,8 +39,11 @@ github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a h1:a6TNDN9CgG+cYjaeN8l2mc4kSz2iMiCDQxPEyltUV/I=
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package appjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
@@ -42,18 +42,17 @@ func TriggerAppJSONGetContent(appName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
b, err := os.ReadFile(getProcessSpecificAppJSONPath(appName))
|
||||
appJSON, err := ReadAppJSON(getProcessSpecificAppJSONPath(appName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Cannot read app.json file: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
content := strings.TrimSpace(string(b))
|
||||
if content == "" {
|
||||
fmt.Print("{}")
|
||||
return nil
|
||||
content, err := json.Marshal(appJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(content)
|
||||
fmt.Print(string(content))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ require (
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/sftp v1.13.5 // indirect
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a // indirect
|
||||
golang.org/x/crypto v0.43.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
|
||||
@@ -51,8 +51,11 @@ github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a h1:a6TNDN9CgG+cYjaeN8l2mc4kSz2iMiCDQxPEyltUV/I=
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
|
||||
@@ -26,7 +26,7 @@ require (
|
||||
github.com/pkg/sftp v1.13.5 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible // indirect
|
||||
github.com/stretchr/testify v1.8.4 // indirect
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a // indirect
|
||||
golang.org/x/crypto v0.43.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect
|
||||
|
||||
@@ -52,6 +52,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a h1:a6TNDN9CgG+cYjaeN8l2mc4kSz2iMiCDQxPEyltUV/I=
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
|
||||
@@ -139,6 +139,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/cobra v1.10.1 // indirect
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a // indirect
|
||||
github.com/traefik/paerser v0.2.2 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xlab/treeprint v1.2.0 // indirect
|
||||
|
||||
@@ -341,6 +341,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a h1:a6TNDN9CgG+cYjaeN8l2mc4kSz2iMiCDQxPEyltUV/I=
|
||||
github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo=
|
||||
github.com/traefik/paerser v0.2.2 h1:cpzW/ZrQrBh3mdwD/jnp6aXASiUFKOVr6ldP+keJTcQ=
|
||||
github.com/traefik/paerser v0.2.2/go.mod h1:7BBDd4FANoVgaTZG+yh26jI6CA2nds7D/4VTEdIsh24=
|
||||
github.com/traefik/traefik/v2 v2.11.30 h1:Jkb5H8aI9lKQY/DDqwK3JP7iNUEnRfNLWXsr3azFf8w=
|
||||
|
||||
24
tests/apps/python/app-heroku-postdeploy.json
Normal file
24
tests/apps/python/app-heroku-postdeploy.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"healthchecks": {
|
||||
"web": [
|
||||
{
|
||||
"attempts": 2,
|
||||
"content": "python/http.server",
|
||||
"name": "check-1",
|
||||
"path": "/",
|
||||
"timeout": 5,
|
||||
"type": "startup",
|
||||
"wait": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"dokku": {
|
||||
// this is a comment for the postdeploy script
|
||||
"postdeploy": "touch /app/postdeploy.test",
|
||||
// this is a comment for the predeploy script
|
||||
"predeploy": "touch /app/predeploy.test"
|
||||
},
|
||||
"postdeploy": "touch /app/heroku-postdeploy.test"
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,9 @@
|
||||
},
|
||||
"scripts": {
|
||||
"dokku": {
|
||||
// this is a comment for the postdeploy script
|
||||
"postdeploy": "touch /app/postdeploy.test",
|
||||
// this is a comment for the predeploy script
|
||||
"predeploy": "touch /app/predeploy.test"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ teardown() {
|
||||
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_failing_postdeploy
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output_contains "Execution of first deploy postdeploy task failed"
|
||||
assert_output_contains "Execution of heroku.postdeploy task failed"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,12 @@ teardown() {
|
||||
}
|
||||
|
||||
@test "(app-json) app.json scripts postdeploy" {
|
||||
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_postdeploy_command
|
||||
run /bin/bash -c "dokku app-json:set $TEST_APP appjson-path app-heroku-postdeploy.json"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
@@ -561,15 +561,6 @@ add_release_command() {
|
||||
echo "release: touch /app/release.test" >>"$APP_REPO_DIR/Procfile"
|
||||
}
|
||||
|
||||
add_postdeploy_command() {
|
||||
local APP="$1"
|
||||
local APP_REPO_DIR="$2"
|
||||
[[ -z "$APP" ]] && local APP="$TEST_APP"
|
||||
touch "$APP_REPO_DIR/app.json"
|
||||
contents=$(jq '.scripts.postdeploy = "touch /app/heroku-postdeploy.test"' "$APP_REPO_DIR/app.json")
|
||||
echo "${contents}" >"$APP_REPO_DIR/app.json"
|
||||
}
|
||||
|
||||
move_dockerfile_into_place() {
|
||||
local APP="$1"
|
||||
local APP_REPO_DIR="$2"
|
||||
|
||||
Reference in New Issue
Block a user