diff --git a/README.md b/README.md
index 6abe044..bf5b9f5 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
+
+
# Corteca Developer Toolkit
@@ -69,16 +71,14 @@ supported:
Devices and the sequences to run on them are configured in `corteca.yaml` and
can be targeted by name when running `corteca exec`.
-
-
## Prerequisites
-| Requirement | Version | Notes |
-| ----------- | -------- | ---------------------------------------------- |
-| Go | ≥ 1.21 | Required to build from source |
-| Docker | ≥ 23.0 | Required to build application container images |
-| Docker BuildKit | ≥ 0.11 | Required for `docker build --output` |
-| make | any | Used to drive the build and install targets |
+| Requirement | Version | Notes |
+|------------------|---------|------------------------------------------------|
+| Go | ≥ 1.21 | Required to build from source |
+| Docker | ≥ 23.0 | Required to build application container images |
+| Docker BuildKit | ≥ 0.11 | Required for `docker build --output` |
+| make | any | Used to drive the build and install targets |
## Build
@@ -87,13 +87,13 @@ can be targeted by name when running `corteca exec`.
To build locally, you need to have a Go toolchain installed. Clone the repository and run:
```bash
-$ make
+make
```
The compiled binary is placed in the `dist/` directory. You can also build packages for your specific platform. The available `make` targets are `deb`, `rpm`, `osx` and `msix`:
```bash
-$ make rpm
+make rpm
```
### Build using Docker
@@ -101,7 +101,7 @@ $ make rpm
If you do not have a local Go toolchain, you can build entirely inside Docker (BuildKit is required). The following command builds all supported packages:
```bash
-$ docker build --output ./dist .
+docker build --output ./dist .
```
#### Selecting the Packages to Build
@@ -109,19 +109,19 @@ $ docker build --output ./dist .
By default, packages are created for all architectures (`arm64` and `amd64`). You can select the architectires to build for by setting the `ARCH` build-time variable to the space-separated list of architectures to build for. For example, to only build packages for the `amd64` architecture run:
```bash
-$ docker build --build-arg ARCH="amd64" --output ./dist .
+docker build --build-arg ARCH="amd64" --output ./dist .
```
The package types created by default are `deb`, `rpm`, `osx` and `msix`. To only build selected package types, set the `PACKAGE` build-time variable to the space-separated list of package types to build. The following command only builds `deb` and `rpm` packages on all architectures:
```bash
-$ docker build --build-arg PACKAGE="deb rpm" --output ./dist .
+docker build --build-arg PACKAGE="deb rpm" --output ./dist .
```
By setting both the `ARCH` and `PACKAGE` build-time variables, you can further limit the build scope to specific packages. For example, to only build the `deb` package for the `amd64` architecture, run:
```bash
-$ docker build --build-arg ARCH="amd64" --build-arg PACKAGE="deb" --output ./dist .
+docker build --build-arg ARCH="amd64" --build-arg PACKAGE="deb" --output ./dist .
```
#### Installing Docker BuildKit
@@ -129,7 +129,7 @@ $ docker build --build-arg ARCH="amd64" --build-arg PACKAGE="deb" --output ./dis
On Docker Engine < 23.0, BuildKit must be enabled manually. On Ubuntu 22.04:
```bash
-$ sudo apt-get install docker-buildx-plugin
+sudo apt-get install docker-buildx-plugin
```
Then either prefix each `docker build` invocation with `DOCKER_BUILDKIT=1`, or
@@ -145,13 +145,13 @@ the `/usr/bin` folder and the default configuration files to the `/etc/corteca`
folder:
```bash
-$ sudo make install
+sudo make install
```
To remove a previous manual installation
```bash
-$ sudo make uninstall
+sudo make uninstall
```
You can customize the destination folder by overriding the `$DESTDIR`
@@ -167,8 +167,8 @@ $ DESTDIR=~/.local/share make install
If you are using debian/ubuntu or redhat-based distributions, you can create a relevant package and let your package manager handle installation. E.g. for ubuntu:
```bash
-$ make deb
-$ make rpm
+make deb
+make rpm
```
## Getting Started
@@ -177,10 +177,10 @@ The fastest way to get up and running is to create a project, build it, and
publish it to a local registry in three commands:
```bash
-$ corteca create my-app # scaffold a new application project
-$ cd my-app
-$ corteca build aarch64 # build an OCI image for aarch64
-$ corteca publish localRegistry # push it to a local OCI registry
+corteca create my-app # scaffold a new application project
+cd my-app
+corteca build aarch64 # build an OCI image for aarch64
+corteca publish localRegistry # push it to a local OCI registry
```
For a step-by-step walkthrough — including how to configure a device target and
diff --git a/cmd/build.go b/cmd/build.go
index 515ae2f..420c11f 100644
--- a/cmd/build.go
+++ b/cmd/build.go
@@ -96,7 +96,7 @@ func doBuildApp(selectedArchitecture string) {
if rootfsPath == "" {
assertOperation(fmt.Sprintf("building '%s'", selectedArchitecture), builder.BuildRootFS(projectRoot, rootfsBuildPath, settings, config.App, config.Build))
} else {
- fsutil.ExtractTarball(rootfsPath, rootfsBuildPath)
+ assertOperation(fmt.Sprintf("extracting rootfs from '%s'", rootfsPath), fsutil.ExtractTarball(rootfsPath, rootfsBuildPath))
}
// STEP 2: validate rootfs
diff --git a/cmd/config.go b/cmd/config.go
index 83103f7..e165072 100644
--- a/cmd/config.go
+++ b/cmd/config.go
@@ -113,7 +113,7 @@ func doGetConfigValue(key string) {
assertOperation("retrieving config value", err)
enc := yaml.NewEncoder(os.Stdout)
enc.SetIndent(configuration.YamlIndentation)
- enc.Encode(field)
+ assertOperation("encoding config value", enc.Encode(field))
}
func doSetConfigValue(key, value string, append bool) {
@@ -144,7 +144,7 @@ func doEvalConfigValue(expr string) {
field := configuration.T(expr)
enc := yaml.NewEncoder(os.Stdout)
enc.SetIndent(configuration.YamlIndentation)
- enc.Encode(field.String())
+ assertOperation("encoding config value", enc.Encode(field.String()))
}
func validConfigArgsFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
diff --git a/cmd/doc.go b/cmd/doc.go
index 36107a2..2b1e001 100644
--- a/cmd/doc.go
+++ b/cmd/doc.go
@@ -48,7 +48,7 @@ func doGenerateDocumentation(cmdNames []string) {
continue
}
cmdFile := createMdFile(cmd)
- defer cmdFile.Close()
+ defer func() { _ = cmdFile.Close() }()
err = generateMarkdown(cmd, cmdFile, false)
assertOperation("generating documentation for corteca commands", err)
}
@@ -67,7 +67,7 @@ func doGenerateDocumentation(cmdNames []string) {
index--
cmdFile := createMdFile(cmd)
- defer cmdFile.Close()
+ defer func() { _ = cmdFile.Close() }()
err = generateMarkdown(cmd, cmdFile, false)
assertOperation("generating documentation for specific corteca command(s)", err)
}
@@ -82,30 +82,38 @@ func doGenerateDocumentation(cmdNames []string) {
if len(notMatchedCmds) == 0 {
fmt.Println("Cortecacli's commands documentation has generated successfuly!")
} else {
- fmt.Fprintf(os.Stdout, "The following commands are not part of cortecacli: %s\n", notMatchedCmds)
+ _, _ = fmt.Fprintf(os.Stdout, "The following commands are not part of cortecacli: %s\n", notMatchedCmds)
}
} else {
- fmt.Fprintf(os.Stdout, "No command documentation was generated\nThe following commands are not part of cortecacli: %s\n", notMatchedCmds)
+ _, _ = fmt.Fprintf(os.Stdout, "No command documentation was generated\nThe following commands are not part of cortecacli: %s\n", notMatchedCmds)
}
}
func generateMarkdown(cmd *cobra.Command, cmdFile *os.File, isSubcmd bool) error {
// Write command usage
if isSubcmd {
- _, writeToFileErr = cmdFile.WriteString(fmt.Sprintf("### Corteca %s %s\n\n%s\n\n", cmd.Parent().Name(), cmd.Name(), cmd.Long))
+ _, writeToFileErr = fmt.Fprintf(cmdFile, "### Corteca %s %s\n\n%s\n\n", cmd.Parent().Name(), cmd.Name(), cmd.Long)
} else {
- _, writeToFileErr = cmdFile.WriteString(fmt.Sprintf("## Corteca %s\n\n%s\n\n", cmd.Name(), cmd.Long))
+ _, writeToFileErr = fmt.Fprintf(cmdFile, "## Corteca %s\n\n%s\n\n", cmd.Name(), cmd.Long)
}
if writeToFileErr != nil {
return writeToFileErr
}
- cmdFile.WriteString(fmt.Sprintf("### Usage:\n\n```\n%s\n```\n\n", cmd.UseLine()))
- cmdFile.WriteString(fmt.Sprintf("### Flags:\n\n```\n%s%s\n```\n\n", cmd.InheritedFlags().FlagUsages(), cmd.LocalFlags().FlagUsages()))
- cmdFile.WriteString(fmt.Sprintf("### Example:\n\n```\n%s\n```\n\n", cmd.Example))
+ if _, err := fmt.Fprintf(cmdFile, "### Usage:\n\n```\n%s\n```\n\n", cmd.UseLine()); err != nil {
+ return err
+ }
+ if _, err := fmt.Fprintf(cmdFile, "### Flags:\n\n```\n%s%s\n```\n\n", cmd.InheritedFlags().FlagUsages(), cmd.LocalFlags().FlagUsages()); err != nil {
+ return err
+ }
+ if _, err := fmt.Fprintf(cmdFile, "### Example:\n\n```\n%s\n```\n\n", cmd.Example); err != nil {
+ return err
+ }
// Append subcommands to parent command's documentation, if any
if len(cmd.Commands()) > 0 {
- cmdFile.WriteString("## Subcommands:\n\n")
+ if _, err := fmt.Fprintf(cmdFile, "## Subcommands:\n\n"); err != nil {
+ return err
+ }
for _, subCmd := range cmd.Commands() {
// Recursive call for nested subcommands
err := generateMarkdown(subCmd, cmdFile, true)
@@ -113,7 +121,9 @@ func generateMarkdown(cmd *cobra.Command, cmdFile *os.File, isSubcmd bool) error
}
}
- cmdFile.WriteString("\n")
+ if _, err := fmt.Fprintf(cmdFile, "\n"); err != nil {
+ return err
+ }
return nil
}
diff --git a/cmd/exec.go b/cmd/exec.go
index 1c0bf36..d54d2aa 100644
--- a/cmd/exec.go
+++ b/cmd/exec.go
@@ -33,7 +33,7 @@ var logFile string
var publishTargetName string
func init() {
- execCmd.RegisterFlagCompletionFunc("artifact", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ _ = execCmd.RegisterFlagCompletionFunc("artifact", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"tar.gz, tar"}, cobra.ShellCompDirectiveFilterFileExt
})
rootCmd.AddCommand(execCmd)
diff --git a/cmd/publish.go b/cmd/publish.go
index ecc0a94..3f8f44e 100644
--- a/cmd/publish.go
+++ b/cmd/publish.go
@@ -21,11 +21,6 @@ import (
"github.com/spf13/cobra"
)
-const (
- ociSuffix = "oci"
- rootfsSuffix = "rootfs"
- artifactNotFoundMessage = "No build artifact found for [%s,%s]"
-)
var publishCmd = &cobra.Command{
Use: "publish TARGET",
@@ -49,7 +44,7 @@ type RegistryConfig struct {
func init() {
publishCmd.PersistentFlags().BoolVar(&skipLocalConfig, "global", false, "Affect global config & ignore any project-local configuration")
publishCmd.PersistentFlags().StringVarP(&artifact, "artifact", "a", "", "Specify the path to a an artifact to publish")
- publishCmd.RegisterFlagCompletionFunc("artifact", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ _ = publishCmd.RegisterFlagCompletionFunc("artifact", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"tar.gz"}, cobra.ShellCompDirectiveFilterFileExt
})
rootCmd.AddCommand(publishCmd)
@@ -65,19 +60,19 @@ func doPublishApp(targetName string, wait bool) {
switch target.Method {
case "listen":
serverConfig := configuration.HttpServerEndpoint{}
- target.Decode(&serverConfig)
+ assertOperation("decoding publish config", target.Decode(&serverConfig))
handleListen(serverConfig, wait)
case "put":
clientConfig := configuration.HttpClientEndpoint{}
- target.Decode(&clientConfig)
+ assertOperation("decoding publish config", target.Decode(&clientConfig))
handlePut(clientConfig, artifact)
case "push":
clientConfig := configuration.HttpClientEndpoint{}
- target.Decode(&clientConfig)
+ assertOperation("decoding publish config", target.Decode(&clientConfig))
handlePush(clientConfig, artifact)
case "registry-v2":
registryConfig := RegistryConfig{}
- target.Decode(®istryConfig)
+ assertOperation("decoding publish config", target.Decode(®istryConfig))
handleRegistry(registryConfig, wait)
default:
failOperation(fmt.Sprintf("unknown publish method '%v'", target.Method))
@@ -93,7 +88,7 @@ func handleListen(target configuration.HttpServerEndpoint, wait bool) {
assertOperation("starting server", err)
if wait {
waitForInterruptSignal()
- srv.Shutdown(context.Background())
+ assertOperation("shutting down server", srv.Shutdown(context.Background()))
} else {
fmt.Printf("Serving %v on %v\n", serverRoot, u.String())
}
diff --git a/cmd/root.go b/cmd/root.go
index b8acbd3..0717185 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -61,20 +61,22 @@ func init() {
rootCmd.SetVersionTemplate("{{.Short}} v{{.Version}}\n")
rootCmd.CompletionOptions.HiddenDefaultCmd = true
rootCmd.PersistentFlags().StringArrayVarP(&configOverrides, "config", "c", []string{}, "Override a configuration value in the form of a 'key=value' pair")
- rootCmd.RegisterFlagCompletionFunc("config", validConfigArgsFunc)
+ _ = rootCmd.RegisterFlagCompletionFunc("config", validConfigArgsFunc)
rootCmd.PersistentFlags().StringVarP(&systemConfigRoot, "configRoot", "r", systemConfigRoot, "Override configuration root folder")
- rootCmd.RegisterFlagCompletionFunc("configRoot", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ _ = rootCmd.RegisterFlagCompletionFunc("configRoot", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveFilterDirs
})
rootCmd.PersistentFlags().StringVarP(&projectRoot, "projectRoot", "C", projectRoot, "Specify project root folder")
- rootCmd.RegisterFlagCompletionFunc("projectRoot", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ _ = rootCmd.RegisterFlagCompletionFunc("projectRoot", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveFilterDirs
})
rootCmd.PersistentFlags().BoolVar(&tui.DisableColoredOutput, "no-color", false, "Disables colored stdout|stderr output")
}
func Execute() {
- rootCmd.Execute()
+ if err := rootCmd.Execute(); err != nil {
+ os.Exit(1)
+ }
}
func readLocalConfiguration() {
@@ -206,16 +208,6 @@ func requireProjectContext() {
configuration.GetCmdContext().Build = &config.Build
}
-func getAppNameFromArtifact(artifactPath string) string {
- artifactName := filepath.Base(artifactPath)
- splitedArtifactName := strings.SplitN(artifactName, "-", 2)
- // If artifact name doesn't contain hyphens we consider the form "appName.tar.gz", else we consider App.Name the string up to the first hyphen.
- if len(splitedArtifactName) == 1 {
- return strings.TrimSuffix(splitedArtifactName[0], ".tar.gz")
- } else {
- return splitedArtifactName[0]
- }
-}
func requireBuildArtifact() {
if artifact != "" {
diff --git a/data/templates/c/.template-info.yaml b/data/templates/c/.template-info.yaml
index f91aeb6..dd5a0e5 100644
--- a/data/templates/c/.template-info.yaml
+++ b/data/templates/c/.template-info.yaml
@@ -1,6 +1,6 @@
name: "c"
description: "An application template for the C language."
dependencies:
- compile: [ gcc, make, g++ ]
- runtime: [ json-c ]
+ compile: [cc, make, g++]
+ runtime: [json-c]
base: _base
diff --git a/data/templates/cpp/.template-info.yaml b/data/templates/cpp/.template-info.yaml
index 8d36e24..9c71500 100644
--- a/data/templates/cpp/.template-info.yaml
+++ b/data/templates/cpp/.template-info.yaml
@@ -1,6 +1,6 @@
name: "cpp"
description: "An application template for the C++ language."
dependencies:
- compile: [ g++, make ]
- runtime: [ libstdc++, json-c ]
+ compile: [g++, make]
+ runtime: [libstdc++, json-c]
base: _base
diff --git a/data/templates/go/.template-info.yaml b/data/templates/go/.template-info.yaml
index 7ef3b0b..9bc13ed 100644
--- a/data/templates/go/.template-info.yaml
+++ b/data/templates/go/.template-info.yaml
@@ -1,6 +1,6 @@
name: "go"
description: "An application template for the Go language."
dependencies:
- compile: [ go, make ]
- runtime: [ json-c ]
+ compile: [go, make]
+ runtime: [json-c]
base: _base
diff --git a/doc/Configuration.md b/doc/Configuration.md
index 977cc35..ff3b499 100644
--- a/doc/Configuration.md
+++ b/doc/Configuration.md
@@ -119,7 +119,7 @@ build:
args: [ , … ] # Arguments passed to the QEMU helper
```
-### Fields
+### Build Fields
| Field | Type | Default | Description |
| ------- | ------ | --------- | ------------- |
@@ -254,13 +254,13 @@ publish:
key: # TLS private key (optional, for HTTPS)
```
-| Field | Type | Required | Description |
-| ------- | ------ | ---------- | ------------- |
-| `addr` | string (template) | Yes | Address the local registry listens on the form of `[:]` (no schema prefix) |
-| `namespace` | string (template) | Yes | Repository path within the registry (e.g., `myapp` or `org/myapp`). Combined with `reference` to form the image path `/:`. |
-| `reference` | string (template) | Yes | Image tag or digest reference (e.g., `1.0.0` or `latest`). Template expressions such as `${ .app.version }` are commonly used here. |
-| `certificate` | string (template) | No | Path to a PEM-encoded TLS certificate. Enables HTTPS when set together with `key`. |
-| `key` | string (template) | No | Path to a PEM-encoded TLS private key. |
+| Field | Type | Required | Description |
+| -------------- | ----------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `addr` | string (template) | Yes | Address the local registry listens on the form of `[:]` (no schema prefix) |
+| `namespace` | string (template) | Yes | Repository path within the registry (e.g., `myapp` or `org/myapp`). Combined with `reference` to form the image path `/:`. |
+| `reference` | string (template) | Yes | Image tag or digest reference (e.g., `1.0.0` or `latest`). Template expressions such as `${ .app.version }` are commonly used here. |
+| `certificate` | string (template) | No | Path to a PEM-encoded TLS certificate. Enables HTTPS when set together with `key`. |
+| `key` | string (template) | No | Path to a PEM-encoded TLS private key. |
---
@@ -275,10 +275,10 @@ meaningful.
The fields shared by all device types are:
-| Field | Type | Description |
-| ------- | ------ | ------------- |
-| `addr` | string (template) | **(Required)** Connection URL. Its scheme (`ssh://`, `cwmp://`, `cwmps://`) selects the device type. |
-| `architecture` | string | Architecture identifier matching an entry in `build.architectures`. |
+| Field | Type | Description |
+| --- | --- | --- |
+| `addr` | string (template) | **(Required)** Connection URL. Its scheme (`ssh://`, `cwmp://`, `cwmps://`) selects the device type. |
+| `architecture` | string | Architecture identifier matching an entry in `build.architectures`. |
---
@@ -299,13 +299,13 @@ devices:
privateKeyFile: # Path to a PEM-encoded private key
```
-| Field | Type | Required | Description |
-| ------- | ------ | -------- | ------------- |
-| `addr` | string (template) | Yes | SSH connection URL. Must use the `ssh://` scheme. Username and password may be embedded (e.g., `ssh://user:pass@host`). |
-| `username` | string (template) | No | SSH username. Overrides any username embedded in `addr`. |
-| `password` | string (template) | No | SSH password. Falls back to the password in `addr`, then prompts interactively if absent. |
-| `password2` | string (template) | No | Secondary (escalation) password required by certain device firmware (e.g., for Quagga deactivation). |
-| `privateKeyFile` | string (template) | No | Path to a PEM-encoded private key file. When provided, public-key authentication is attempted first. |
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `addr` | string (template) | Yes | SSH connection URL. Must use the `ssh://` scheme. Username and password may be embedded (e.g., `ssh://user:pass@host`). |
+| `username` | string (template) | No | SSH username. Overrides any username embedded in `addr`. |
+| `password` | string (template) | No | SSH password. Falls back to the password in `addr`, then prompts interactively if absent. |
+| `password2` | string (template) | No | Secondary (escalation) password required by certain device firmware (e.g., for Quagga deactivation). |
+| `privateKeyFile` | string (template) | No | Path to a PEM-encoded private key file. When provided, public-key authentication is attempted first. |
---
@@ -371,7 +371,7 @@ sequences:
| Field | Type | Default | Description |
| ------- | ------ | --------- | ------------- |
| `cmd` | string (template) | — | **(Required)** Command to execute. Interpreted differently per device type; see below. |
-| `delay` | string (duration) | `0` | Time to wait after the step (or each retry) completes. Refer to [this](https://pkg.go.dev/time#ParseDuration) for syntax. |
+| `delay` | string (duration) | `0` | Time to wait after the step (or each retry) completes. Refer to [this](https://pkg.go.dev/time#ParseDuration) for syntax. |
| `timeout` | string (duration) | 5 minutes | Maximum execution time. If exceeded, the step is considered failed. Same syntax as `delay`. |
| `retries` | uint | `0` | How many additional attempts to make if the step fails. |
| `ignoreFailure` | bool | `false` | When `true`, a failing step does not abort the sequence. |
@@ -434,7 +434,7 @@ templates:
Each key is a path to a Go template file (relative to the `.corteca` directory inside the project), and the corresponding value is the output path where the rendered result should be written.
-### Fields
+### Templates Fields
| Field | Description |
| ------- | ------------- |
@@ -456,7 +456,7 @@ ${ ["":].[:[:]] }
```
| Part | Description |
-|------|-------------|
+| ------ | ------------- |
| `"":` | Optional literal string prepended to each resolved value. |
| `.` | Dot-separated path into the context (e.g., `.app.name`, `.env.HOME`). |
| `:` | For **map** fields: separator between each key and its value (default: space). |
@@ -465,7 +465,7 @@ ${ ["":].[:[:]] }
Examples:
| Expression | Result |
-|------------|--------|
+| ------ | ------------- |
| `${ .app.name }` | Value of `app.name` |
| `${ .app.version }` | Value of `app.version` |
| `${ "v":.app.version }` | `v` prepended to `app.version` (e.g., `v1.2.0`) |
@@ -481,7 +481,7 @@ Template fields can reference other template fields; circular references will ca
### `.app` — Application Settings
| Field | Type | Description |
-|-------|------|-------------|
+| ------- | ------ | ------------- |
| `.app.name` | string | Application name. |
| `.app.author` | string | Application author. |
| `.app.version` | string | Application version string. |
@@ -494,7 +494,7 @@ Template fields can reference other template fields; circular references will ca
### `.build` — Build Settings
| Field | Type | Description |
-|-------|------|-------------|
+| ------- | ------ | ------------- |
| `.build.default` | string | Default target architecture name. |
| `.build.options.outputType` | string | Build output type (`rootfs`, `oci`, or `docker`). |
| `.build.options.debug` | bool | Whether debug mode is enabled. |
@@ -507,14 +507,14 @@ Template fields can reference other template fields; circular references will ca
### `.arch` and `.platform` — Current Architecture
| Field | Type | Description |
-|-------|------|-------------|
+| ------- | ------ | ------------- |
| `.arch` | string | Name of the target architecture currently being built (e.g., `aarch64`). Set during `build`, `regen`, and `exec`. |
| `.platform` | string | Docker platform string for the current architecture (e.g., `linux/arm64`). Set alongside `.arch`. |
### `.artifact` — Build Artifact
| Field | Type | Description |
-|-------|------|-------------|
+| ------- | ------ | ------------- |
| `.artifact` | string | Absolute path to the current build artifact (e.g., `/project/dist/app-1.0-aarch64-oci.tar`). Set during `publish` and `exec`. |
### `.publish` — Active Publish Target
@@ -522,7 +522,7 @@ Template fields can reference other template fields; circular references will ca
Populated when a publish target is selected (during `publish` or `exec` with a publish target).
| Field | Type | Description |
-|-------|------|-------------|
+| ------- | ------ | ------------- |
| `.publish.name` | string | Alias name of the active publish target. |
| `.publish.method` | string | Delivery method of the active publish target. |
@@ -531,7 +531,7 @@ Populated when a publish target is selected (during `publish` or `exec` with a p
Populated when a device is selected (during `exec`).
| Field | Type | Description |
-|-------|------|-------------|
+| ------- | ------ | ------------- |
| `.device.name` | string | Alias name of the active device. |
| `.device.addr` | string (template) | Connection URL of the device. |
| `.device.architecure` | string | Architecture identifier of the device. |
@@ -539,7 +539,7 @@ Populated when a device is selected (during `exec`).
### `.env` — Host Environment Variables
| Field | Type | Description |
-|-------|------|-------------|
+| ------- | ------ | ------------- |
| `.env` | map | All host environment variables at the time the command was invoked. Access individual variables as `.env.` (e.g., `.env.HOME`, `.env.PATH`). |
---
diff --git a/doc/GettingStarted.md b/doc/GettingStarted.md
index 416bf92..b3868b3 100644
--- a/doc/GettingStarted.md
+++ b/doc/GettingStarted.md
@@ -1,10 +1,11 @@
# Getting Started
## Install corteca cli
+
To install or build from source, you can follow the [relevant instructions](../README.md#install) in the main README file. To make sure everything is in place, run the following command in any folder.
-```bash
-$ corteca config get
+```shell
+corteca config get
```
You should see your default system-wide configuration.
@@ -18,8 +19,8 @@ on how to write templates, refer to the [relevant guide](templates.md). We will
Change to the parent folder where you want your application directory to be
created and run the following command:
-```bash
-$ corteca create test_app
+```shell
+corteca create test_app
```
Follow the prompt instructions to populate the application settings. A
@@ -67,20 +68,22 @@ generated.
Corteca cli provides a publish functionality to help users upload their produced artifacts in an OCI registry. There are some registries set up by default in corteca that can be used as a template from the user to set up the target registry. You can check the current configuration for publish using
-```bash
-$ corteca config get publish
+```shell
+corteca config get publish
```
#### Publish in corteca local registry
+
User can use the already set up local registry.
`corteca publish localRegistry armv7l`
-Corteca will set up a local oci registry and will upload the artifact. (check with https://localhost:8080/v2/_catalog). If this registry is needed to be visible outside the host device, publicURL needs to be changed to the device IP.
+Corteca will set up a local oci registry and will upload the artifact. (check with ). If this registry is needed to be visible outside the host device, publicURL needs to be changed to the device IP.
`corteca config set publish.localRegistry.publicURL http://192.168.18.2:8080`
#### Publish in custom registry
+
User can add his own registry using *corteca config*
`corteca config add publish "{myregistry: { addr: https://my-registry.com, auth: basic, username: user1, password: pass1, method: push}}"`
@@ -94,7 +97,8 @@ After the registry is set up, the application can be uploaded.
For more info on corteca publish check [corteca publish](reference/corteca_publish.md) guide.
## Configuring application
-Application configuration is noted in the corteca.yaml file placed in application's folder when created. The *app* section -related to the create command- specifies app settings you can alter during the build process.
+
+Application configuration is noted in the corteca.yaml file placed in application's folder when created. The *app* section -related to the create command- specifies app settings you can alter during the build process.
The complete set of settings for Corteca Cli is created by combining this file with /etc/corteca/corteca.yaml.
Corteca cli provides methods to show or edit these settings using the *config* method. You can also check or edit the settings values using the auto complete of Corteca cli by pressing key twice like in a shell.
@@ -108,6 +112,4 @@ To set a value
`corteca config set app.version 1.0.1`
-
-
For more detailed guide on config get/set/add please refer to [corteca config](reference/corteca_config.md) guide.
diff --git a/doc/USAGE.md b/doc/USAGE.md
index 873b5d2..e5a8db7 100644
--- a/doc/USAGE.md
+++ b/doc/USAGE.md
@@ -9,9 +9,9 @@
- [`corteca build`](reference/corteca_build.md)
- [`corteca config`](reference/corteca_config.md)
- - [`corteca config add`](reference/corteca_config_add.md)
- - [`corteca config get`](reference/corteca_config_get.md)
- - [`corteca config set`](reference/corteca_config_set.md)
+ - [`corteca config add`](reference/corteca_config_add.md)
+ - [`corteca config get`](reference/corteca_config_get.md)
+ - [`corteca config set`](reference/corteca_config_set.md)
- [`corteca create`](reference/corteca_create.md)
- [`corteca exec`](reference/corteca_exec.md)
- [`corteca publish`](reference/corteca_publish.md)
diff --git a/doc/reference/corteca_create.md b/doc/reference/corteca_create.md
index 62ff129..c92734f 100644
--- a/doc/reference/corteca_create.md
+++ b/doc/reference/corteca_create.md
@@ -1,6 +1,6 @@
# `create`
-The create command is used to generate a new application skeleton/template based on a selected programming language (see details about templates [here](../USAGE.md#application-templates)). This command can be configured interactively or through specified flags.
+The create command is used to generate a new application skeleton/template based on a selected programming language [see details about templates](../USAGE.md#application-templates). This command can be configured interactively or through specified flags.
## Usage
diff --git a/doc/templates.md b/doc/templates.md
index b627d1e..2616a27 100644
--- a/doc/templates.md
+++ b/doc/templates.md
@@ -2,7 +2,7 @@
`corteca` scans for available templates in a `templates/` folder that resides in the global config folder (defaults to `$HOME/.config/corteca`). You can override the global config folder using the `--configRoot` flag.
-Each subfolder containing a `.template-info.yaml` file will be treated as a template and will be rendered with all configuration variables available for rendering. For more information, see [configuration section](#configuration) above.
+Each subfolder containing a `.template-info.yaml` file will be treated as a template and will be rendered with all configuration variables available for rendering. For more information, see the configuration section above.
Here is an overview of the content of `.template-info.yaml`
diff --git a/internal/configuration/configuration.go b/internal/configuration/configuration.go
index 07ce055..a89ef8f 100755
--- a/internal/configuration/configuration.go
+++ b/internal/configuration/configuration.go
@@ -359,9 +359,8 @@ func (conf *Settings) ReadFromFile(path string) error {
if err != nil {
return err
}
- defer in.Close()
+ defer func() { _ = in.Close() }()
if err = ReadYamlInto(conf, in); err != nil {
- in.Close()
return err
}
@@ -810,7 +809,7 @@ func readTemplateInfo(fullPath string) (info TemplateInfo, err error) {
if err != nil {
return TemplateInfo{}, err
}
- defer yamlData.Close()
+ defer func() { _ = yamlData.Close() }()
if err = ReadYamlInto(&info, yamlData); err != nil {
return
diff --git a/internal/configuration/sequence.go b/internal/configuration/sequence.go
index 6e76064..ad0ae25 100644
--- a/internal/configuration/sequence.go
+++ b/internal/configuration/sequence.go
@@ -129,7 +129,9 @@ func (sm *SequenceMap) executeSequenceSteps(executor CommandExecutor, seqName st
// TODO: provide option to suppress output
tui.SetOutputColor(tui.CBlue, os.Stdout)
enc := yaml.NewEncoder(os.Stdout)
- enc.Encode(res)
+ if err := enc.Encode(res); err != nil {
+ return fmt.Errorf("failed to encode step result: %w", err)
+ }
tui.ResetOutputColor(os.Stdout)
}
return nil
diff --git a/internal/configuration/templating/templating.go b/internal/configuration/templating/templating.go
index 09688df..ade8fe1 100644
--- a/internal/configuration/templating/templating.go
+++ b/internal/configuration/templating/templating.go
@@ -94,7 +94,7 @@ func RenderTemplateToFile(fs afero.Fs, srcFile, destFile string, context any) er
if err != nil {
return err
}
- defer out.Close()
+ defer func() { _ = out.Close() }()
if err := fileTmpl.Execute(out, context); err != nil {
return err
diff --git a/internal/configuration/templating/templating_test.go b/internal/configuration/templating/templating_test.go
index f9bd9bb..8455159 100644
--- a/internal/configuration/templating/templating_test.go
+++ b/internal/configuration/templating/templating_test.go
@@ -9,6 +9,7 @@ import (
"testing"
"github.com/spf13/afero"
+ "github.com/stretchr/testify/require"
)
func TestRenderTemplateFile(t *testing.T) {
@@ -18,8 +19,8 @@ func TestRenderTemplateFile(t *testing.T) {
templateContent := "Hello, {{.Name}}!"
templateDir := "/project"
templateFile := "template.txt"
- fs.MkdirAll(templateDir, 0755)
- afero.WriteFile(fs, filepath.Join(templateDir, templateFile), []byte(templateContent), 0644)
+ require.NoError(t, fs.MkdirAll(templateDir, 0755))
+ require.NoError(t, afero.WriteFile(fs, filepath.Join(templateDir, templateFile), []byte(templateContent), 0644))
testCases := []struct {
name string
diff --git a/internal/device/cwmp/cwmpdevice.go b/internal/device/cwmp/cwmpdevice.go
index 015845f..847d8f5 100644
--- a/internal/device/cwmp/cwmpdevice.go
+++ b/internal/device/cwmp/cwmpdevice.go
@@ -197,9 +197,9 @@ func (c *CWMPDevice) sendConnectionRequest(cpe *configuration.HttpClientEndpoint
if err != nil {
return fmt.Errorf("error sending connection request: %w", err)
}
- defer resp.Body.Close()
- c.log.Write(fmt.Appendf([]byte(""), "[%s] Connection Request (response: %s)\n", time.Now().Format(time.DateTime), resp.Status))
- io.Copy(io.Discard, resp.Body)
+ defer func() { _ = resp.Body.Close() }()
+ _, _ = c.log.Write(fmt.Appendf([]byte(""), "[%s] Connection Request (response: %s)\n", time.Now().Format(time.DateTime), resp.Status))
+ _, _ = io.Copy(io.Discard, resp.Body)
tui.LogNormal("Connection Request sent to %s; status code: %d", url.String(), resp.StatusCode)
return nil
}
@@ -227,9 +227,10 @@ func (d *CWMPDevice) initServer(config *configuration.HttpServerEndpoint) error
// Run server in a goroutine
go func() {
var err error
- if u.Scheme == "http" {
+ switch u.Scheme {
+ case "http":
err = d.server.ListenAndServe()
- } else if u.Scheme == "https" {
+ case "https":
err = d.server.ListenAndServeTLS(config.Certificate.String(), config.Key.String())
}
if err != nil && err != http.ErrServerClosed {
@@ -248,7 +249,7 @@ func (d *CWMPDevice) handleHTTPRequest(w http.ResponseWriter, r *http.Request) {
return
}
- d.log.Write(fmt.Appendf([]byte(""), "[%s] IN: %s %s %s\n",
+ _, _ = d.log.Write(fmt.Appendf([]byte(""), "[%s] IN: %s %s %s\n",
time.Now().Format(time.DateTime),
r.Method,
r.RequestURI,
@@ -264,7 +265,7 @@ func (d *CWMPDevice) handleHTTPRequest(w http.ResponseWriter, r *http.Request) {
return
}
} else {
- d.log.Write([]byte("\n"))
+ _, _ = d.log.Write([]byte("\n"))
if len(env.Body.Messages) > 1 {
tui.LogNormal("%d messages received; ignoring all but first", len(env.Body.Messages))
} else if len(env.Body.Messages) == 0 {
@@ -276,10 +277,10 @@ func (d *CWMPDevice) handleHTTPRequest(w http.ResponseWriter, r *http.Request) {
d.SetSessionID(env.GetID())
}
- d.log.Write(fmt.Appendf([]byte(""), "[%s] OUT:\n", time.Now().Format(time.DateTime)))
+ _, _ = d.log.Write(fmt.Appendf([]byte(""), "[%s] OUT:\n", time.Now().Format(time.DateTime)))
resp := <-d.out
d.writeHTTPResponse(w, http.StatusOK, resp)
- d.log.Write([]byte("\n--------------------------------------------------------------------------------\n"))
+ _, _ = d.log.Write([]byte("\n--------------------------------------------------------------------------------\n"))
}
// write a reply to the response
diff --git a/internal/device/cwmp/messages/ChangeDUState_test.go b/internal/device/cwmp/messages/ChangeDUState_test.go
index cc49e39..5cfb686 100644
--- a/internal/device/cwmp/messages/ChangeDUState_test.go
+++ b/internal/device/cwmp/messages/ChangeDUState_test.go
@@ -6,12 +6,12 @@ package messages_test
import (
"bytes"
- "github.com/nokia/corteca-cli/internal/configuration"
- c "github.com/nokia/corteca-cli/internal/configuration"
- "github.com/nokia/corteca-cli/internal/device/cwmp/messages"
"encoding/xml"
"testing"
+ c "github.com/nokia/corteca-cli/internal/configuration"
+ "github.com/nokia/corteca-cli/internal/device/cwmp/messages"
+
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)
@@ -91,10 +91,10 @@ var ChangeDUStateInputMsg = messages.ChangeDUState{
}
func setupContext() {
- configuration.ResetContext()
- configuration.GetCmdContext().App.Name = "image"
- configuration.GetCmdContext().App.Version = "1.0.0"
- configuration.GetCmdContext().App.DUID = "c0c4328b-18a4-4b3b-b1da-e8ea8d8f457d"
+ c.ResetContext()
+ c.GetCmdContext().App.Name = "image"
+ c.GetCmdContext().App.Version = "1.0.0"
+ c.GetCmdContext().App.DUID = "c0c4328b-18a4-4b3b-b1da-e8ea8d8f457d"
}
func TestChangeDUStateParseFromXML(t *testing.T) {
diff --git a/internal/device/ssh/mock_ssh_server_test.go b/internal/device/ssh/mock_ssh_server_test.go
index 9252f92..786e2b4 100644
--- a/internal/device/ssh/mock_ssh_server_test.go
+++ b/internal/device/ssh/mock_ssh_server_test.go
@@ -86,7 +86,7 @@ func startTestServer(t *testing.T, expectedUsername, password string, authorized
if err != nil {
t.Fatalf("startTestServer: listen: %v", err)
}
- t.Cleanup(func() { ln.Close() })
+ t.Cleanup(func() { _ = ln.Close() })
go func() {
for {
@@ -106,12 +106,12 @@ func serveConn(conn net.Conn, cfg *ssh.ServerConfig, handler cmdHandlerFunc) {
if err != nil {
return // auth failure or protocol error — nothing to do
}
- defer sshConn.Close()
+ defer func() { _ = sshConn.Close() }()
go ssh.DiscardRequests(reqs)
for newChan := range chans {
if newChan.ChannelType() != "session" {
- newChan.Reject(ssh.UnknownChannelType, "unsupported channel type")
+ _ = newChan.Reject(ssh.UnknownChannelType, "unsupported channel type")
continue
}
ch, requests, err := newChan.Accept()
@@ -127,22 +127,22 @@ func serveSession(ch ssh.Channel, reqs <-chan *ssh.Request, handler cmdHandlerFu
// exec request, the channel is closed. This causes the client-side
// s.wait goroutine's "for msg := range reqs" loop to exit, which populates
// s.exitStatus and unblocks session.Wait() — preventing a deadlock.
- defer ch.Close()
+ defer func() { _ = ch.Close() }()
for req := range reqs {
switch req.Type {
case "exec":
if len(req.Payload) < 4 {
- req.Reply(false, nil)
+ _ = req.Reply(false, nil)
continue
}
n := binary.BigEndian.Uint32(req.Payload[:4])
if uint32(len(req.Payload)) < 4+n {
- req.Reply(false, nil)
+ _ = req.Reply(false, nil)
continue
}
cmd := string(req.Payload[4 : 4+n])
- req.Reply(true, nil)
+ _ = req.Reply(true, nil)
// Call the handler synchronously. For normal commands this returns
// quickly. For the context-cancellation test the handler blocks on
@@ -166,7 +166,7 @@ func serveSession(ch ssh.Channel, reqs <-chan *ssh.Request, handler cmdHandlerFu
// other channel requests the client may send while the handler is
// running or between commands.
if req.WantReply {
- req.Reply(false, nil)
+ _ = req.Reply(false, nil)
}
}
}
diff --git a/internal/device/ssh/sshdevice.go b/internal/device/ssh/sshdevice.go
index ed899e3..2c79b0e 100644
--- a/internal/device/ssh/sshdevice.go
+++ b/internal/device/ssh/sshdevice.go
@@ -20,7 +20,6 @@ import (
"sync"
"time"
- "golang.org/x/crypto/ssh"
stdssh "golang.org/x/crypto/ssh"
)
@@ -41,10 +40,6 @@ const (
deactivateQuaggaCmd = "sed -i 's#/usr/bin/vtysh#/bin/ash#' /etc/passwd"
maxNumRetries = 3
defaultSSHPort = "22"
-
- authSSHPassword = "password"
- authSSHPublicKey = "publicKey"
- cmdCPUArch = "uname -m"
)
type SSHDevice struct {
@@ -64,7 +59,9 @@ func NewSSHDevice(c *configuration.DeviceConfig, log io.Writer) (device.Device,
err error
sshconfig configuration.SSHClientEndpoint
)
- c.Decode(&sshconfig)
+ if err = c.Decode(&sshconfig); err != nil {
+ return nil, err
+ }
if err = d.connectSSHClient(&sshconfig); err != nil {
return nil, err
@@ -78,7 +75,7 @@ func NewSSHDevice(c *configuration.DeviceConfig, log io.Writer) (device.Device,
if err := deactivateQuagga(d.client, passwd2); err != nil {
return nil, err
}
- d.client.Close()
+ _ = d.client.Close()
tui.LogNormal("Need to reconnect...")
return NewSSHDevice(c, log)
}
@@ -115,7 +112,7 @@ func (d *SSHDevice) executeCommandString(ctx context.Context, cmd string) (any,
if err != nil {
return nil, fmt.Errorf("cannot start SSH command session: %w", err)
}
- defer session.Close()
+ defer func() { _ = session.Close() }()
output := bytes.NewBuffer(make([]byte, 0, 512))
session.Stdout = io.MultiWriter(d.log, output)
session.Stderr = d.log
@@ -141,7 +138,7 @@ func (d *SSHDevice) executeCommandString(ctx context.Context, cmd string) (any,
}
case <-ctx.Done():
- _ = session.Signal(ssh.SIGKILL)
+ _ = session.Signal(stdssh.SIGKILL)
return nil, ctx.Err()
}
}
@@ -213,7 +210,7 @@ func (d *SSHDevice) connectSSHClient(sshconfig *configuration.SSHClientEndpoint)
// connect to stdssh server
d.client, err = stdssh.Dial("tcp", u.Host, config)
if err != nil {
- fmt.Fprintf(d.log, "\n=== New connection to %s at %s ===\n", u.Host, time.Now().Format(time.DateTime))
+ _, _ = fmt.Fprintf(d.log, "\n=== New connection to %s at %s ===\n", u.Host, time.Now().Format(time.DateTime))
}
return err
}
@@ -223,7 +220,7 @@ func hasQuagga(client *stdssh.Client) (bool, error) {
if err != nil {
return false, err
}
- defer session.Close()
+ defer func() { _ = session.Close() }()
var output bytes.Buffer
session.Stdout = &output
@@ -248,7 +245,7 @@ func deactivateQuagga(client *stdssh.Client, password2 string) error {
if err != nil {
return err
}
- defer session.Close()
+ defer func() { _ = session.Close() }()
if err := session.RequestPty("xterm", 80, 40, stdssh.TerminalModes{}); err != nil {
return err
@@ -275,5 +272,5 @@ func deactivateQuagga(client *stdssh.Client, password2 string) error {
}
func (d *SSHDevice) Close() {
- d.client.Close()
+ _ = d.client.Close()
}
diff --git a/internal/device/ssh/sshdevice_test.go b/internal/device/ssh/sshdevice_test.go
index 1df3ccb..feefc5b 100644
--- a/internal/device/ssh/sshdevice_test.go
+++ b/internal/device/ssh/sshdevice_test.go
@@ -179,7 +179,7 @@ func TestSSHDevice_PublicKeyAuth(t *testing.T) {
if err := pem.Encode(keyFile, &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER}); err != nil {
t.Fatalf("write PEM key: %v", err)
}
- keyFile.Close()
+ _ = keyFile.Close()
// Start a server that only accepts the generated public key (no password).
addr := startTestServer(t, "testuser", "", sshPubKey, withQuaggaProbe(func(cmd string) (string, uint32) {
diff --git a/internal/fsutil/fsutil.go b/internal/fsutil/fsutil.go
index 5be3529..0c3455f 100644
--- a/internal/fsutil/fsutil.go
+++ b/internal/fsutil/fsutil.go
@@ -26,7 +26,7 @@ func CopyFile(src, dst string) (int64, error) {
if err != nil {
return 0, err
}
- defer source.Close()
+ defer func() { _ = source.Close() }()
destDir := filepath.Dir(dst)
if err := os.MkdirAll(destDir, os.ModePerm); err != nil {
@@ -37,7 +37,7 @@ func CopyFile(src, dst string) (int64, error) {
if err != nil {
return 0, err
}
- defer destination.Close()
+ defer func() { _ = destination.Close() }()
nBytes, err := io.Copy(destination, source)
if err != nil {
return nBytes, err
@@ -78,13 +78,13 @@ func TarAndGzip(basePath, targetTarGzPath string, includePaths []string) error {
if err != nil {
return err
}
- defer tarFile.Close()
+ defer func() { _ = tarFile.Close() }()
gzipWriter := gzip.NewWriter(tarFile)
- defer gzipWriter.Close()
+ defer func() { _ = gzipWriter.Close() }()
tarWriter := tar.NewWriter(gzipWriter)
- defer tarWriter.Close()
+ defer func() { _ = tarWriter.Close() }()
for _, includePath := range includePaths {
fullPath := filepath.Join(basePath, includePath)
@@ -150,7 +150,7 @@ func addFileToTarWriter(tarWriter *tar.Writer, file string, fi os.FileInfo, base
if err != nil {
return err
}
- defer fileContent.Close()
+ defer func() { _ = fileContent.Close() }()
if _, err := io.Copy(tarWriter, fileContent); err != nil {
return err
@@ -166,14 +166,14 @@ func ExtractTarball(src, dest string) error {
if err != nil {
return fmt.Errorf("could not open source file: %v", err)
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
// Create a gzip reader
gzipReader, err := gzip.NewReader(file)
if err != nil {
return fmt.Errorf("could not create gzip reader: %v", err)
}
- defer gzipReader.Close()
+ defer func() { _ = gzipReader.Close() }()
// Create a tar reader
tarReader := tar.NewReader(gzipReader)
@@ -207,7 +207,7 @@ func ExtractTarball(src, dest string) error {
if err != nil {
return fmt.Errorf("could not create file: %v", err)
}
- defer outFile.Close()
+ defer func() { _ = outFile.Close() }()
err = os.Chmod(targetPath, header.FileInfo().Mode())
if err != nil {
@@ -242,16 +242,16 @@ func CreateTarArchive(fileName, archivePath string) error {
if err != nil {
return fmt.Errorf("failed to open file: %w", err)
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
archive, err := os.Create(archivePath)
if err != nil {
return fmt.Errorf("failed to create archive file: %w", err)
}
- defer archive.Close()
+ defer func() { _ = archive.Close() }()
writer := tar.NewWriter(archive)
- defer writer.Close()
+ defer func() { _ = writer.Close() }()
fileInfo, err := file.Stat()
if err != nil {
@@ -292,13 +292,13 @@ func CreateTarArchiveFromFolder(srcDir, destTarGzPath string) error {
if err != nil {
return fmt.Errorf("failed to create archive file: %w", err)
}
- defer archive.Close()
+ defer func() { _ = archive.Close() }()
gzipWriter := gzip.NewWriter(archive)
- defer gzipWriter.Close()
+ defer func() { _ = gzipWriter.Close() }()
tarWriter := tar.NewWriter(gzipWriter)
- defer tarWriter.Close()
+ defer func() { _ = tarWriter.Close() }()
baseDir := filepath.Clean(srcDir)
return filepath.Walk(srcDir, func(file string, fi os.FileInfo, err error) error {
@@ -333,7 +333,7 @@ func CreateTarArchiveFromFolder(srcDir, destTarGzPath string) error {
if err != nil {
return err
}
- defer fileContent.Close()
+ defer func() { _ = fileContent.Close() }()
if _, err := io.Copy(tarWriter, fileContent); err != nil {
return err
diff --git a/internal/packager/oci.go b/internal/packager/oci.go
index 41d4df8..932f926 100644
--- a/internal/packager/oci.go
+++ b/internal/packager/oci.go
@@ -31,7 +31,7 @@ func writeBuildInfoJson(buildInfo map[string]string, rootfsPath string) error {
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
_, err = file.Write(data)
if err != nil {
@@ -50,7 +50,7 @@ func writeRuntimeSpecToJSON(appSettings configuration.AppSettings, filePath stri
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
_, err = file.Write(data)
if err != nil {
diff --git a/internal/packager/validation.go b/internal/packager/validation.go
index 8d7bc1b..931f2b2 100644
--- a/internal/packager/validation.go
+++ b/internal/packager/validation.go
@@ -44,7 +44,7 @@ func isBinary(entrypointPath string) (bool, string, error) {
if err != nil {
return false, "", fmt.Errorf("failed to open file: %v", err)
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
is_bin := false
const chunkSize = 4
buf := make([]byte, chunkSize)
@@ -73,7 +73,7 @@ func isScript(entrypointPath string) (bool, string, error) {
if err != nil {
return false, "", fmt.Errorf("failed to open file: %v", err)
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
reader := bufio.NewReader(file)
line, err := reader.ReadString('\n')
if err != nil && err != io.EOF {
@@ -186,7 +186,7 @@ func verifyMachineCompatibility(path, targetArch string) error {
if err != nil {
return fmt.Errorf("ELF validation failed: %w", err)
}
- defer f.Close()
+ defer func() { _ = f.Close() }()
if f.Type != elf.ET_EXEC && f.Type != elf.ET_DYN {
return fmt.Errorf("invalid ELF type: %s", f.Type)
diff --git a/internal/packager/validation_test.go b/internal/packager/validation_test.go
index a2c79c4..605e4d0 100644
--- a/internal/packager/validation_test.go
+++ b/internal/packager/validation_test.go
@@ -295,30 +295,18 @@ func createSymlink(t *testing.T, target, link string) {
func TestDiscoverEntrypointPath_SymlinkToBinary(t *testing.T) {
- tmpDir := t.TempDir()
- bin := createBinaryFile(t, "", []byte{0x7F, 0x45, 0x4C, 0x46})
- tmpPathToBinary := filepath.Join(tmpDir, bin)
- link := "link"
- tmpPathToLink := filepath.Join(tmpDir, link)
- createSymlink(t, bin, link)
+ tmpDir := t.TempDir()
+ bin := createBinaryFile(t, tmpDir, []byte{0x7F, 0x45, 0x4C, 0x46})
+ tmpPathToLink := filepath.Join(tmpDir, "link")
+ createSymlink(t, "binaryfile", tmpPathToLink)
- err := os.Rename(bin, tmpPathToBinary)
+ path, err := discoverEntrypointPath(tmpPathToLink, tmpDir)
if err != nil {
- t.Fatalf("failed to move file: %v", err)
+ t.Fatalf("unexpected error: %v", err)
}
-
- err = os.Rename(link, tmpPathToLink)
- if err != nil {
- t.Fatalf("failed to move file: %v", err)
+ if path != bin {
+ t.Errorf("expected path to be '%s', got '%s'", bin, path)
}
-
- path, err := discoverEntrypointPath(tmpPathToLink, tmpDir)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if path != tmpPathToBinary {
- t.Errorf("expected path to be '%s', got '%s'", bin, path)
- }
}
func TestDiscoverEntrypointPath_ScriptWithShebang(t *testing.T) {
@@ -337,32 +325,19 @@ func TestDiscoverEntrypointPath_ScriptWithShebang(t *testing.T) {
}
func TestDiscoverEntrypointPath_ChainSymlinkScriptBinary(t *testing.T) {
- tmpDir := t.TempDir()
- bin := createBinaryFile(t, tmpDir, []byte{0x7F, 0x45, 0x4C, 0x46})
- script := createFile(t, "", "testfile.sh", "#!/binaryfile\n")
- tmpPathToScript := filepath.Join(tmpDir, script)
- link := "linkfile"
- tmpPathToLink := filepath.Join(tmpDir, link)
- createSymlink(t, script, link)
+ tmpDir := t.TempDir()
+ bin := createBinaryFile(t, tmpDir, []byte{0x7F, 0x45, 0x4C, 0x46})
+ _ = createFile(t, tmpDir, "testfile.sh", "#!/binaryfile\n")
+ tmpPathToLink := filepath.Join(tmpDir, "linkfile")
+ createSymlink(t, "testfile.sh", tmpPathToLink)
- err := os.Rename(script, tmpPathToScript)
+ path, err := discoverEntrypointPath(tmpPathToLink, tmpDir)
if err != nil {
- t.Fatalf("failed to move file: %v", err)
+ t.Fatalf("unexpected error: %v", err)
}
-
- err = os.Rename(link, tmpPathToLink)
- if err != nil {
- t.Fatalf("failed to move file: %v", err)
+ if path != bin {
+ t.Errorf("expected path to be '%s', got '%s'", bin, path)
}
-
- path, err := discoverEntrypointPath(tmpPathToLink, tmpDir)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- expected := bin
- if path != expected {
- t.Errorf("expected path to be '%s', got '%s'", expected, path)
- }
}
func TestDiscoverEntrypointPath_UnknownFileType(t *testing.T) {
diff --git a/internal/publish/listen.go b/internal/publish/listen.go
index 0c1da5b..08f7830 100644
--- a/internal/publish/listen.go
+++ b/internal/publish/listen.go
@@ -30,7 +30,7 @@ func ListenAsync(serverRoot string, addr *url.URL) (*http.Server, error) {
Handler: http.FileServer(http.Dir(serverRoot)),
}
go func() {
- srv.Serve(l)
+ _ = srv.Serve(l)
}()
return srv, nil
default:
diff --git a/internal/publish/push.go b/internal/publish/push.go
index c47a83f..a96fe80 100644
--- a/internal/publish/push.go
+++ b/internal/publish/push.go
@@ -39,7 +39,7 @@ func GzipOpener(path string) tarball.Opener {
gz, err := gzip.NewReader(f)
if err != nil {
- f.Close()
+ _ = f.Close()
return nil, err
}
diff --git a/internal/publish/put.go b/internal/publish/put.go
index 3d97b3e..8443577 100644
--- a/internal/publish/put.go
+++ b/internal/publish/put.go
@@ -68,7 +68,7 @@ func HttpPut(filePath string, url url.URL, token string) error {
if err != nil {
return err
}
- defer file.Close()
+ defer func() { _ = file.Close() }()
prog := tui.PromptForProgress(fmt.Sprintf("Uploading %s", fileName))
defer close(prog)
@@ -98,7 +98,7 @@ func HttpPut(filePath string, url url.URL, token string) error {
return err
}
- defer resp.Body.Close()
+ defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
return fmt.Errorf("server returned non-successful status: %s", resp.Status)
diff --git a/internal/publish/put_test.go b/internal/publish/put_test.go
index cabb93b..c5e3cb5 100644
--- a/internal/publish/put_test.go
+++ b/internal/publish/put_test.go
@@ -101,13 +101,13 @@ func TestHttpPut(t *testing.T) {
if err != nil {
t.Fatalf("Failed to create temporary file: %v", err)
}
- defer os.Remove(tmpFile.Name())
+ defer func() { _ = os.Remove(tmpFile.Name()) }()
_, err = tmpFile.Write([]byte("test content"))
if err != nil {
t.Fatalf("Failed to write to temporary file: %v", tmpFile)
}
- tmpFile.Close()
+ _ = tmpFile.Close()
var server *httptest.Server
if tc.setupServer != nil {
diff --git a/internal/tui/tui.go b/internal/tui/tui.go
index 136a0ff..0815a0f 100644
--- a/internal/tui/tui.go
+++ b/internal/tui/tui.go
@@ -37,7 +37,7 @@ var (
// If no-color flag is set or no terminal found then remove colored output
func DefineOutputColor() {
- if DisableColoredOutput || !(term.IsTerminal(int(os.Stdout.Fd())) && term.IsTerminal(int(os.Stderr.Fd()))) {
+ if DisableColoredOutput || !term.IsTerminal(int(os.Stdout.Fd())) || !term.IsTerminal(int(os.Stderr.Fd())) {
DisableColoredOutput = true
pterm.DisableColor()
}
@@ -64,13 +64,13 @@ func LogError(format string, args ...any) {
func SetOutputColor(colorSeq string, out io.Writer) {
if !DisableColoredOutput {
- fmt.Fprintf(out, colorSeq)
+ _, _ = fmt.Fprint(out, colorSeq)
}
}
func ResetOutputColor(out io.Writer) {
if !DisableColoredOutput {
- fmt.Fprintf(out, CsReset)
+ _, _ = fmt.Fprint(out, CsReset)
}
}
@@ -117,7 +117,7 @@ func PromptForProgress(label string) chan<- ProgressUpdate {
diff := current - bar.Current
bar.Add(diff)
}
- bar.Stop()
+ _, _ = bar.Stop()
}()
return ch
}