Skip to content

Commit

Permalink
attempt to make a dev build release path
Browse files Browse the repository at this point in the history
  • Loading branch information
aybabtme committed Oct 10, 2024
1 parent 6f63b9c commit b6e841f
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 77 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }}
- run: echo "${HOME}/.humanlog/bin" >> $GITHUB_PATH
- run: curl https://humanlog.io/install_apictl.sh | bash
- run: curl https://humanlog.dev/install_apictl.sh | bash
- run: ./script/create_version_artifacts.sh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .goreleaser-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ archives:
checksum:
name_template: "checksums.txt"
snapshot:
version_template: "{{ incpatch .Version }}-next.{{ .Timestamp }}"
version_template: "{{ incpatch .Version }}-next.{{ .Timestamp }}.{{ .ShortCommit }}"
changelog:
sort: asc
filters:
Expand Down
3 changes: 2 additions & 1 deletion cmd/humanlog/localhost.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,11 @@ func startLocalhostServer(
cfg *config.Config,
state *state.State,
machineID uint64,
port int,
localhostHttpClient *http.Client,
ownVersion *typesv1.Version,
) (localsink sink.Sink, done func(context.Context) error, err error) {
localhostAddr := net.JoinHostPort("localhost", strconv.Itoa(*cfg.ExperimentalServeLocalhostOnPort))
localhostAddr := net.JoinHostPort("localhost", strconv.Itoa(port))

l, err := net.Listen("tcp", localhostAddr)
if err != nil && !isEADDRINUSE(err) {
Expand Down
131 changes: 77 additions & 54 deletions cmd/humanlog/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"strings"
"time"

"github.com/99designs/keyring"
"github.com/aybabtme/rgbterm"
"github.com/blang/semver"
types "github.com/humanlogio/api/go/types/v1"
Expand Down Expand Up @@ -211,15 +212,32 @@ func newApp() *cli.App {
}
promptedToUpdate *semver.Version
updateRes <-chan *checkForUpdateRes
apiURL = "https://api.humanlog.io"

getCtx = func(*cli.Context) context.Context { return ctx }
getLogger = func(*cli.Context) *slog.Logger { return slog.New(slog.NewJSONHandler(os.Stderr, nil)) }
getCfg = func(*cli.Context) *config.Config { return cfg }
getState = func(*cli.Context) *state.State { return statefile }
getTokenSource = func(*cli.Context) *auth.UserRefreshableTokenSource { return auth.NewRefreshableTokenSource() }
getAPIUrl = func(*cli.Context) string { logdebug("using api at %q", apiURL); return apiURL }
getHTTPClient = func(cctx *cli.Context) *http.Client {
apiURL = ""
keyringName = "humanlog"

getCtx = func(*cli.Context) context.Context { return ctx }
getLogger = func(*cli.Context) *slog.Logger { return slog.New(slog.NewJSONHandler(os.Stderr, nil)) }
getCfg = func(*cli.Context) *config.Config { return cfg }
getState = func(*cli.Context) *state.State { return statefile }
getKeyring = func(cctx *cli.Context) (keyring.Keyring, error) {
stateDir, err := state.GetDefaultStateDirpath()
if err != nil {
return nil, err
}
return keyring.Open(keyring.Config{
ServiceName: keyringName,
KeychainSynchronizable: true,
FileDir: stateDir,
})

}
getTokenSource = func(cctx *cli.Context) *auth.UserRefreshableTokenSource {
return auth.NewRefreshableTokenSource(func() (keyring.Keyring, error) {
return getKeyring(cctx)
})
}
getAPIUrl = func(*cli.Context) string { logdebug("using api at %q", apiURL); return apiURL }
getHTTPClient = func(cctx *cli.Context) *http.Client {
u, _ := url.Parse(apiURL)
if host, _, _ := net.SplitHostPort(u.Host); host == "localhost" {
getLogger(cctx).Debug("using localhost client")
Expand Down Expand Up @@ -377,56 +395,61 @@ func newApp() *cli.App {
sink = stdiosink.NewStdio(colorable.NewColorableStdout(), sinkOpts)
handlerOpts := humanlog.HandlerOptionsFrom(*cfg)

if apiURL := getAPIUrl(cctx); apiURL != "" {
// TODO(antoine): remove this codepath, it's redundant with the localhost port path
ll := getLogger(cctx)
remotesink, err := ingest(ctx, ll, cctx, apiURL, getCfg, getState, getTokenSource, getHTTPClient)
if err != nil {
return fmt.Errorf("can't send logs: %v", err)
}
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
defer cancel()
if err := remotesink.Flush(ctx); err != nil {
ll.ErrorContext(ctx, "couldn't flush buffered log", slog.Any("err", err))
} else {
ll.InfoContext(ctx, "done sending all logs")
}
}()
loginfo("saving to %s", apiURL)
sink = teesink.NewTeeSink(sink, remotesink)
}
if cfg.ExperimentalServeLocalhostOnPort != nil {
state := getState(cctx)
// TODO(antoine): all logs to a single location, right now there's code logging
// randomly everywhere
ll := getLogger(cctx)
var machineID uint64
for state.MachineID == nil {
// no machine ID assigned, ensure machine gets onboarded via the loggin flow
// TODO(antoine): if an account token exists, auto-onboard the machine. it's probably
// not an interactive session
_, err := ensureLoggedIn(ctx, cctx, state, getTokenSource(cctx), apiURL, getHTTPClient(cctx))
if cfg.ExperimentalFeatures != nil {
if cfg.ExperimentalFeatures.SendLogsToCloud != nil && *cfg.ExperimentalFeatures.SendLogsToCloud {
// TODO(antoine): remove this codepath, it's redundant with the localhost port path
ll := getLogger(cctx)
apiURL := getAPIUrl(cctx)
remotesink, err := ingest(ctx, ll, cctx, apiURL, getCfg, getState, getTokenSource, getHTTPClient)
if err != nil {
return fmt.Errorf("this feature requires a valid machine ID, which requires an account. failed to login: %v", err)
return fmt.Errorf("can't send logs: %v", err)
}
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
defer cancel()
if err := remotesink.Flush(ctx); err != nil {
ll.ErrorContext(ctx, "couldn't flush buffered log", slog.Any("err", err))
} else {
ll.InfoContext(ctx, "done sending all logs")
}
}()
loginfo("saving to %s", apiURL)
sink = teesink.NewTeeSink(sink, remotesink)
}
machineID = uint64(*state.MachineID)
localhostSink, done, err := startLocalhostServer(ctx, ll, cfg, state, machineID, getLocalhostHTTPClient(cctx), version)
if err != nil {
loginfo("starting experimental localhost service: %v", err)
} else {
sink = teesink.NewTeeSink(sink, localhostSink)
}
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
defer cancel()
if err := done(ctx); err != nil {
ll.ErrorContext(ctx, "couldn't flush buffered log (localhost)", slog.Any("err", err))

if cfg.ExperimentalFeatures.ServeLocalhostOnPort != nil {
port := *cfg.ExperimentalFeatures.ServeLocalhostOnPort
state := getState(cctx)
// TODO(antoine): all logs to a single location, right now there's code logging
// randomly everywhere
ll := getLogger(cctx)
var machineID uint64
for state.MachineID == nil {
// no machine ID assigned, ensure machine gets onboarded via the loggin flow
// TODO(antoine): if an account token exists, auto-onboard the machine. it's probably
// not an interactive session
_, err := ensureLoggedIn(ctx, cctx, state, getTokenSource(cctx), apiURL, getHTTPClient(cctx))
if err != nil {
return fmt.Errorf("this feature requires a valid machine ID, which requires an account. failed to login: %v", err)
}
}
machineID = uint64(*state.MachineID)
localhostSink, done, err := startLocalhostServer(ctx, ll, cfg, state, machineID, port, getLocalhostHTTPClient(cctx), version)
if err != nil {
loginfo("starting experimental localhost service: %v", err)
} else {
ll.InfoContext(ctx, "done sending all logs")
sink = teesink.NewTeeSink(sink, localhostSink)
}
}()
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
defer cancel()
if err := done(ctx); err != nil {
ll.ErrorContext(ctx, "couldn't flush buffered log (localhost)", slog.Any("err", err))
} else {
ll.InfoContext(ctx, "done sending all logs")
}
}()
}
}

loginfo("reading stdin...")
Expand Down
7 changes: 6 additions & 1 deletion internal/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ type Config struct {
Interrupt *bool `json:"interrupt"`
SkipCheckForUpdates *bool `json:"skip_check_updates"`

ExperimentalServeLocalhostOnPort *int `json:"experimental_serve_localhost_on_port"`
ExperimentalFeatures *Features `json:"experimental_features"`
}

type Features struct {
SendLogsToCloud *bool `json:"send_logs_to_cloud"`
ServeLocalhostOnPort *int `json:"serve_localhost_on_port"`
}

func (cfg Config) populateEmpty(other *Config) *Config {
Expand Down
14 changes: 11 additions & 3 deletions internal/pkg/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,20 @@ var DefaultState = State{
Version: 1,
}

func GetDefaultStateFilepath() (string, error) {
func GetDefaultStateDirpath() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("$HOME not set, can't determine a state file path")
return "", fmt.Errorf("$HOME not set, can't determine a state dir path")
}
stateDirpath := filepath.Join(home, ".state", "humanlog")
return stateDirpath, nil
}

func GetDefaultStateFilepath() (string, error) {
stateDirpath, err := GetDefaultStateDirpath()
if err != nil {
return "", err
}
stateFilepath := filepath.Join(stateDirpath, "state.json")
dfi, err := os.Stat(stateDirpath)
if err != nil && !errors.Is(err, os.ErrNotExist) {
Expand Down Expand Up @@ -105,7 +113,7 @@ type State struct {

IngestionToken *typesv1.AccountToken `json:"ingestion_token,omitempty"`

// preferences set in the TUI when querying
// preferences set in the CLI/TUI when querying
CurrentOrgID *int64 `json:"current_org_id,omitempty"`
CurrentAccountID *int64 `json:"current_account_id,omitempty"`
CurrentMachineID *int64 `json:"current_machine_id,omitempty"`
Expand Down
23 changes: 7 additions & 16 deletions pkg/auth/token_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,22 @@ import (
)

const (
KeyringName = "humanlog"
UserTokenKeyringKey = "user-token"
)

type UserRefreshableTokenSource struct {
utmu sync.Mutex
ut *typesv1.UserToken

getKeyring func() (keyring.Keyring, error)
}

func NewRefreshableTokenSource() *UserRefreshableTokenSource {
return &UserRefreshableTokenSource{}
func NewRefreshableTokenSource(getKeyring func() (keyring.Keyring, error)) *UserRefreshableTokenSource {
return &UserRefreshableTokenSource{getKeyring: getKeyring}
}

func (rts *UserRefreshableTokenSource) ClearToken(ctx context.Context) error {
ring, err := keyring.Open(keyring.Config{
ServiceName: KeyringName,
KeychainSynchronizable: true,
})
ring, err := rts.getKeyring()
if err != nil {
return fmt.Errorf("opening keyring: %v", err)
}
Expand All @@ -49,11 +47,7 @@ func (rts *UserRefreshableTokenSource) GetUserToken(ctx context.Context) (*types
if rts.ut != nil {
return rts.ut, nil
}

ring, err := keyring.Open(keyring.Config{
ServiceName: KeyringName,
KeychainSynchronizable: true,
})
ring, err := rts.getKeyring()
if err != nil {
return nil, fmt.Errorf("opening keyring: %v", err)
}
Expand Down Expand Up @@ -86,10 +80,7 @@ func (rts *UserRefreshableTokenSource) SetUserToken(ctx context.Context, userTok
if err != nil {
return fmt.Errorf("marshaling token to proto: %v", err)
}
ring, err := keyring.Open(keyring.Config{
ServiceName: KeyringName,
KeychainSynchronizable: true,
})
ring, err := rts.getKeyring()
if err != nil {
return fmt.Errorf("opening keyring: %v", err)
}
Expand Down

0 comments on commit b6e841f

Please sign in to comment.