Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing JFrog CLI AI commands assistant #2703

Merged
merged 21 commits into from
Sep 24, 2024
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
2 changes: 1 addition & 1 deletion build/npm/v2-jf/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion build/npm/v2-jf/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jfrog-cli-v2-jf",
"version": "2.68.0",
"version": "2.69.0",
"description": "🐸 Command-line interface for JFrog Artifactory, Xray, Distribution, Pipelines and Mission Control 🐸",
"homepage": "https://github.com/jfrog/jfrog-cli",
"preferGlobal": true,
Expand Down
2 changes: 1 addition & 1 deletion build/npm/v2/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion build/npm/v2/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jfrog-cli-v2",
"version": "2.68.0",
"version": "2.69.0",
"description": "🐸 Command-line interface for JFrog Artifactory, Xray, Distribution, Pipelines and Mission Control 🐸",
"homepage": "https://github.com/jfrog/jfrog-cli",
"preferGlobal": true,
Expand Down
2 changes: 1 addition & 1 deletion docs/general/ai/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package ai
var Usage = []string{"how"}

func GetDescription() string {
return "This AI-based interface converts your natural language inputs into fully functional JFrog CLI commands. This is an interactive command that accepts no arguments."
return "An AI-powered interface that converts natural language inputs into AI-generated JFrog CLI commands."
}
36 changes: 32 additions & 4 deletions general/ai/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import (
type ApiCommand string

const (
cliAiAppApiUrl = "https://cli-ai-app-stg.jfrog.info/api/"
cliAiAppApiUrl = "https://cli-ai-app.jfrog.io/api/"
askRateLimitHeader = "X-JFrog-CLI-AI"
// The latest version of the terms and conditions for using the AI interface. (https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli/cli-ai/terms)
aiTermsRevision = 1
)

type ApiType string
Expand All @@ -41,9 +43,16 @@ func HowCmd(c *cli.Context) error {
if c.NArg() > 0 {
return cliutils.WrongNumberOfArgumentsHandler(c)
}
log.Output(coreutils.PrintLink("This AI-based interface converts your natural language inputs into fully functional JFrog CLI commands.\n" +
log.Output(coreutils.PrintLink("This AI-powered interface converts natural language inputs into AI-generated JFrog CLI commands.\n" +
"For more information about this interface, see https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli/cli-ai\n" +
"NOTE: This is an experimental version and it supports mostly Artifactory and Xray commands.\n"))

// Ask the user to agree to the terms and conditions. If the user does not agree, the command will not proceed.
// Ask this only once per JFrog CLI installation, unless the terms are updated.
if agreed, err := handleAiTermsAgreement(); err != nil || !agreed {
return err
}

for {
var question string
scanner := bufio.NewScanner(os.Stdin)
Expand Down Expand Up @@ -147,12 +156,12 @@ func sendRestAPI(apiType ApiType, content interface{}) (response string, err err
if err = errorutils.CheckResponseStatus(resp, http.StatusOK); err != nil {
switch resp.StatusCode {
case http.StatusInternalServerError:
err = errorutils.CheckErrorf("CLI-AI model endpoint is not available. Please try again later.")
err = errorutils.CheckErrorf("JFrog CLI-AI model endpoint is not available. Please try again later.")
case http.StatusNotAcceptable:
err = errorutils.CheckErrorf("The system is currently handling multiple requests from other users\n" +
"Please try submitting your question again in a few minutes. Thank you for your patience!")
default:
err = errorutils.CheckErrorf("CLI-AI server is not available. Please check your network or try again later. Note that the this command is supported while inside JFrog's internal network only.\n" + err.Error())
err = errorutils.CheckErrorf("JFrog CLI-AI server is not available. Please check your network or try again later:\n" + err.Error())
}
return
}
Expand All @@ -176,3 +185,22 @@ func sendRestAPI(apiType ApiType, content interface{}) (response string, err err
response = strings.TrimSpace(string(body))
return
}

func handleAiTermsAgreement() (bool, error) {
latestTermsVer, err := cliutils.GetLatestAiTermsRevision()
if err != nil {
return false, err
}
if latestTermsVer == nil || *latestTermsVer < aiTermsRevision {
if !coreutils.AskYesNo("By using this interface, you agree to the terms of JFrog's AI Addendum on behalf of your organization as an active JFrog customer.\n"+
"Review these terms at "+coreutils.PrintLink("https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli/cli-ai/terms")+
"\nDo you agree?", false) {
return false, nil
}
if err = cliutils.SetLatestAiTermsRevision(aiTermsRevision); err != nil {
return false, err
}
log.Output()
}
return true, nil
}
12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ replace (
require (
github.com/agnivade/levenshtein v1.1.1
github.com/buger/jsonparser v1.1.1
github.com/docker/docker v27.2.1+incompatible
github.com/docker/docker v27.3.1+incompatible
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1
github.com/jfrog/archiver/v3 v3.6.1
github.com/jfrog/build-info-go v1.10.0
Expand Down Expand Up @@ -148,11 +148,11 @@ require (
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/otel/sdk v1.29.0 // indirect
go.opentelemetry.io/otel/trace v1.29.0 // indirect
go.opentelemetry.io/otel v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 // indirect
go.opentelemetry.io/otel/metric v1.30.0 // indirect
go.opentelemetry.io/otel/sdk v1.30.0 // indirect
go.opentelemetry.io/otel/trace v1.30.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/mod v0.21.0 // indirect
Expand Down
40 changes: 20 additions & 20 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -707,8 +707,8 @@ github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v27.2.1+incompatible h1:fQdiLfW7VLscyoeYEBz7/J8soYFDZV1u6VW6gJEjNMI=
github.com/docker/docker v27.2.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
Expand Down Expand Up @@ -1195,18 +1195,18 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 h1:JAv0Jwtl01UFiyWZEMiJZBiTlv5A50zNs8lsthXqIio=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0/go.mod h1:QNKLmUEAq2QUbPQUfvw4fmv0bgbK7UlOSFCnXyfvSNc=
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo=
go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts=
go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0/go.mod h1:KQsVNh4OjgjTG0G6EiNi1jVpnaeeKsKMRwbLN+f1+8M=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 h1:umZgi92IyxfXd/l4kaDhnKgY8rnN/cZcF1LKc6I8OQ8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0/go.mod h1:4lVs6obhSVRb1EW5FhOuBTyiQhtRtAnnva9vD3yRfq8=
go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w=
go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ=
go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE=
go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg=
go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc=
go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
Expand Down Expand Up @@ -1836,15 +1836,15 @@ google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo=
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g=
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
Expand Down Expand Up @@ -1886,8 +1886,8 @@ google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM=
google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
Expand Down
2 changes: 0 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,12 +274,10 @@ func getCommands() ([]cli.Command, error) {
Action: login.LoginCmd,
},
{
Hidden: true,
Name: "how",
Usage: aiDocs.GetDescription(),
HelpName: corecommon.CreateUsage("how", aiDocs.GetDescription(), aiDocs.Usage),
BashComplete: corecommon.CreateBashCompletionFunc(),
Category: otherCategory,
Action: ai.HowCmd,
},
{
Expand Down
1 change: 1 addition & 0 deletions npm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func testNpm(t *testing.T, isLegacy bool) {
assert.NoError(t, err)
return
}
log.Info("npm version:", npmVersion.GetVersion())
isNpm7 := isNpm7(npmVersion)

// Temporarily change the cache folder to a temporary folder - to make sure the cache is clean and dependencies will be downloaded from Artifactory
Expand Down
2 changes: 1 addition & 1 deletion utils/cliutils/cli_consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "time"

const (
// General CLI constants
CliVersion = "2.68.0"
CliVersion = "2.69.0"
ClientAgent = "jfrog-cli-go"

// CLI base commands constants:
Expand Down
136 changes: 136 additions & 0 deletions utils/cliutils/persistence.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package cliutils

import (
"encoding/json"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
"os"
"path/filepath"
gosync "sync"
)

const persistenceFileName = "persistence.json"

// PersistenceFile holds various indicators that need to be persisted between CLI runs
type PersistenceFile struct {
LatestCliVersionCheckTime *int64 `json:"latestCliVersionCheckTime,omitempty"`
LatestAiTermsRevision *int `json:"latestAiTermsRevision,omitempty"`
}

var (
persistenceFilePath string
fileLock gosync.Mutex
)

// getPersistenceFilePath ensures that the persistence file path is initialized
func getPersistenceFilePath() error {
if persistenceFilePath == "" {
homeDir, err := coreutils.GetJfrogHomeDir()
if err != nil {
return errorutils.CheckErrorf("failed to get JFrog home directory: " + err.Error())
}
persistenceFilePath = filepath.Join(homeDir, persistenceFileName)
}
return nil
}

// setCliLatestVersionCheckTime updates the latest version check time in the persistence file
func setCliLatestVersionCheckTime(timestamp int64) error {
fileLock.Lock()
defer fileLock.Unlock()

info, err := getPersistenceInfo()
if err != nil {
return err
}

info.LatestCliVersionCheckTime = &timestamp
return setPersistenceInfo(info)
}

// getLatestCliVersionCheckTime retrieves the latest version check time from the persistence file
func getLatestCliVersionCheckTime() (*int64, error) {
fileLock.Lock()
defer fileLock.Unlock()

info, err := getPersistenceInfo()
if err != nil {
return nil, err
}

return info.LatestCliVersionCheckTime, nil
}

// SetLatestAiTermsRevision updates the AI terms version in the persistence file
func SetLatestAiTermsRevision(version int) error {
sverdlov93 marked this conversation as resolved.
Show resolved Hide resolved
fileLock.Lock()
defer fileLock.Unlock()

info, err := getPersistenceInfo()
if err != nil {
return err
}

info.LatestAiTermsRevision = &version
return setPersistenceInfo(info)
}

// GetLatestAiTermsRevision retrieves the AI terms version from the persistence file
func GetLatestAiTermsRevision() (*int, error) {
sverdlov93 marked this conversation as resolved.
Show resolved Hide resolved
fileLock.Lock()
defer fileLock.Unlock()

info, err := getPersistenceInfo()
if err != nil {
return nil, err
}

return info.LatestAiTermsRevision, nil
}

// getPersistenceInfo reads the persistence file, creates it if it doesn't exist, and returns the persisted info
func getPersistenceInfo() (*PersistenceFile, error) {
if err := getPersistenceFilePath(); err != nil {
return nil, err
}
if exists, err := fileutils.IsFileExists(persistenceFilePath, false); err != nil || !exists {
if err != nil {
return nil, err
}
// Create an empty persistence file if it doesn't exist
pFile := &PersistenceFile{}
if err = setPersistenceInfo(pFile); err != nil {
return nil, errorutils.CheckErrorf("failed while attempting to initialize persistence file: " + err.Error())
}
return pFile, nil
}

data, err := os.ReadFile(persistenceFilePath)
if err != nil {
return nil, errorutils.CheckErrorf("failed while attempting to read persistence file: " + err.Error())
}

var info PersistenceFile
if err = json.Unmarshal(data, &info); err != nil {
return nil, errorutils.CheckErrorf("failed while attempting to parse persistence file: " + err.Error())
}

return &info, nil
}

// setPersistenceInfo writes the given info to the persistence file
func setPersistenceInfo(info *PersistenceFile) error {
if err := getPersistenceFilePath(); err != nil {
return err
}
data, err := json.MarshalIndent(info, "", " ")
if err != nil {
return errorutils.CheckErrorf("failed while attempting to create persistence file: " + err.Error())
}

if err = os.WriteFile(persistenceFilePath, data, 0644); err != nil {
return errorutils.CheckErrorf("failed while attempting to write persistence file: " + err.Error())
}
return nil
}
Loading
Loading