Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ jobs:
with:
go-version: ${{ matrix.go-version }}
- name: build go
run: go build ./cmd/app/main.go
run: go build ./cmd/app
- name: build noui (headless)
run: CGO_ENABLED=0 go build -tags=noui -o console-headless ./cmd/app/main.go
run: CGO_ENABLED=0 go build -tags=noui -o console-headless ./cmd/app
- name: Install Test Converter and run tests
run: |
export GOPATH="$HOME/go/"
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,28 +103,28 @@ jobs:
# Cross-compile all platform binaries from a single runner using CGO_ENABLED=0
# Static Go binaries are cross-platform compatible, so we can build all targets from Linux
- name: Build Linux x64
run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/linux/console_linux_x64 ./cmd/app/main.go
run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/linux/console_linux_x64 ./cmd/app

- name: Build Linux x64 headless
run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags=noui -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/linux/console_linux_x64_headless ./cmd/app/main.go
run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags=noui -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/linux/console_linux_x64_headless ./cmd/app

- name: Build Linux arm64
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/linux/console_linux_arm64 ./cmd/app/main.go
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/linux/console_linux_arm64 ./cmd/app

- name: Build Linux arm64 headless
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -tags=noui -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/linux/console_linux_arm64_headless ./cmd/app/main.go
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -tags=noui -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/linux/console_linux_arm64_headless ./cmd/app

- name: Build Windows x64
run: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/windows/console_windows_x64.exe ./cmd/app/main.go
run: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/windows/console_windows_x64.exe ./cmd/app

- name: Build Windows x64 headless
run: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -tags=noui -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/windows/console_windows_x64_headless.exe ./cmd/app/main.go
run: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -tags=noui -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/windows/console_windows_x64_headless.exe ./cmd/app

- name: Build macOS arm64
run: CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/darwin/console_mac_arm64 ./cmd/app/main.go
run: CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/darwin/console_mac_arm64 ./cmd/app

- name: Build macOS arm64 headless
run: CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -tags=noui -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/darwin/console_mac_arm64_headless ./cmd/app/main.go
run: CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -tags=noui -ldflags "-s -w -X 'github.com/device-management-toolkit/console/internal/app.Version=${{ needs.prepare.outputs.version }}'" -trimpath -o dist/darwin/console_mac_arm64_headless ./cmd/app

# Cache all build artifacts in a single step
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
Expand Down Expand Up @@ -246,7 +246,7 @@ jobs:
- name: Generate OpenAPI specification
if: steps.semantic-release.outputs.new_release_published == 'true' && steps.check-openapi-changes.outputs.changed == 'true'
run: |
GIN_MODE=debug go run ./cmd/app/main.go
GIN_MODE=debug go run ./cmd/app

- name: Verify OpenAPI spec was generated
run: |
Expand Down
12 changes: 7 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
ARG BUILD_TAGS=""

# Step 1: Modules caching
FROM golang:1.25.6-alpine@sha256:98e6cffc31ccc44c7c15d83df1d69891efee8115a5bb7ede2bf30a38af3e3c92 AS modules
FROM golang:1.25.7-alpine@sha256:f6751d823c26342f9506c03797d2527668d095b0a15f1862cddb4d927a7a4ced AS modules
COPY go.mod go.sum /modules/
WORKDIR /modules
RUN apk add --no-cache git
RUN go mod download

# Step 2: Builder
FROM golang:1.25.6-alpine@sha256:98e6cffc31ccc44c7c15d83df1d69891efee8115a5bb7ede2bf30a38af3e3c92 AS builder
FROM golang:1.25.7-alpine@sha256:f6751d823c26342f9506c03797d2527668d095b0a15f1862cddb4d927a7a4ced AS builder
# Build tags control dependencies:
# - Default (no tags): Full build with UI
# - noui: Excludes web UI assets
Expand All @@ -37,10 +37,12 @@ RUN mkdir -p /.config/device-management-toolkit
# Step 3: Final - Use scratch for all builds (all are fully static with pure Go)
FROM scratch
ENV TMPDIR=/tmp
COPY --from=builder /app/tmp /tmp
COPY --from=builder /app/config /config
ENV XDG_CONFIG_HOME=/.config
COPY --chown=65534:65534 --from=builder /app/tmp /tmp
COPY --chown=65534:65534 --from=builder /app/config /config
COPY --from=builder /app/internal/app/migrations /migrations
COPY --from=builder /bin/app /app
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /.config/device-management-toolkit /.config/device-management-toolkit
COPY --chown=65534:65534 --from=builder /.config/device-management-toolkit /.config/device-management-toolkit
USER 65534:65534
CMD ["/app"]
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Security Policy
Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation.

