The apps plugin accepted `apps:set --global deploy-source` and `apps:set --global deploy-source-metadata` even though nothing reads those properties globally, accepted `apps:set <app> disable-autocreation` even though only the global form is consulted by `maybeCreateApp`, and never emitted `disable-autocreation` in `apps:report` for either scope. Drop the global `deploy-source*` writes from `GlobalProperties`, reject per-app `disable-autocreation` writes at the plugin level, and surface `--app-global-disable-autocreation` in both the per-app and `--global` reports so the property the runtime actually reads is round-trippable.
257 lines
5.8 KiB
Go
257 lines
5.8 KiB
Go
package apps
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/dokku/dokku/plugins/common"
|
|
)
|
|
|
|
// CommandClone clones an app
|
|
func CommandClone(oldAppName string, newAppName string, skipDeploy bool, ignoreExisting bool) error {
|
|
if oldAppName == "" {
|
|
return errors.New("Please specify an app to run the command on")
|
|
}
|
|
|
|
if newAppName == "" {
|
|
return errors.New("Please specify an new app name")
|
|
}
|
|
|
|
if err := common.VerifyAppName(oldAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := common.IsValidAppName(newAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := appExists(newAppName); err == nil {
|
|
if ignoreExisting {
|
|
common.LogWarn("Name is already taken")
|
|
return nil
|
|
}
|
|
|
|
return errors.New("Name is already taken")
|
|
}
|
|
|
|
common.LogInfo1Quiet(fmt.Sprintf("Cloning %s to %s", oldAppName, newAppName))
|
|
if err := createApp(newAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err := common.CallPlugnTrigger(common.PlugnTriggerInput{
|
|
Trigger: "post-app-clone-setup",
|
|
Args: []string{oldAppName, newAppName},
|
|
StreamStdio: true,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if skipDeploy {
|
|
os.Setenv("SKIP_REBUILD", "true")
|
|
}
|
|
|
|
_, err = common.CallPlugnTrigger(common.PlugnTriggerInput{
|
|
Trigger: "git-has-code",
|
|
Args: []string{newAppName},
|
|
StreamStdio: true,
|
|
})
|
|
if err != nil {
|
|
os.Setenv("SKIP_REBUILD", "true")
|
|
}
|
|
|
|
_, err = common.CallPlugnTrigger(common.PlugnTriggerInput{
|
|
Trigger: "post-app-clone",
|
|
Args: []string{oldAppName, newAppName},
|
|
StreamStdio: true,
|
|
})
|
|
return err
|
|
}
|
|
|
|
// CommandCreate creates app via command line
|
|
func CommandCreate(appName string) error {
|
|
if err := common.IsValidAppName(appName); err != nil {
|
|
return err
|
|
}
|
|
|
|
return createApp(appName)
|
|
}
|
|
|
|
// CommandDestroy destroys an app
|
|
func CommandDestroy(appName string, force bool) error {
|
|
if err := common.VerifyAppName(appName); err != nil {
|
|
return err
|
|
}
|
|
|
|
if force {
|
|
os.Setenv("DOKKU_APPS_FORCE_DELETE", "1")
|
|
}
|
|
|
|
return destroyApp(appName)
|
|
}
|
|
|
|
// CommandExists checks if an app exists
|
|
func CommandExists(appName string) error {
|
|
return appExists(appName)
|
|
}
|
|
|
|
// CommandList lists all apps
|
|
func CommandList() error {
|
|
common.LogInfo2Quiet("My Apps")
|
|
apps, err := common.DokkuApps()
|
|
if err != nil {
|
|
common.LogWarn(err.Error())
|
|
return nil
|
|
}
|
|
|
|
for _, appName := range apps {
|
|
common.Log(appName)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CommandLock locks an app for deployment
|
|
func CommandLock(appName string) error {
|
|
if err := common.VerifyAppName(appName); err != nil {
|
|
return err
|
|
}
|
|
|
|
lockPath := getLockPath(appName)
|
|
if _, err := os.Create(lockPath); err != nil {
|
|
return errors.New("Unable to create deploy lock")
|
|
}
|
|
|
|
common.LogInfo1("Deploy lock created")
|
|
return nil
|
|
}
|
|
|
|
// CommandLocked checks if an app is locked for deployment
|
|
func CommandLocked(appName string) error {
|
|
if err := common.VerifyAppName(appName); err != nil {
|
|
return err
|
|
}
|
|
|
|
if appIsLocked(appName) {
|
|
common.LogQuiet("Deploy lock exists")
|
|
return nil
|
|
|
|
}
|
|
return errors.New("Deploy lock does not exist")
|
|
}
|
|
|
|
// CommandRename renames an app
|
|
func CommandRename(oldAppName string, newAppName string, skipDeploy bool) error {
|
|
if oldAppName == "" {
|
|
return errors.New("Please specify an app to run the command on")
|
|
}
|
|
|
|
if newAppName == "" {
|
|
return errors.New("Please specify an new app name")
|
|
}
|
|
|
|
if err := common.VerifyAppName(oldAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := common.IsValidAppName(newAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := appExists(newAppName); err == nil {
|
|
return errors.New("Name is already taken")
|
|
}
|
|
|
|
common.LogInfo1Quiet(fmt.Sprintf("Renaming %s to %s", oldAppName, newAppName))
|
|
if err := createApp(newAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err := common.CallPlugnTrigger(common.PlugnTriggerInput{
|
|
Trigger: "post-app-rename-setup",
|
|
Args: []string{oldAppName, newAppName},
|
|
StreamStdio: true,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
os.Setenv("DOKKU_APPS_FORCE_DELETE", "1")
|
|
if err := destroyApp(oldAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
if skipDeploy {
|
|
os.Setenv("SKIP_REBUILD", "true")
|
|
}
|
|
|
|
_, err = common.CallPlugnTrigger(common.PlugnTriggerInput{
|
|
Trigger: "git-has-code",
|
|
Args: []string{newAppName},
|
|
StreamStdio: true,
|
|
})
|
|
if err != nil {
|
|
os.Setenv("SKIP_REBUILD", "true")
|
|
}
|
|
|
|
_, err = common.CallPlugnTrigger(common.PlugnTriggerInput{
|
|
Trigger: "post-app-rename",
|
|
Args: []string{oldAppName, newAppName},
|
|
StreamStdio: true,
|
|
})
|
|
return err
|
|
}
|
|
|
|
// CommandReport displays an app report for one or more apps
|
|
func CommandReport(appName string, format string, infoFlag string) error {
|
|
if len(appName) == 0 {
|
|
apps, err := common.DokkuApps()
|
|
if err != nil {
|
|
if errors.Is(err, common.NoAppsExist) {
|
|
common.LogWarn(err.Error())
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
for _, appName := range apps {
|
|
if err := ReportSingleApp(appName, format, infoFlag); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
return ReportSingleApp(appName, format, infoFlag)
|
|
}
|
|
|
|
// CommandSet set or clear an apps property for an app
|
|
func CommandSet(appName string, property string, value string) error {
|
|
if appName != "--global" && property == "disable-autocreation" {
|
|
common.LogFail("Property can only be specified globally")
|
|
}
|
|
common.CommandPropertySet("apps", appName, property, value, DefaultProperties, GlobalProperties)
|
|
return nil
|
|
}
|
|
|
|
// CommandUnlock unlocks an app for deployment
|
|
func CommandUnlock(appName string) error {
|
|
if err := common.VerifyAppName(appName); err != nil {
|
|
return err
|
|
}
|
|
|
|
lockfilePath := getLockPath(appName)
|
|
if _, err := os.Stat(lockfilePath); !os.IsNotExist(err) {
|
|
common.LogWarn("A deploy may be in progress.")
|
|
common.LogWarn("Removing the app lock will not stop in progress deploys.")
|
|
}
|
|
|
|
if err := os.Remove(lockfilePath); err != nil {
|
|
return errors.New("Unable to remove deploy lock")
|
|
}
|
|
|
|
common.LogInfo1("Deploy lock removed")
|
|
return nil
|
|
}
|