* fix `nil` parsed of `postCreateCommand` * parse undefined fields to `nil` ptr * Added JSON Unmarshaler with default values * Refactored DevContainer Model Errors * Added `DevContainerJSON` to unmarshal devcontainer.json
314 lines
16 KiB
Go
314 lines
16 KiB
Go
package devstar_devcontainer
|
||
|
||
import (
|
||
"code.gitea.io/gitea/modules/json"
|
||
)
|
||
|
||
// DevContainerJSON 用于反序列化仓库中的 DevContainer 配置信息
|
||
// `.devcontainer/devcontainer.json` or `.devcontainer.json` in project root.
|
||
// ref: https://containers.dev/implementors/json_reference
|
||
type DevContainerJSON struct {
|
||
/***********************************************************************************
|
||
* BEGIN General devcontainer.json properties
|
||
* https://containers.dev/implementors/json_reference/#general-properties
|
||
***********************************************************************************/
|
||
// Name A name for the dev container displayed in the UI.
|
||
Name string `json:"name"`
|
||
|
||
// ForwardPorts An array of port numbers or "host:port" values (e.g. [3000, "db:5432"])
|
||
// that should always be forwarded from inside the primary container to the local machine (including on the web).
|
||
ForwardPorts []interface{} `json:"forwardPorts,omitempty"`
|
||
|
||
// PortsAttributes Object that maps a port number, "host:port" value, range, or regular expression
|
||
// to a set of default options.
|
||
PortsAttributes map[string]PortAttributeType `json:"portsAttributes,omitempty"`
|
||
|
||
// OtherPortsAttributes Default options for ports, port ranges, and hosts that aren’t configured using portsAttributes.
|
||
OtherPortsAttributes *PortAttributeType `json:"otherPortsAttributes,omitempty"`
|
||
|
||
// ContainerEnv A set of name-value pairs that sets or overrides environment variables for the container.
|
||
ContainerEnv map[string]string `json:"containerEnv,omitempty"`
|
||
|
||
// RemoteEnv A set of name-value pairs that sets or overrides environment variables
|
||
// for the devcontainer.json supporting service / tool (or sub-processes like terminals)
|
||
// but not the container as a whole.
|
||
RemoteEnv map[string]interface{} `json:"remoteEnv,omitempty"`
|
||
|
||
// RemoteUser Overrides the user for all operations run as inside the container.
|
||
// Default to either `root` or the last USER instruction in the related Dockerfile used to create the image.
|
||
RemoteUser *string `json:"remoteUser,omitempty"`
|
||
|
||
// ContainerUser
|
||
// Defaults to either root or the last USER instruction in the related Dockerfile used to create the image.
|
||
ContainerUser *string `json:"containerUser,omitempty"`
|
||
|
||
// UpdateRemoteUserUID On Linux, if containerUser or remoteUser is specified,
|
||
// the user’s UID/GID will be updated to match the local user’s UID/GID to avoid permission problems with bind mounts.
|
||
UpdateRemoteUserUID bool `json:"updateRemoteUserUID"`
|
||
|
||
// UserEnvProbe Indicates the type of shell to use to “probe” for user environment variables
|
||
// to include in devcontainer.json supporting services’ / tools’ processes:
|
||
// none, interactiveShell, loginShell, or loginInteractiveShell (default).
|
||
UserEnvProbe UserEnvProbeType `json:"userEnvProbe"`
|
||
|
||
// OverrideCommand Tells devcontainer.json supporting services / tools
|
||
// whether they should run /bin/sh -c "while sleep 1000; do :; done" when starting the container
|
||
// instead of the container’s default command (since the container can shut down if the default command fails).
|
||
// Set to false if the default command must run for the container to function properly.
|
||
// Default to `true` for when using an image Dockerfile and `false` when referencing a Docker Compose file.
|
||
OverrideCommand bool `json:"overrideCommand,omitempty"`
|
||
|
||
// ShutdownAction Indicates whether devcontainer.json supporting tools should stop the containers
|
||
// when the related tool window is closed / shut down.
|
||
// Values are `none`, `stopContainer`(default for image/Dockerfile), and `stopCompose`(default for Docker Compose).
|
||
ShutdownAction ShutdownActionType `json:"shutdownAction,omitempty"`
|
||
|
||
// Init Defaults to false.
|
||
// Cross-orchestrator way to indicate whether the tini init process should be used to help deal with zombie processes.
|
||
Init bool `json:"init"`
|
||
|
||
// Privileged Defaults to false. Cross-orchestrator way to cause the container to run in privileged mode (--privileged).
|
||
// Required for things like Docker-in-Docker, but has security implications particularly when running directly on Linux.
|
||
Privileged bool `json:"privileged"`
|
||
|
||
// CapAdd Defaults to []. Cross-orchestrator way to add capabilities typically disabled for a container.
|
||
// Most often used to add the *ptrace* capability required to debug languages like C++, Go, and Rust.
|
||
CapAdd []string `json:"capAdd"`
|
||
|
||
// SecurityOpt Defaults to []. Cross-orchestrator way to set container security options.
|
||
SecurityOpt []string `json:"securityOpt"`
|
||
|
||
// Mounts Cross-orchestrator way to add additional mounts to a container.
|
||
// Each value is a string that accepts the same values as the Docker CLI --mount flag.
|
||
// Environment and pre-defined variables may be referenced in the value.
|
||
Mounts []interface{} `json:"mounts,omitempty"`
|
||
|
||
// Features An object of Dev Container Feature IDs and related options to be added into your primary container.
|
||
// The specific options that are available varies by feature, so see its documentation for additional details.
|
||
Features map[string]interface{} `json:"features,omitempty"`
|
||
|
||
// OverrideFeatureInstallOrder allows you to override the Feature install order when needed.
|
||
OverrideFeatureInstallOrder []interface{} `json:"overrideFeatureInstallOrder,omitempty"`
|
||
|
||
// Customizations Product specific properties, defined in https://containers.dev/supporting#visual-studio-code
|
||
Customizations map[string]interface{} `json:"customizations,omitempty"`
|
||
/***********************************************************************************
|
||
* END General devcontainer.json properties
|
||
* https://containers.dev/implementors/json_reference/#general-properties
|
||
***********************************************************************************/
|
||
|
||
/***********************************************************************************
|
||
* BEGIN Scenario specific properties
|
||
* https://containers.dev/implementors/json_reference/#scenario-specific
|
||
***********************************************************************************/
|
||
// Image Required when using an image. The name of an image in a container registry
|
||
// that devcontainer.json supporting services/tools should use to create the DevContainer.
|
||
Image string `json:"image"`
|
||
|
||
// Build Docker build specific properties
|
||
Build *DockerBuildType `json:"build,omitempty"`
|
||
|
||
// AppPort accepts a port or array of ports that should be published locally when the container is running.
|
||
AppPort *interface{} `json:"appPort"`
|
||
|
||
// WorkspaceMount Requires workspaceFolder be set as well.
|
||
// Overrides the default local mount point for the workspace when the container is created.
|
||
// Supports the same values as the Docker CLI --mount flag.
|
||
// Environment and pre-defined variables may be referenced in the value.
|
||
WorkspaceMount *string `json:"workspaceMount,omitempty"`
|
||
|
||
// WorkspaceFolder Requires workspaceMount be set. Sets the default path that devcontainer.json supporting services / tools should open when connecting to the container. Defaults to the automatic source code mount location.
|
||
WorkspaceFolder *string `json:"workspaceFolder,omitempty"`
|
||
|
||
// RunArgs An array of Docker CLI arguments that should be used when running the container.
|
||
RunArgs []string `json:"runArgs,omitempty"`
|
||
|
||
// DockerComposeFile Required when using Docker Compose.
|
||
// Path or an ordered list of paths to Docker Compose files relative to the devcontainer.json file.
|
||
DockerComposeFile *interface{} `json:"dockerComposeFile,omitempty"`
|
||
|
||
// Service Required when using Docker Compose.
|
||
// The name of the service devcontainer.json supporting services / tools should connect to once running.
|
||
Service *string `json:"service,omitempty"`
|
||
|
||
// RunServices An array of services in your Docker Compose configuration
|
||
// that should be started by devcontainer.json supporting services / tools.
|
||
// These will also be stopped when you disconnect unless "shutdownAction" is "none". Defaults to all services.
|
||
RunServices []interface{} `json:"runServices,omitempty"`
|
||
|
||
// WorkspaceFolder Sets the default path that devcontainer.json
|
||
// supporting services/tools should open when connecting to the container
|
||
// (which is often the path to a volume mount where the source code can be found in the container).
|
||
// Defaults to "/".
|
||
//WorkspaceFolder string `json:"workspaceFolder"`
|
||
/***********************************************************************************
|
||
* END Scenario specific properties
|
||
* https://containers.dev/implementors/json_reference/#scenario-specific
|
||
***********************************************************************************/
|
||
|
||
/***********************************************************************************
|
||
* BEGIN Tool-specific properties
|
||
* https://containers.dev/implementors/json_reference/#tool-specific
|
||
***********************************************************************************/
|
||
// InitializedCommand A command string or list of command arguments to run on the host machine during initialization,
|
||
// including during container creation and on subsequent starts.
|
||
// The command may run more than once during a given session.
|
||
// ⚠ The command is run wherever the source code is located on the host. For cloud services, this is in the cloud.
|
||
// Note that the array syntax will execute the command without a shell.
|
||
// You can learn more about formatting string vs array vs object properties.
|
||
InitializeCommand *interface{} `json:"initializeCommand,omitempty"`
|
||
|
||
// OnCreateCommand is the first of three (along with updateContentCommand and postCreateCommand)
|
||
// that finalizes container setup when a dev container is created.
|
||
// It and subsequent commands execute inside the container immediately after it has started for the first time.
|
||
// Cloud services can use this command when caching or prebuilding a container.
|
||
// This means that it will not typically have access to user-scoped assets or secrets.
|
||
// Note that the array syntax will execute the command without a shell.
|
||
OnCreateCommand interface{} `json:"onCreateCommand"`
|
||
UpdateContentCommand interface{} `json:"updateContentCommand "`
|
||
PostCreateCommand interface{} `json:"postCreateCommand"`
|
||
PostStartCommand interface{} `json:"postStartCommand"`
|
||
PostAttachCommand interface{} `json:"postAttachCommand"`
|
||
WaitFor DevContainerConfigCommandType `json:"waitFor,omitempty"`
|
||
HostRequirements *HostRequirementsType `json:"hostRequirements,omitempty"`
|
||
/***********************************************************************************
|
||
* END Tool-specific properties
|
||
* https://containers.dev/implementors/json_reference/#tool-specific
|
||
***********************************************************************************/
|
||
}
|
||
|
||
type DevContainerConfigCommandType string
|
||
|
||
const (
|
||
InitializeCommand DevContainerConfigCommandType = "initializeCommand"
|
||
OnCreateCommand DevContainerConfigCommandType = "onCreateCommand"
|
||
UpdateContentCommand DevContainerConfigCommandType = "updateContentCommand"
|
||
PostCreateCommand DevContainerConfigCommandType = "postCreateCommand"
|
||
PostStartCommand DevContainerConfigCommandType = "postStartCommand"
|
||
PostAttachCommand DevContainerConfigCommandType = "postAttachCommand"
|
||
)
|
||
|
||
type ShutdownActionType string
|
||
|
||
const (
|
||
NoneShutdownActionType ShutdownActionType = "none"
|
||
StopContainerShutdownActionType ShutdownActionType = "stopContainer"
|
||
StopComposeShutdownActionType ShutdownActionType = "stopCompose"
|
||
)
|
||
|
||
type UserEnvProbeType string
|
||
|
||
const (
|
||
NoneUserEnvProbeType UserEnvProbeType = "none"
|
||
LoginShellUserEnvProbeType UserEnvProbeType = "loginShell"
|
||
InteractiveShellUserEnvProbeType UserEnvProbeType = "interactiveShell"
|
||
LoginInteractiveShellUserEnvProbeType UserEnvProbeType = "loginInteractiveShell"
|
||
)
|
||
|
||
// DockerBuildType Docker build specific properties
|
||
type DockerBuildType struct {
|
||
// Dockerfile Required when using a Dockerfile.
|
||
// The location of a Dockerfile that defines the contents of the container.
|
||
// The path is relative to the devcontainer.json file.
|
||
Dockerfile string `json:"dockerfile,omitempty"`
|
||
|
||
// Path that the Docker build should be run from relative to devcontainer.json.
|
||
Context string `json:"context"`
|
||
|
||
// Args A set of name-value pairs containing Docker image build arguments that should be passed when building a Dockerfile.
|
||
// Environment and pre-defined variables may be referenced in the values.
|
||
Args map[string]string `json:"args,omitempty"`
|
||
|
||
// Options An array of Docker image build options that passed to the build command when building a Dockerfile.
|
||
Options []string `json:"options"`
|
||
|
||
// Target A string that specifies a Docker image build target that should be passed when building a Dockerfile.
|
||
Target string `json:"target,omitempty"`
|
||
|
||
// CacheFrom A string or array of strings that specify one or more images to use as caches when building the image.
|
||
// Cached image identifiers are passed to the docker build command with --cache-from.
|
||
CacheFrom interface{} `json:"cacheFrom"`
|
||
}
|
||
|
||
// ProtocolType Enum values for Protocol
|
||
type ProtocolType string
|
||
|
||
const (
|
||
ProtocolTCP ProtocolType = "tcp"
|
||
ProtocolHTTP ProtocolType = "http"
|
||
ProtocolHTTPS ProtocolType = "https"
|
||
)
|
||
|
||
// OnAutoForwardType Enum for OnAutoForward
|
||
type OnAutoForwardType string
|
||
|
||
const (
|
||
OnAutoForwardNotify OnAutoForwardType = "notify"
|
||
OnAutoForwardOpenBrowser OnAutoForwardType = "openBrowser"
|
||
OnAutoForwardOpenBrowserOnce OnAutoForwardType = "openBrowserOnce"
|
||
OnAutoForwardOpenPreview OnAutoForwardType = "openPreview"
|
||
OnAutoForwardSilent OnAutoForwardType = "silent"
|
||
OnAutoForwardIgnore OnAutoForwardType = "ignore"
|
||
)
|
||
|
||
// PortAttributeType
|
||
//
|
||
// ref: https://containers.dev/implementors/json_reference/#port-attributes
|
||
type PortAttributeType struct {
|
||
Label string `json:"label,omitempty"`
|
||
Protocol ProtocolType `json:"protocol,omitempty"`
|
||
OnAutoForward OnAutoForwardType `json:"onAutoForward,omitempty"`
|
||
RequireLocalPort bool `json:"requireLocalPort,omitempty"`
|
||
ElevateIfNeeded bool `json:"elevateIfNeeded,omitempty"`
|
||
}
|
||
|
||
type HostGPURequirements struct {
|
||
cores *uint64 `json:"cores"`
|
||
memory *string `json:"memory"`
|
||
}
|
||
|
||
type HostRequirementsType struct {
|
||
CPUs int `json:"cpus,omitempty"`
|
||
Memory string `json:"memory,omitempty"`
|
||
Storage string `json:"storage,omitempty"`
|
||
GPU interface{} `json:"gpu,omitempty"` // Could be bool, 'optional', or HostGPURequirements
|
||
}
|
||
|
||
// Unmarshal 反序列化JSON,返回带有默认值的 DevContainerJSON
|
||
func Unmarshal(devcontainerJSONContent string) (*DevContainerJSON, error) {
|
||
// 1. 根据官方文档配置默认值
|
||
devcontainerJSON := &DevContainerJSON{
|
||
ForwardPorts: []interface{}{},
|
||
UpdateRemoteUserUID: true,
|
||
UserEnvProbe: LoginInteractiveShellUserEnvProbeType,
|
||
Init: false,
|
||
Privileged: false,
|
||
CapAdd: []string{},
|
||
SecurityOpt: []string{},
|
||
Build: &DockerBuildType{
|
||
Context: ".",
|
||
Options: []string{},
|
||
},
|
||
WaitFor: UpdateContentCommand,
|
||
OtherPortsAttributes: &PortAttributeType{
|
||
OnAutoForward: OnAutoForwardNotify,
|
||
},
|
||
}
|
||
|
||
// 2. JSON 反序列化
|
||
err := json.Unmarshal([]byte(devcontainerJSONContent), devcontainerJSON)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 3. 对于配置的端口的缺省字段进行设置
|
||
for _, portAttribute := range devcontainerJSON.PortsAttributes {
|
||
if len(portAttribute.OnAutoForward) == 0 {
|
||
portAttribute.OnAutoForward = OnAutoForwardNotify
|
||
}
|
||
}
|
||
|
||
// 4. 返回
|
||
return devcontainerJSON, err
|
||
}
|