## Reporting a Vulnerability

Please report any security vulnerabilities in this project utilizing the Github's guidelines [here](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability).
Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html).
57 changes: 57 additions & 0 deletions cmd/app/browser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//go:build !noui

package main

import (
"context"
"os/exec"
"runtime"

"github.com/device-management-toolkit/console/config"
)

func launchBrowser(cfg *config.Config) {
scheme := "http"
if cfg.TLS.Enabled {
scheme = "https"
}

if err := openBrowser(scheme+"://localhost:"+cfg.Port, runtime.GOOS); err != nil {
panic(err)
}
}

// CommandExecutor is an interface to allow for mocking exec.Command in tests.
type CommandExecutor interface {
Execute(name string, arg ...string) error
}

// RealCommandExecutor is a real implementation of CommandExecutor.
type RealCommandExecutor struct{}

func (e *RealCommandExecutor) Execute(name string, arg ...string) error {
return exec.CommandContext(context.Background(), name, arg...).Start()
}

// Global command executor, can be replaced in tests.
var cmdExecutor CommandExecutor = &RealCommandExecutor{}

func openBrowser(url, currentOS string) error {
var cmd string

var args []string

switch currentOS {
case "darwin":
cmd = "open"
args = []string{url}
case "windows":
cmd = "cmd"
args = []string{"/c", "start", url}
default:
cmd = "xdg-open"
args = []string{url}
}

return cmdExecutor.Execute(cmd, args...)
}
8 changes: 8 additions & 0 deletions cmd/app/browser_noui.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//go:build noui

package main

import "github.com/device-management-toolkit/console/config"

// launchBrowser is a no-op in noui builds; there is no UI to open.
func launchBrowser(_ *config.Config) {}
53 changes: 53 additions & 0 deletions cmd/app/browser_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//go:build !noui

package main

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

type MockCommandExecutor struct {
mock.Mock
}

func (m *MockCommandExecutor) Execute(name string, arg ...string) error {
args := m.Called(name, arg)

return args.Error(0)
}

func TestOpenBrowserWindows(t *testing.T) { //nolint:paralleltest // cannot have simultaneous tests modifying executor.
mockCmdExecutor := new(MockCommandExecutor)
cmdExecutor = mockCmdExecutor

mockCmdExecutor.On("Execute", "cmd", []string{"/c", "start", "http://localhost:8080"}).Return(nil)

err := openBrowser("http://localhost:8080", "windows")
assert.NoError(t, err)
mockCmdExecutor.AssertExpectations(t)
}

func TestOpenBrowserDarwin(t *testing.T) { //nolint:paralleltest // cannot have simultaneous tests modifying executor.
mockCmdExecutor := new(MockCommandExecutor)
cmdExecutor = mockCmdExecutor

mockCmdExecutor.On("Execute", "open", []string{"http://localhost:8080"}).Return(nil)

err := openBrowser("http://localhost:8080", "darwin")
assert.NoError(t, err)
mockCmdExecutor.AssertExpectations(t)
}

func TestOpenBrowserLinux(t *testing.T) { //nolint:paralleltest // cannot have simultaneous tests modifying executor.
mockCmdExecutor := new(MockCommandExecutor)
cmdExecutor = mockCmdExecutor

mockCmdExecutor.On("Execute", "xdg-open", []string{"http://localhost:8080"}).Return(nil)

err := openBrowser("http://localhost:8080", "ubuntu")
assert.NoError(t, err)
mockCmdExecutor.AssertExpectations(t)
}
80 changes: 17 additions & 63 deletions cmd/app/main.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package main

