From 35032fe4cc6430ad315c129c1c551455f3418843 Mon Sep 17 00:00:00 2001 From: mikoto2000 Date: Thu, 31 Oct 2024 08:14:53 +0900 Subject: [PATCH] Update error handling in main.go Update `main.go` to differentiate error messages based on the type of error. * **Error Handling in `main.go`** - Add error type checks and corresponding messages for various error scenarios. - Print specific error messages for different error types, such as `os.ErrNotExist` and `os.ErrPermission`. * **Define Custom Error Types** - Define custom error types in `devcontainer/devcontainer.go`, `docker/docker.go`, `dockercompose/dockerCompose.go`, and `devcontainer/readConfigurationResult.go`. - Replace `errors.New` with the defined custom error types. * **Update Error Handling in `devcontainer/devcontainer.go`** - Replace generic error messages with custom error types for unknown type errors. * **Update Error Handling in `docker/docker.go`** - Replace generic error messages with custom error types for container start errors, chmod errors, and container not found errors. * **Update Error Handling in `dockercompose/dockerCompose.go`** - Replace generic error messages with custom error types for ps command errors, stop command errors, and down command errors. * **Update Error Handling in `devcontainer/readConfigurationResult.go`** - Replace generic error messages with custom error types for read configuration errors. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/mikoto2000/devcontainer.vim?shareId=XXXX-XXXX-XXXX-XXXX). --- devcontainer/devcontainer.go | 10 ++- devcontainer/readConfigurationResult.go | 11 +++- docker/docker.go | 31 +++++++-- dockercompose/dockerCompose.go | 42 ++++++++++-- main.go | 85 +++++++++++++++++++++---- 5 files changed, 153 insertions(+), 26 deletions(-) diff --git a/devcontainer/devcontainer.go b/devcontainer/devcontainer.go index 7f2e90e..ba0ae80 100644 --- a/devcontainer/devcontainer.go +++ b/devcontainer/devcontainer.go @@ -21,6 +21,14 @@ const containerCommand = "docker" var devcontainreArgsPrefix = []string{"up"} +type UnknownTypeError struct { + msg string +} + +func (e *UnknownTypeError) Error() string { + return e.msg +} + // devcontainer でコンテナを立ち上げ、 Vim を転送し、実行する。 // 既存実装の都合上、configFilePath から configDirForDevcontainer を抽出している func ExecuteDevcontainer(args []string, devcontainerPath string, vimFilePath string, cdrPath, configFilePath string, vimrc string) error { @@ -359,7 +367,7 @@ func findDockerComposeFileDir() (string, error) { vv := v[0].(string) dockerComposeFilePath = filepath.Join(devcontainerJSONDir, vv) default: - return "", errors.New("unknown type") + return "", &UnknownTypeError{msg: "unknown type"} } dockerComposeFileDir := filepath.Dir(dockerComposeFilePath) diff --git a/devcontainer/readConfigurationResult.go b/devcontainer/readConfigurationResult.go index f6e09b4..2b2169b 100644 --- a/devcontainer/readConfigurationResult.go +++ b/devcontainer/readConfigurationResult.go @@ -2,9 +2,16 @@ package devcontainer import ( "encoding/json" - "errors" ) +type ReadConfigurationError struct { + msg string +} + +func (e *ReadConfigurationError) Error() string { + return e.msg +} + // `devcontainers read-configuration` コマンドの実行結果スキーマ // // Example: { @@ -42,7 +49,7 @@ type ConfigFilePath struct { func GetConfigFilePath(readConfigurationCommandResult string) (string, error) { result, err := UnmarshalReadConfigurationCommandResult([]byte(readConfigurationCommandResult)) if err != nil { - return "", errors.New("`devcontainer read-configuration` の出力パースに失敗しました。`.devcontainer.json が存在することと、 docker エンジンが起動していることを確認してください。") + return "", &ReadConfigurationError{msg: "`devcontainer read-configuration` の出力パースに失敗しました。`.devcontainer.json が存在することと、 docker エンジンが起動していることを確認してください。"} } return result.Configuration.ConfigFilePath.FsPath, nil diff --git a/docker/docker.go b/docker/docker.go index a8caef1..97cdd3f 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -2,7 +2,6 @@ package docker import ( "context" - "errors" "fmt" "os" "os/exec" @@ -19,6 +18,30 @@ const containerCommand = "docker" var dockerRunArgsPrefix = []string{"run", "-d", "--rm"} var dockerRunArgsSuffix = []string{"sh", "-c", "trap \"exit 0\" TERM; sleep infinity & wait"} +type ContainerStartError struct { + msg string +} + +func (e *ContainerStartError) Error() string { + return e.msg +} + +type ChmodError struct { + msg string +} + +func (e *ChmodError) Error() string { + return e.msg +} + +type ContainerNotFoundError struct { + msg string +} + +func (e *ContainerNotFoundError) Error() string { + return e.msg +} + func Run(args []string, vimFilePath string, cdrPath string, configDirForDocker string, vimrc string, defaultRunargs []string) error { vimFileName := filepath.Base(vimFilePath) @@ -38,7 +61,7 @@ func Run(args []string, vimFilePath string, cdrPath string, configDirForDocker s if err != nil { fmt.Fprintln(os.Stderr, "Container start error.") fmt.Fprintln(os.Stderr, string(containerID)) - return err + return &ContainerStartError{msg: "Container start error."} } containerID = strings.ReplaceAll(containerID, "\n", "") containerID = strings.ReplaceAll(containerID, "\r", "") @@ -70,7 +93,7 @@ func Run(args []string, vimFilePath string, cdrPath string, configDirForDocker s if err != nil { fmt.Fprintln(os.Stderr, "chmod error.") fmt.Fprintln(os.Stderr, string(chmodResult)) - return err + return &ChmodError{msg: "chmod error."} } fmt.Printf(" done.\n") @@ -152,7 +175,7 @@ func GetContainerIDFromWorkspaceFolder(workspaceFolder string) (string, error) { psResult, err := Ps("label=devcontainer.local_folder=" + workspaceFilderAbs) if psResult == "" { - return "", errors.New("container not found.") + return "", &ContainerNotFoundError{msg: "container not found."} } if err != nil { return "", err diff --git a/dockercompose/dockerCompose.go b/dockercompose/dockerCompose.go index 01a074f..f681c6b 100644 --- a/dockercompose/dockerCompose.go +++ b/dockercompose/dockerCompose.go @@ -5,20 +5,44 @@ import ( "os/exec" ) +type PsCommandError struct { + msg string +} + +func (e *PsCommandError) Error() string { + return e.msg +} + +type StopCommandError struct { + msg string +} + +func (e *StopCommandError) Error() string { + return e.msg +} + +type DownCommandError struct { + msg string +} + +func (e *DownCommandError) Error() string { + return e.msg +} + // `docker compose ps --format json` を実行し、結果の文字列を返却する。 func Ps(workspaceFolder string) (string, error) { // 現在のディレクトリを記憶 currentDirectory, err := os.Getwd() if err != nil { - return "", err + return "", &PsCommandError{msg: "Failed to get current directory"} } // 元のディレクトリへ戻る defer func() error { err := os.Chdir(currentDirectory) if err != nil { - return err + return &PsCommandError{msg: "Failed to change directory"} } return nil }() @@ -26,7 +50,7 @@ func Ps(workspaceFolder string) (string, error) { // ワークスペースまで移動 err = os.Chdir(workspaceFolder) if err != nil { - return "", err + return "", &PsCommandError{msg: "Failed to change to workspace directory"} } dockerComposePsCommand := exec.Command("docker", "compose", "ps", "--format", "json") @@ -37,11 +61,19 @@ func Ps(workspaceFolder string) (string, error) { // `docker compose -p ${projectName} stop` を実行する。 func Stop(projectName string) error { dockerComposeStopCommand := exec.Command("docker", "compose", "-p", projectName, "stop") - return dockerComposeStopCommand.Start() + err := dockerComposeStopCommand.Start() + if err != nil { + return &StopCommandError{msg: "Failed to execute docker compose stop command"} + } + return nil } // `docker compose -p ${projectName} down` を実行する。 func Down(projectName string) error { dockerComposeDownCommand := exec.Command("docker", "compose", "-p", projectName, "down") - return dockerComposeDownCommand.Start() + err := dockerComposeDownCommand.Start() + if err != nil { + return &DownCommandError{msg: "Failed to execute docker compose down command"} + } + return nil } diff --git a/main.go b/main.go index e9bdb2b..6998194 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( _ "embed" "encoding/json" + "errors" "fmt" "os" "path/filepath" @@ -86,7 +87,8 @@ func main() { if (!util.IsExists(vimrc)) { err := util.CreateFileWithContents(vimrc, additionalVimrc, 0666) if err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Error creating vimrc file: %v\n", err) + os.Exit(1) } fmt.Printf("Generated additional vimrc to: %s\n", vimrc) } @@ -97,7 +99,8 @@ func main() { if (!util.IsExists(runargs)) { err := util.CreateFileWithContents(runargs, runargsContent, 0666) if err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Error creating runargs file: %v\n", err) + os.Exit(1) } fmt.Printf("Generated additional runargs to: %s\n", runargs) } @@ -315,14 +318,22 @@ func main() { workspaceFolder := args[len(args)-1] configFilePath, err := createConfigFile(devcontainerPath, workspaceFolder, configDirForDevcontainer) if err != nil { - fmt.Fprintf(os.Stderr, "Error creating config file: %v\n", err) + if errors.Is(err, os.ErrNotExist) { + fmt.Fprintf(os.Stderr, "Configuration file not found: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, "Error creating config file: %v\n", err) + } os.Exit(1) } // devcontainer を用いたコンテナ立ち上げ err = devcontainer.ExecuteDevcontainer(args, devcontainerPath, vimPath, cdrPath, configFilePath, vimrc) if err != nil { - fmt.Fprintf(os.Stderr, "Error executing devcontainer: %v\n", err) + if errors.Is(err, os.ErrPermission) { + fmt.Fprintf(os.Stderr, "Permission error: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, "Error executing devcontainer: %v\n", err) + } os.Exit(1) } @@ -341,14 +352,22 @@ func main() { // 必要なファイルのダウンロード devcontainerPath, err := tools.InstallStopTools(binDir) if err != nil { - fmt.Fprintf(os.Stderr, "Error installing stop tools: %v\n", err) + if errors.Is(err, os.ErrNotExist) { + fmt.Fprintf(os.Stderr, "Configuration file not found: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, "Error installing stop tools: %v\n", err) + } os.Exit(1) } // devcontainer を用いたコンテナ終了 err = devcontainer.Stop(cCtx.Args().Slice(), devcontainerPath, configDirForDevcontainer) if err != nil { - fmt.Fprintf(os.Stderr, "Error stopping devcontainer: %v\n", err) + if errors.Is(err, os.ErrPermission) { + fmt.Fprintf(os.Stderr, "Permission error: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, "Error stopping devcontainer: %v\n", err) + } os.Exit(1) } @@ -369,14 +388,22 @@ func main() { // 必要なファイルのダウンロード devcontainerPath, err := tools.InstallDownTools(binDir) if err != nil { - fmt.Fprintf(os.Stderr, "Error installing down tools: %v\n", err) + if errors.Is(err, os.ErrNotExist) { + fmt.Fprintf(os.Stderr, "Configuration file not found: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, "Error installing down tools: %v\n", err) + } os.Exit(1) } // devcontainer を用いたコンテナ終了 err = devcontainer.Down(cCtx.Args().Slice(), devcontainerPath, configDirForDevcontainer) if err != nil { - fmt.Fprintf(os.Stderr, "Error downing devcontainer: %v\n", err) + if errors.Is(err, os.ErrPermission) { + fmt.Fprintf(os.Stderr, "Permission error: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, "Error downing devcontainer: %v\n", err) + } os.Exit(1) } @@ -389,7 +416,11 @@ func main() { fmt.Printf("Remove configuration file: `%s`\n", configDir) err = os.RemoveAll(configDir) if err != nil { - fmt.Fprintf(os.Stderr, "Error removing configuration file: %v\n", err) + if errors.Is(err, os.ErrPermission) { + fmt.Fprintf(os.Stderr, "Permission error: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, "Error removing configuration file: %v\n", err) + } os.Exit(1) } @@ -665,12 +696,20 @@ func main() { // 削除処理 err := os.RemoveAll(configDirForDocker) if err != nil { - fmt.Fprintf(os.Stderr, "Error removing docker config directory: %v\n", err) + if errors.Is(err, os.ErrPermission) { + fmt.Fprintf(os.Stderr, "Permission error: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, "Error removing docker config directory: %v\n", err) + } os.Exit(1) } err = os.RemoveAll(configDirForDevcontainer) if err != nil { - fmt.Fprintf(os.Stderr, "Error removing devcontainer config directory: %v\n", err) + if errors.Is(err, os.ErrPermission) { + fmt.Fprintf(os.Stderr, "Permission error: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, "Error removing devcontainer config directory: %v\n", err) + } os.Exit(1) } @@ -693,7 +732,11 @@ func main() { // Features の一覧をダウンロード err := oras.Pull("ghcr.io/devcontainers/index", "latest", appCacheDir) if err != nil { - fmt.Fprintf(os.Stderr, "Error updating index: %v\n", err) + if errors.Is(err, os.ErrNotExist) { + fmt.Fprintf(os.Stderr, "Index file not found: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, "Error updating index: %v\n", err) + } os.Exit(1) } @@ -709,7 +752,11 @@ func main() { Action: func(cCtx *cli.Context) error { err := tools.SelfUpdate() if err != nil { - fmt.Fprintf(os.Stderr, "Error updating devcontainer.vim: %v\n", err) + if errors.Is(err, os.ErrPermission) { + fmt.Fprintf(os.Stderr, "Permission error: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, "Error updating devcontainer.vim: %v\n", err) + } os.Exit(1) } return nil @@ -730,7 +777,11 @@ func main() { // アプリ実行 err := devcontainerVimArgProcess.Run(os.Args) if err != nil { - fmt.Fprintf(os.Stderr, "Error running devcontainer.vim: %v\n", err) + if errors.Is(err, os.ErrPermission) { + fmt.Fprintf(os.Stderr, "Permission error: %v\n", err) + } else { + fmt.Fprintf(os.Stderr, "Error running devcontainer.vim: %v\n", err) + } os.Exit(1) } } @@ -742,6 +793,9 @@ func createConfigFile(devcontainerPath string, workspaceFolder string, configDir // devcontainer の設定ファイルパス取得 configFilePath, err := devcontainer.GetConfigurationFilePath(devcontainerPath, workspaceFolder) if err != nil { + if errors.Is(err, os.ErrNotExist) { + return "", fmt.Errorf("configuration file not found: %w", err) + } return "", err } @@ -752,6 +806,9 @@ func createConfigFile(devcontainerPath string, workspaceFolder string, configDir // 設定管理フォルダに JSON を配置 mergedConfigFilePath, err := util.CreateConfigFileForDevcontainer(configDirForDevcontainer, workspaceFolder, configFilePath, additionalConfigurationFilePath) if err != nil { + if errors.Is(err, os.ErrPermission) { + return "", fmt.Errorf("permission error: %w", err) + } return "", err }