mirror of
https://github.com/holos-run/holos.git
synced 2026-03-20 09:15:02 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ce88bf491 | ||
|
|
b05571a595 | ||
|
|
4edfc71d68 | ||
|
|
3049694a0a | ||
|
|
5860c5747b | ||
|
|
d3c2d55706 | ||
|
|
ac2ff47a9c |
3
.github/workflows/release.yaml
vendored
3
.github/workflows/release.yaml
vendored
@@ -54,6 +54,9 @@ jobs:
|
||||
- name: List keys
|
||||
run: gpg -K
|
||||
|
||||
- name: Git diff
|
||||
run: git diff
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@ coverage.out
|
||||
*.hold/
|
||||
/deploy/
|
||||
.vscode/
|
||||
tmp/
|
||||
|
||||
@@ -34,7 +34,7 @@ let OBJECTS = #APIObjects & {
|
||||
containers: [
|
||||
{
|
||||
name: Holos
|
||||
image: "271053619184.dkr.ecr.us-east-2.amazonaws.com/holos-run/holos-server/holos:0.74.0"
|
||||
image: "271053619184.dkr.ecr.us-east-2.amazonaws.com/holos-run/holos-server/holos:v0.76.0"
|
||||
imagePullPolicy: "Always"
|
||||
env: [
|
||||
{
|
||||
|
||||
6
go.mod
6
go.mod
@@ -6,6 +6,7 @@ require (
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.33.0-20240401165935-b983156c5e99.1
|
||||
connectrpc.com/connect v1.16.0
|
||||
connectrpc.com/grpcreflect v1.2.0
|
||||
connectrpc.com/otelconnect v0.7.0
|
||||
connectrpc.com/validate v0.1.0
|
||||
cuelang.org/go v0.8.0
|
||||
entgo.io/ent v0.13.1
|
||||
@@ -20,6 +21,7 @@ require (
|
||||
github.com/lmittmann/tint v1.0.4
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-runewidth v0.0.15
|
||||
github.com/mennanov/fieldmask-utils v1.1.2
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/prometheus/client_golang v1.19.0
|
||||
github.com/rogpeppe/go-internal v1.12.0
|
||||
@@ -29,6 +31,7 @@ require (
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/net v0.24.0
|
||||
golang.org/x/tools v0.20.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa
|
||||
google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002
|
||||
honnef.co/go/tools v0.4.7
|
||||
k8s.io/api v0.29.2
|
||||
@@ -43,7 +46,6 @@ require (
|
||||
ariga.io/atlas v0.19.1-0.20240203083654-5948b60a8e43 // indirect
|
||||
cloud.google.com/go/compute v1.23.3 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
connectrpc.com/otelconnect v0.7.0 // indirect
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e // indirect
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
@@ -157,7 +159,6 @@ require (
|
||||
github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mennanov/fieldmask-utils v1.1.2 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/miekg/dns v1.1.58 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
@@ -248,7 +249,6 @@ require (
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa // indirect
|
||||
google.golang.org/grpc v1.62.1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
||||
43
internal/cli/generate/generate.go
Normal file
43
internal/cli/generate/generate.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/generate"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// New returns a new generate command.
|
||||
func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("generate")
|
||||
cmd.Aliases = []string{"gen"}
|
||||
cmd.Short = "generate local resources"
|
||||
cmd.Args = cobra.NoArgs
|
||||
|
||||
cmd.AddCommand(NewPlatform(cfg))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewPlatform(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("platform")
|
||||
cmd.Use = "platform [flags] PLATFORM"
|
||||
cmd.Short = "generate a platform from an embedded schematic"
|
||||
cmd.Long = fmt.Sprintf("Embedded platforms available to generate:\n\n %s", strings.Join(generate.Platforms(), "\n "))
|
||||
cmd.Args = cobra.ExactArgs(1)
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
for _, name := range args {
|
||||
if err := generate.GeneratePlatform(ctx, name); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
cue "cuelang.org/go/cue/errors"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
)
|
||||
|
||||
// MakeMain makes a main function for the cli or tests.
|
||||
@@ -25,7 +27,8 @@ func MakeMain(options ...holos.Option) func() int {
|
||||
|
||||
// HandleError is the top level error handler that unwraps and logs errors.
|
||||
func HandleError(ctx context.Context, err error, hc *holos.Config) (exitCode int) {
|
||||
log := hc.NewTopLevelLogger()
|
||||
// Connect errors have codes, log them.
|
||||
log := hc.NewTopLevelLogger().With("code", connect.CodeOf(err))
|
||||
var cueErr cue.Error
|
||||
var errAt *errors.ErrorAt
|
||||
const msg = "could not execute"
|
||||
@@ -39,5 +42,24 @@ func HandleError(ctx context.Context, err error, hc *holos.Config) (exitCode int
|
||||
msg := cue.Details(cueErr, nil)
|
||||
_, _ = fmt.Fprint(hc.Stderr(), msg)
|
||||
}
|
||||
// connect errors have details and codes.
|
||||
// Refer to https://connectrpc.com/docs/go/errors
|
||||
if connectErr := new(connect.Error); errors.As(err, &connectErr) {
|
||||
for _, detail := range connectErr.Details() {
|
||||
msg, valueErr := detail.Value()
|
||||
if valueErr != nil {
|
||||
log.WarnContext(ctx, "could not decode error detail", "err", err, "type", detail.Type(), "note", "this usually means we don't have the schema for the protobuf message type")
|
||||
continue
|
||||
}
|
||||
if info, ok := msg.(*errdetails.ErrorInfo); ok {
|
||||
logDetail := log.With("reason", info.GetReason(), "domain", info.GetDomain())
|
||||
for k, v := range info.GetMetadata() {
|
||||
logDetail = logDetail.With(k, v)
|
||||
}
|
||||
logDetail.ErrorContext(ctx, info.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
34
internal/cli/register/register.go
Normal file
34
internal/cli/register/register.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package register
|
||||
|
||||
import (
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/register"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// New returns a new register command.
|
||||
func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("register")
|
||||
cmd.Short = "register with holos server"
|
||||
cmd.Args = cobra.NoArgs
|
||||
|
||||
config := register.NewConfig(cfg)
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
|
||||
|
||||
cmd.AddCommand(NewUser(config))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// NewUser returns a command to register a user with holos server.
|
||||
func NewUser(cfg *register.Config) *cobra.Command {
|
||||
cmd := command.New("user")
|
||||
cmd.Short = "user registration workflow"
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
return register.User(ctx, cfg)
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
@@ -5,23 +5,26 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/holos-run/holos/version"
|
||||
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/holos-run/holos/internal/server"
|
||||
|
||||
"github.com/holos-run/holos/internal/cli/build"
|
||||
"github.com/holos-run/holos/internal/cli/controller"
|
||||
"github.com/holos-run/holos/internal/cli/create"
|
||||
"github.com/holos-run/holos/internal/cli/generate"
|
||||
"github.com/holos-run/holos/internal/cli/get"
|
||||
"github.com/holos-run/holos/internal/cli/kv"
|
||||
"github.com/holos-run/holos/internal/cli/login"
|
||||
"github.com/holos-run/holos/internal/cli/logout"
|
||||
"github.com/holos-run/holos/internal/cli/preflight"
|
||||
"github.com/holos-run/holos/internal/cli/register"
|
||||
"github.com/holos-run/holos/internal/cli/render"
|
||||
"github.com/holos-run/holos/internal/cli/rpc"
|
||||
"github.com/holos-run/holos/internal/cli/token"
|
||||
"github.com/holos-run/holos/internal/cli/txtar"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/holos-run/holos/version"
|
||||
)
|
||||
|
||||
// New returns a new root *cobra.Command for command line execution.
|
||||
@@ -41,7 +44,7 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
return err
|
||||
}
|
||||
log := cfg.Logger()
|
||||
c.SetContext(logger.NewContext(c.Context(), log))
|
||||
c.Root().SetContext(logger.NewContext(c.Context(), log))
|
||||
// Set the default logger after flag parsing.
|
||||
slog.SetDefault(log)
|
||||
return nil
|
||||
@@ -65,6 +68,8 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
rootCmd.AddCommand(logout.New(cfg))
|
||||
rootCmd.AddCommand(token.New(cfg))
|
||||
rootCmd.AddCommand(rpc.New(cfg))
|
||||
rootCmd.AddCommand(generate.New(cfg))
|
||||
rootCmd.AddCommand(register.New(cfg))
|
||||
|
||||
// Maybe not needed?
|
||||
rootCmd.AddCommand(txtar.New(cfg))
|
||||
|
||||
@@ -45,7 +45,7 @@ func NewPlatformModel(cfg *Config) *cobra.Command {
|
||||
cmd := command.New("platform-model")
|
||||
cmd.Short = "get the platform model"
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
ctx := cmd.Root().Context()
|
||||
log := logger.FromContext(ctx)
|
||||
// client := platformconnect.NewPlatformServiceClient(token.NewClient(cfg.token), cfg.client.Server())
|
||||
client := platformconnect.NewPlatformServiceClient(token.NewClient(cfg.token), cfg.client.Server())
|
||||
|
||||
2027
internal/frontend/holos/package-lock.json
generated
2027
internal/frontend/holos/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -40,7 +40,7 @@
|
||||
"@angular-eslint/template-parser": "17.3.0",
|
||||
"@angular/cli": "^17.3.4",
|
||||
"@angular/compiler-cli": "^17.3.0",
|
||||
"@bufbuild/buf": "^1.31.0",
|
||||
"@bufbuild/buf": "^1.32.0",
|
||||
"@bufbuild/protoc-gen-es": "^1.9.0",
|
||||
"@connectrpc/protoc-gen-connect-es": "^1.4.0",
|
||||
"@connectrpc/protoc-gen-connect-query": "^1.4.0",
|
||||
|
||||
@@ -10,6 +10,7 @@ import { UserService } from './gen/holos/user/v1alpha1/user_service_connect';
|
||||
import { OrganizationService } from './gen/holos/organization/v1alpha1/organization_service_connect';
|
||||
import { PlatformService } from './gen/holos/platform/v1alpha1/platform_service_connect';
|
||||
import { HolosPanelWrapperComponent } from '../wrappers/holos-panel-wrapper/holos-panel-wrapper.component';
|
||||
import { SystemService } from './gen/holos/system/v1alpha1/system_service_connect';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
@@ -19,6 +20,7 @@ export const appConfig: ApplicationConfig = {
|
||||
provideClient(UserService),
|
||||
provideClient(OrganizationService),
|
||||
provideClient(PlatformService),
|
||||
provideClient(SystemService),
|
||||
importProvidersFrom(
|
||||
ConnectModule.forRoot({
|
||||
baseUrl: window.location.origin
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated from file holos/system/v1alpha1/system.proto (package holos.system.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
|
||||
import { Message, proto3 } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* @generated from message holos.system.v1alpha1.Version
|
||||
*/
|
||||
export class Version extends Message<Version> {
|
||||
/**
|
||||
* @generated from field: string version = 1;
|
||||
*/
|
||||
version = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string git_commit = 2;
|
||||
*/
|
||||
gitCommit = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string git_tree_state = 3;
|
||||
*/
|
||||
gitTreeState = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string go_version = 4;
|
||||
*/
|
||||
goVersion = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string build_date = 5;
|
||||
*/
|
||||
buildDate = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string os = 6;
|
||||
*/
|
||||
os = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string arch = 7;
|
||||
*/
|
||||
arch = "";
|
||||
|
||||
constructor(data?: PartialMessage<Version>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.system.v1alpha1.Version";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "version", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 2, name: "git_commit", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 3, name: "git_tree_state", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 4, name: "go_version", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 5, name: "build_date", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 6, name: "os", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 7, name: "arch", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Version {
|
||||
return new Version().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Version {
|
||||
return new Version().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Version {
|
||||
return new Version().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: Version | PlainMessage<Version> | undefined, b: Version | PlainMessage<Version> | undefined): boolean {
|
||||
return proto3.util.equals(Version, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { DropTablesRequest, DropTablesResponse, SeedDatabaseRequest, SeedDatabaseResponse } from "./system_service_pb.js";
|
||||
import { DropTablesRequest, DropTablesResponse, GetVersionRequest, GetVersionResponse, SeedDatabaseRequest, SeedDatabaseResponse } from "./system_service_pb.js";
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
@@ -13,12 +13,12 @@ export const SystemService = {
|
||||
typeName: "holos.system.v1alpha1.SystemService",
|
||||
methods: {
|
||||
/**
|
||||
* @generated from rpc holos.system.v1alpha1.SystemService.SeedDatabase
|
||||
* @generated from rpc holos.system.v1alpha1.SystemService.GetVersion
|
||||
*/
|
||||
seedDatabase: {
|
||||
name: "SeedDatabase",
|
||||
I: SeedDatabaseRequest,
|
||||
O: SeedDatabaseResponse,
|
||||
getVersion: {
|
||||
name: "GetVersion",
|
||||
I: GetVersionRequest,
|
||||
O: GetVersionResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
@@ -30,6 +30,15 @@ export const SystemService = {
|
||||
O: DropTablesResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.system.v1alpha1.SystemService.SeedDatabase
|
||||
*/
|
||||
seedDatabase: {
|
||||
name: "SeedDatabase",
|
||||
I: SeedDatabaseRequest,
|
||||
O: SeedDatabaseResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -4,7 +4,84 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
|
||||
import { Message, proto3 } from "@bufbuild/protobuf";
|
||||
import { FieldMask, Message, proto3 } from "@bufbuild/protobuf";
|
||||
import { Version } from "./system_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from message holos.system.v1alpha1.GetVersionRequest
|
||||
*/
|
||||
export class GetVersionRequest extends Message<GetVersionRequest> {
|
||||
/**
|
||||
* FieldMask represents the fields to include in the response.
|
||||
*
|
||||
* @generated from field: google.protobuf.FieldMask field_mask = 1;
|
||||
*/
|
||||
fieldMask?: FieldMask;
|
||||
|
||||
constructor(data?: PartialMessage<GetVersionRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.system.v1alpha1.GetVersionRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "field_mask", kind: "message", T: FieldMask },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetVersionRequest {
|
||||
return new GetVersionRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetVersionRequest {
|
||||
return new GetVersionRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetVersionRequest {
|
||||
return new GetVersionRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetVersionRequest | PlainMessage<GetVersionRequest> | undefined, b: GetVersionRequest | PlainMessage<GetVersionRequest> | undefined): boolean {
|
||||
return proto3.util.equals(GetVersionRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.system.v1alpha1.GetVersionResponse
|
||||
*/
|
||||
export class GetVersionResponse extends Message<GetVersionResponse> {
|
||||
/**
|
||||
* @generated from field: holos.system.v1alpha1.Version version = 1;
|
||||
*/
|
||||
version?: Version;
|
||||
|
||||
constructor(data?: PartialMessage<GetVersionResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.system.v1alpha1.GetVersionResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "version", kind: "message", T: Version },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetVersionResponse {
|
||||
return new GetVersionResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetVersionResponse {
|
||||
return new GetVersionResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetVersionResponse {
|
||||
return new GetVersionResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetVersionResponse | PlainMessage<GetVersionResponse> | undefined, b: GetVersionResponse | PlainMessage<GetVersionResponse> | undefined): boolean {
|
||||
return proto3.util.equals(GetVersionResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.system.v1alpha1.SeedDatabaseRequest
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { CreateUserRequest, CreateUserResponse, GetUserRequest, GetUserResponse } from "./user_service_pb.js";
|
||||
import { CreateUserRequest, CreateUserResponse, GetUserRequest, GetUserResponse, RegisterUserRequest, RegisterUserResponse } from "./user_service_pb.js";
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
@@ -36,6 +36,17 @@ export const UserService = {
|
||||
O: GetUserResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* Register an user and initialize an organization, bare platform, and reference platform.
|
||||
*
|
||||
* @generated from rpc holos.user.v1alpha1.UserService.RegisterUser
|
||||
*/
|
||||
registerUser: {
|
||||
name: "RegisterUser",
|
||||
I: RegisterUserRequest,
|
||||
O: RegisterUserResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialM
|
||||
import { FieldMask, Message, proto3 } from "@bufbuild/protobuf";
|
||||
import { User } from "./user_pb.js";
|
||||
import { UserRef } from "../../object/v1alpha1/object_pb.js";
|
||||
import { Organization } from "../../organization/v1alpha1/organization_pb.js";
|
||||
|
||||
/**
|
||||
* Create a User from the oidc id token claims or the provided user. Each one
|
||||
@@ -172,3 +173,118 @@ export class GetUserResponse extends Message<GetUserResponse> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a User from the oidc id token claims or the provided user. Each one
|
||||
* of subject, email, and user id must be globally unique.
|
||||
*
|
||||
* @generated from message holos.user.v1alpha1.RegisterUserRequest
|
||||
*/
|
||||
export class RegisterUserRequest extends Message<RegisterUserRequest> {
|
||||
/**
|
||||
* User resource to create. If absent, the server populates User fields with
|
||||
* the oidc id token claims of the authenticated request.
|
||||
* NOTE: The server may ignore this request field and register the user solely
|
||||
* from authenticated identity claims.
|
||||
*
|
||||
* @generated from field: optional holos.user.v1alpha1.User user = 1;
|
||||
*/
|
||||
user?: User;
|
||||
|
||||
/**
|
||||
* Mask of the user fields to include in the response.
|
||||
*
|
||||
* @generated from field: optional google.protobuf.FieldMask user_mask = 2;
|
||||
*/
|
||||
userMask?: FieldMask;
|
||||
|
||||
/**
|
||||
* Organization resource to create. If absent, the server generates an
|
||||
* organization based on the user fields.
|
||||
* NOTE: The server may ignore this request field and register the
|
||||
* organization solely from authenticated identity claims.
|
||||
*
|
||||
* @generated from field: optional holos.organization.v1alpha1.Organization organization = 3;
|
||||
*/
|
||||
organization?: Organization;
|
||||
|
||||
/**
|
||||
* Mask of the organization fields to include in the response.
|
||||
*
|
||||
* @generated from field: optional google.protobuf.FieldMask organization_mask = 4;
|
||||
*/
|
||||
organizationMask?: FieldMask;
|
||||
|
||||
constructor(data?: PartialMessage<RegisterUserRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.user.v1alpha1.RegisterUserRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "user", kind: "message", T: User, opt: true },
|
||||
{ no: 2, name: "user_mask", kind: "message", T: FieldMask, opt: true },
|
||||
{ no: 3, name: "organization", kind: "message", T: Organization, opt: true },
|
||||
{ no: 4, name: "organization_mask", kind: "message", T: FieldMask, opt: true },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): RegisterUserRequest {
|
||||
return new RegisterUserRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): RegisterUserRequest {
|
||||
return new RegisterUserRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): RegisterUserRequest {
|
||||
return new RegisterUserRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: RegisterUserRequest | PlainMessage<RegisterUserRequest> | undefined, b: RegisterUserRequest | PlainMessage<RegisterUserRequest> | undefined): boolean {
|
||||
return proto3.util.equals(RegisterUserRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.user.v1alpha1.RegisterUserResponse
|
||||
*/
|
||||
export class RegisterUserResponse extends Message<RegisterUserResponse> {
|
||||
/**
|
||||
* @generated from field: holos.user.v1alpha1.User user = 1;
|
||||
*/
|
||||
user?: User;
|
||||
|
||||
/**
|
||||
* @generated from field: holos.organization.v1alpha1.Organization organization = 2;
|
||||
*/
|
||||
organization?: Organization;
|
||||
|
||||
constructor(data?: PartialMessage<RegisterUserResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.user.v1alpha1.RegisterUserResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "user", kind: "message", T: User },
|
||||
{ no: 2, name: "organization", kind: "message", T: Organization },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): RegisterUserResponse {
|
||||
return new RegisterUserResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): RegisterUserResponse {
|
||||
return new RegisterUserResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): RegisterUserResponse {
|
||||
return new RegisterUserResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: RegisterUserResponse | PlainMessage<RegisterUserResponse> | undefined, b: RegisterUserResponse | PlainMessage<RegisterUserResponse> | undefined): boolean {
|
||||
return proto3.util.equals(RegisterUserResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
</button>
|
||||
}
|
||||
</span>
|
||||
<app-version-button></app-version-button>
|
||||
<app-profile-button [user$]="user$"></app-profile-button>
|
||||
</mat-toolbar>
|
||||
<main class="main-content">
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
|
||||
import { AsyncPipe, NgIf } from '@angular/common';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { ProfileButtonComponent } from '../profile-button/profile-button.component';
|
||||
import { User } from '../gen/holos/user/v1alpha1/user_pb';
|
||||
import { UserService } from '../services/user.service';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { map, shareReplay, takeUntil } from 'rxjs/operators';
|
||||
import { Organization } from '../gen/holos/organization/v1alpha1/organization_pb';
|
||||
import { User } from '../gen/holos/user/v1alpha1/user_pb';
|
||||
import { ProfileButtonComponent } from '../profile-button/profile-button.component';
|
||||
import { OrganizationService } from '../services/organization.service';
|
||||
import { UserService } from '../services/user.service';
|
||||
import { VersionButtonComponent } from '../version-button/version-button.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-nav',
|
||||
@@ -34,28 +35,35 @@ import { OrganizationService } from '../services/organization.service';
|
||||
RouterOutlet,
|
||||
MatCardModule,
|
||||
ProfileButtonComponent,
|
||||
VersionButtonComponent,
|
||||
]
|
||||
})
|
||||
export class NavComponent implements OnInit {
|
||||
export class NavComponent implements OnInit, OnDestroy {
|
||||
private breakpointObserver = inject(BreakpointObserver);
|
||||
private userService = inject(UserService);
|
||||
private orgService = inject(OrganizationService);
|
||||
private destroy$: Subject<boolean> = new Subject<boolean>();
|
||||
|
||||
user$!: Observable<User | null>;
|
||||
org$!: Observable<Organization | undefined>;
|
||||
|
||||
refreshOrg(): void {
|
||||
this.orgService.refreshOrganizations()
|
||||
}
|
||||
|
||||
isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
|
||||
.pipe(
|
||||
map(result => result.matches),
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
refreshOrg(): void {
|
||||
this.orgService.refreshOrganizations()
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.user$ = this.userService.getUser();
|
||||
this.org$ = this.orgService.activeOrg();
|
||||
this.user$ = this.userService.getUser().pipe(takeUntil(this.destroy$));
|
||||
this.org$ = this.orgService.activeOrg().pipe(takeUntil(this.destroy$));
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.destroy$.next(true);
|
||||
this.destroy$.complete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { FieldMask, JsonValue, Struct } from '@bufbuild/protobuf';
|
||||
import { Observable, of, switchMap } from 'rxjs';
|
||||
import { ObservableClient } from '../../connect/observable-client';
|
||||
import { Organization } from '../gen/holos/organization/v1alpha1/organization_pb';
|
||||
import { Platform } from '../gen/holos/platform/v1alpha1/platform_pb';
|
||||
import { PlatformService as ConnectPlatformService } from '../gen/holos/platform/v1alpha1/platform_service_connect';
|
||||
import { Platform, Spec } from '../gen/holos/platform/v1alpha1/platform_pb';
|
||||
import { GetPlatformRequest, ListPlatformsRequest, UpdatePlatformOperation, UpdatePlatformRequest } from '../gen/holos/platform/v1alpha1/platform_service_pb';
|
||||
import { FieldMask, JsonValue, Struct } from '@bufbuild/protobuf';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SystemService } from './system.service';
|
||||
|
||||
describe('SystemService', () => {
|
||||
let service: SystemService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(SystemService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
22
internal/frontend/holos/src/app/services/system.service.ts
Normal file
22
internal/frontend/holos/src/app/services/system.service.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { Observable, of, switchMap } from 'rxjs';
|
||||
import { ObservableClient } from '../../connect/observable-client';
|
||||
import { Version } from '../gen/holos/system/v1alpha1/system_pb';
|
||||
import { SystemService as ConnectSystemService } from '../gen/holos/system/v1alpha1/system_service_connect';
|
||||
import { GetVersionRequest } from '../gen/holos/system/v1alpha1/system_service_pb';
|
||||
import { FieldMask } from '@bufbuild/protobuf';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SystemService {
|
||||
getVersion(): Observable<Version | undefined> {
|
||||
const fieldMask = new FieldMask({ paths: ["version", "git_commit", "go_version", "os", "arch"] })
|
||||
const req = new GetVersionRequest({ fieldMask: fieldMask })
|
||||
return this.client.getVersion(req).pipe(
|
||||
switchMap(resp => { return of(resp.version) })
|
||||
)
|
||||
}
|
||||
|
||||
constructor(@Inject(ConnectSystemService) private client: ObservableClient<typeof ConnectSystemService>) { }
|
||||
}
|
||||
8
internal/frontend/holos/src/app/truncate.pipe.spec.ts
Normal file
8
internal/frontend/holos/src/app/truncate.pipe.spec.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { TruncatePipe } from './truncate.pipe';
|
||||
|
||||
describe('TruncatePipe', () => {
|
||||
it('create an instance', () => {
|
||||
const pipe = new TruncatePipe();
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
});
|
||||
13
internal/frontend/holos/src/app/truncate.pipe.ts
Normal file
13
internal/frontend/holos/src/app/truncate.pipe.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'truncate',
|
||||
standalone: true
|
||||
})
|
||||
export class TruncatePipe implements PipeTransform {
|
||||
|
||||
transform(value: string, limit: number = 8): string {
|
||||
if (!value) return '';
|
||||
return value.length > limit ? value.substring(0, limit) : value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
@if (version$ | async; as version) {
|
||||
<button mat-button [matMenuTriggerFor]="menu">
|
||||
{{ version.version }}
|
||||
</button>
|
||||
|
||||
<mat-menu class="version-menu" #menu="matMenu">
|
||||
<mat-card class="version-card">
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{ version.version }}</mat-card-title>
|
||||
<mat-card-subtitle>Server version info</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<pre>Git: {{ version.gitCommit | truncate }}</pre>
|
||||
<pre>Go: {{ version.goVersion | truncate }}</pre>
|
||||
<pre>OS: {{ version.os | truncate }}</pre>
|
||||
<pre>Arch: {{ version.arch | truncate }}</pre>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-button (click)="refreshVersion()" [disabled]="isLoading">Refresh</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</mat-menu>
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { VersionButtonComponent } from './version-button.component';
|
||||
|
||||
describe('VersionButtonComponent', () => {
|
||||
let component: VersionButtonComponent;
|
||||
let fixture: ComponentFixture<VersionButtonComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [VersionButtonComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(VersionButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
import { AsyncPipe, NgIf, NgStyle } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { Observable, Subject, of, startWith, switchMap, takeUntil } from 'rxjs';
|
||||
import { Version } from '../gen/holos/system/v1alpha1/system_pb';
|
||||
import { SystemService } from '../services/system.service';
|
||||
import { TruncatePipe } from '../truncate.pipe';
|
||||
import { MatDivider } from '@angular/material/divider';
|
||||
|
||||
@Component({
|
||||
selector: 'app-version-button',
|
||||
standalone: true,
|
||||
imports: [
|
||||
AsyncPipe,
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatDivider,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
NgIf,
|
||||
NgStyle,
|
||||
TruncatePipe,
|
||||
],
|
||||
templateUrl: './version-button.component.html',
|
||||
styleUrl: './version-button.component.scss'
|
||||
})
|
||||
export class VersionButtonComponent implements OnInit, OnDestroy {
|
||||
private destroy$: Subject<boolean> = new Subject<boolean>();
|
||||
private refreshVersion$ = new Subject<boolean>();
|
||||
private systemService = inject(SystemService);
|
||||
version$!: Observable<Version | undefined>;
|
||||
isLoading = false;
|
||||
|
||||
refreshVersion(): void {
|
||||
this.refreshVersion$.next(true);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.version$ = this.refreshVersion$.pipe(
|
||||
takeUntil(this.destroy$),
|
||||
startWith(true),
|
||||
switchMap(() => {
|
||||
this.isLoading = true;
|
||||
return this.systemService.getVersion().pipe(
|
||||
switchMap((version) => { this.isLoading = false; return of(version); })
|
||||
);
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.destroy$.next(true);
|
||||
this.destroy$.complete();
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,14 @@ import { Component, Input, OnDestroy, inject } from '@angular/core';
|
||||
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButton } from '@angular/material/button';
|
||||
import { MatDivider } from '@angular/material/divider';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTab, MatTabGroup } from '@angular/material/tabs';
|
||||
import { JsonValue } from '@bufbuild/protobuf';
|
||||
import { FormlyFieldConfig, FormlyFormOptions, FormlyModule } from '@ngx-formly/core';
|
||||
import { FormlyMaterialModule } from '@ngx-formly/material';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
import { PlatformService } from '../../services/platform.service';
|
||||
import { Platform } from '../../gen/holos/platform/v1alpha1/platform_pb';
|
||||
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { PlatformService } from '../../services/platform.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-platform-detail',
|
||||
|
||||
97
internal/generate/generate.go
Normal file
97
internal/generate/generate.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
)
|
||||
|
||||
//go:embed all:platforms
|
||||
var platforms embed.FS
|
||||
|
||||
// root is the root path to copy platform cue code from.
|
||||
const root = "platforms"
|
||||
|
||||
// Platforms returns a slice of embedded platforms or nil if there are none.
|
||||
func Platforms() []string {
|
||||
entries, err := fs.ReadDir(platforms, root)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
dirs := make([]string, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() && entry.Name() != "cue.mod" {
|
||||
dirs = append(dirs, entry.Name())
|
||||
}
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
// GeneratePlatform writes the cue code for a platform to the local working
|
||||
// directory.
|
||||
func GeneratePlatform(ctx context.Context, name string) error {
|
||||
// Check for a valid platform
|
||||
platformPath := filepath.Join(root, name)
|
||||
if !dirExists(platforms, platformPath) {
|
||||
return errors.Wrap(fmt.Errorf("cannot generate: have: [%s] want: %+v", name, Platforms()))
|
||||
}
|
||||
|
||||
// Copy the cue.mod directory
|
||||
if err := copyEmbedFS(ctx, platforms, filepath.Join(root, "cue.mod"), "cue.mod"); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// Copy the named platform
|
||||
if err := copyEmbedFS(ctx, platforms, platformPath, "."); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dirExists(srcFS embed.FS, path string) bool {
|
||||
entries, err := fs.ReadDir(srcFS, path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return len(entries) > 0
|
||||
}
|
||||
|
||||
func copyEmbedFS(ctx context.Context, srcFS embed.FS, srcPath, dstPath string) error {
|
||||
log := logger.FromContext(ctx)
|
||||
return fs.WalkDir(srcFS, srcPath, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
relPath, err := filepath.Rel(srcPath, path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
dstFullPath := filepath.Join(dstPath, relPath)
|
||||
|
||||
if d.IsDir() {
|
||||
if err := os.MkdirAll(dstFullPath, os.ModePerm); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.DebugContext(ctx, "created", "directory", dstFullPath)
|
||||
} else {
|
||||
data, err := srcFS.ReadFile(path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if err := os.WriteFile(dstFullPath, data, os.ModePerm); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.DebugContext(ctx, "wrote", "file", dstFullPath)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user