import (
"context"
"errors"
"fmt"
"log"
"os"
"os/exec"
"runtime"

"github.com/device-management-toolkit/go-wsman-messages/v2/pkg/security"

Expand All @@ -30,7 +27,9 @@ var (
var (
initializeConfigFunc = config.NewConfig
initializeAppFunc = app.Init
runAppFunc = app.Run
runAppFunc = func(cfg *config.Config, log logger.Interface) {
app.Run(cfg, log)
}
// NewGeneratorFunc allows tests to inject a fake OpenAPI generator.
NewGeneratorFunc = func(u usecase.Usecases, l logger.Interface) interface {
GenerateSpec() ([]byte, error)
Expand Down Expand Up @@ -63,9 +62,11 @@ func main() {
log.Fatalf("CIRA certificate setup error: %s", err)
}

l := logger.New(cfg.Level)

handleEncryptionKey(cfg)
handleDebugMode(cfg)
runAppFunc(cfg)
handleDebugMode(cfg, l)
runAppFunc(cfg, l)
}

func setupCIRACertificates(cfg *config.Config, secretsClient security.Storager) error {
Expand All @@ -86,29 +87,15 @@ func setupCIRACertificates(cfg *config.Config, secretsClient security.Storager)
return nil
}

func handleDebugMode(cfg *config.Config) {
func handleDebugMode(cfg *config.Config, l logger.Interface) {
if os.Getenv("GIN_MODE") != "debug" {
go launchBrowser(cfg)
} else {
if err := handleOpenAPIGeneration(); err != nil {
log.Fatalf("Failed to generate OpenAPI spec: %s", err)
}
}
}

func launchBrowser(cfg *config.Config) {
scheme := "http"
if cfg.TLS.Enabled {
scheme = "https"
}

if err := openBrowser(scheme+"://localhost:"+cfg.Port, runtime.GOOS); err != nil {
panic(err)
handleOpenAPIGeneration(l)
}
}

func handleOpenAPIGeneration() error {
l := logger.New("info")
func handleOpenAPIGeneration(l logger.Interface) {
usecases := usecase.Usecases{}

// Create OpenAPI generator
Expand All @@ -117,17 +104,19 @@ func handleOpenAPIGeneration() error {
// Generate specification
spec, err := generator.GenerateSpec()
if err != nil {
return err
l.Warn("Failed to generate OpenAPI spec: %s", err)

return
}

// Save to file
if err := generator.SaveSpec(spec, "doc/openapi.json"); err != nil {
return err
}
l.Warn("Failed to save OpenAPI spec: %s", err)

log.Println("OpenAPI specification generated at doc/openapi.json")
return
}

return nil
l.Info("OpenAPI specification generated at doc/openapi.json")
}

func handleSecretsConfig(cfg *config.Config) (security.Storager, error) {
Expand Down Expand Up @@ -299,38 +288,3 @@ func handleKeyNotFound(toolkitCrypto security.Crypto, _, _ security.Storager) st

return toolkitCrypto.GenerateKey()
}

// CommandExecutor is an interface to allow for mocking exec.Command in tests.
type CommandExecutor interface {
Execute(name string, arg ...string) error
}

// RealCommandExecutor is a real implementation of CommandExecutor.
type RealCommandExecutor struct{}

func (e *RealCommandExecutor) Execute(name string, arg ...string) error {
return exec.CommandContext(context.Background(), name, arg...).Start()
}

// Global command executor, can be replaced in tests.
var cmdExecutor CommandExecutor = &RealCommandExecutor{}

func openBrowser(url, currentOS string) error {
var cmd string

var args []string

switch currentOS {
case "darwin":
cmd = "open"
args = []string{url}
case "windows":
cmd = "cmd"
args = []string{"/c", "start", url}
default:
cmd = "xdg-open"
args = []string{url}
}

return cmdExecutor.Execute(cmd, args...)
}
Loading
Loading