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: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ tmp/
.vagrant/
# IDEs
.vscode/
# AI files and directories
.cmsis-dev
.plans
.codex
7 changes: 4 additions & 3 deletions cmd/commands/update_index.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,16 @@ var UpdateIndexCmd = &cobra.Command{
if err != nil {
return err
}

installer.UnlockPackRoot()
defer installer.LockPackRoot()
if err := installer.ReadIndexFiles(); err != nil {
return err
}

err = installer.UpdatePublicIndex("", updateIndexCmdFlags.sparse, false, updateIndexCmdFlags.downloadUpdatePdscFiles, !updateIndexCmdFlags.includeDeprecated, true, true, updateIndexCmdFlags.insecureSkipVerify, viper.GetInt("concurrent-downloads"), viper.GetInt("timeout"))
return err
if err := installer.UpdatePublicIndex("", updateIndexCmdFlags.sparse, false, updateIndexCmdFlags.downloadUpdatePdscFiles, !updateIndexCmdFlags.includeDeprecated, true, true, updateIndexCmdFlags.insecureSkipVerify, viper.GetInt("concurrent-downloads"), viper.GetInt("timeout")); err != nil {
return err
}
return installer.RecordPublicIndexUpdate()
},
}

Expand Down
31 changes: 31 additions & 0 deletions cmd/commands/update_index_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: Apache-2.0 */
/* Copyright Contributors to the cpackget project. */

package commands

import (
"errors"
"path/filepath"
"testing"

errs "github.com/open-cmsis-pack/cpackget/cmd/errors"
viperType "github.com/spf13/viper"
)

func TestUpdateIndexRejectsMissingPackRoot(t *testing.T) {
originalViper := viper
originalCreatePackRoot := createPackRoot
t.Cleanup(func() {
viper = originalViper
createPackRoot = originalCreatePackRoot
})

viper = viperType.New()
viper.Set("pack-root", filepath.Join(t.TempDir(), "missing"))
createPackRoot = false

err := UpdateIndexCmd.RunE(UpdateIndexCmd, nil)
if !errors.Is(err, errs.ErrPackRootDoesNotExist) {
t.Fatalf("expected ErrPackRootDoesNotExist, got %v", err)
}
}
58 changes: 48 additions & 10 deletions cmd/commands/update_index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import (
"errors"
"fmt"
"os"
"path/filepath"
"testing"
"time"

errs "github.com/open-cmsis-pack/cpackget/cmd/errors"
"github.com/open-cmsis-pack/cpackget/cmd/installer"
)

Expand Down Expand Up @@ -42,9 +45,23 @@ var updateIndexCmdTests = []TestCase{
</index>`
indexContent = fmt.Sprintf(indexContent, updateIndexServer.URL())
_ = os.WriteFile(installer.Installation.PublicIndex, []byte(indexContent), 0600)
oldDate := time.Now().AddDate(0, 0, -2).Format("2-1-2006")
updateCfgPath := filepath.Join(installer.Installation.WebDir, "update.cfg")
_ = os.WriteFile(updateCfgPath, []byte("Date="+oldDate+"\nAuto=false\nUpdateDaily=false\n"), 0600)

updateIndexServer.AddRoute(installer.PublicIndexName, []byte(indexContent))
},
validationFunc: func(t *testing.T) {
updateCfgPath := filepath.Join(installer.Installation.WebDir, "update.cfg")
content, err := os.ReadFile(updateCfgPath)
if err != nil {
t.Fatal(err)
}
expected := "Date=" + time.Now().Format("2-1-2006") + "\nAuto=false\nUpdateDaily=false\n"
if string(content) != expected {
t.Fatalf("unexpected update.cfg content: %q", content)
}
},
},
{
name: "test updating index",
Expand All @@ -66,26 +83,47 @@ var updateIndexCmdTests = []TestCase{

updateIndexServer.AddRoute(installer.PublicIndexName, []byte(indexContent))
},
validationFunc: func(t *testing.T) {
updateCfgPath := filepath.Join(installer.Installation.WebDir, "update.cfg")
content, err := os.ReadFile(updateCfgPath)
if err != nil {
t.Fatal(err)
}
expected := "Date=" + time.Now().Format("2-1-2006") + "\nAuto=true\nUpdateDaily=true\n"
if string(content) != expected {
t.Fatalf("unexpected update.cfg content: %q", content)
}
},
},
{
name: "test updating index with insecure-skip-verify flag",
args: []string{"update-index", "--insecure-skip-verify"},
name: "test malformed index returns read error",
args: []string{"update-index"},
createPackRoot: true,
expectedStdout: []string{"Updating public index", "Downloading " + installer.PublicIndexName},
setUpFunc: func(t *TestCase) {
setUpFunc: func(test *TestCase) {
if err := os.WriteFile(installer.Installation.PublicIndex, []byte("not xml"), 0o600); err != nil {
test.expectedErr = err
return
}
test.expectedErr = installer.Installation.PublicIndexXML.Read()
},
},
{
name: "test failed index update returns download error",
args: []string{"update-index"},
createPackRoot: true,
expectedErr: errs.ErrBadRequest,
expErrUnwrap: true,
setUpFunc: func(test *TestCase) {
indexContent := `<?xml version="1.0" encoding="UTF-8" ?>
<index schemaVersion="1.1.0" xs:noNamespaceSchemaLocation="PackIndex.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<vendor>TheVendor</vendor>
<url>%s</url>
<timestamp>2021-10-17T12:21:59.1747971+00:00</timestamp>
<pindex>
<pdsc url="http://the.vendor/" vendor="TheVendor" name="PackName" version="1.2.3" />
</pindex>
<pindex />
</index>`
indexContent = fmt.Sprintf(indexContent, updateIndexServer.URL())
_ = os.WriteFile(installer.Installation.PublicIndex, []byte(indexContent), 0600)

updateIndexServer.AddRoute(installer.PublicIndexName, []byte(indexContent))
_ = os.WriteFile(installer.Installation.PublicIndex, []byte(indexContent), 0o600)
updateIndexServer.AddRoute(installer.PublicIndexName, nil)
},
},
}
Expand Down
64 changes: 41 additions & 23 deletions cmd/installer/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
"sync"
"syscall"
Expand Down Expand Up @@ -962,7 +963,7 @@ func UpdatePublicIndexIfOnline() error {
if errors.Unwrap(err) != errs.ErrOffline {
var updateConf updateCfg
err = Installation.checkUpdateCfg(&updateConf, true)
if err != nil {
if err != nil && updateConf.UpdateDaily {
UnlockPackRoot()
err1 := UpdatePublicIndex(ActualPublicIndex, false, false, false, true, false, false, false, 0, 0)
if err1 != nil {
Expand All @@ -985,6 +986,7 @@ func UpdatePublicIndexIfOnline() error {
}
var updateConf updateCfg
updateConf.Auto = true
updateConf.UpdateDaily = true
_ = Installation.updateUpdateCfg(&updateConf) // create the update config file
}
return nil
Expand Down Expand Up @@ -1810,11 +1812,13 @@ type PacksInstallationType struct {

// updateCfg represents the content of "update.cfg" file.
// - Date: a string representing the date of the last update.
// - Auto: a boolean indicating whether automatic updates are enabled.
// - Auto: a legacy setting preserved for compatibility.
// - UpdateDaily: a boolean indicating whether automatic daily updates are enabled.
type updateCfg struct {
// Default struct {
Date string
Auto bool
Date string
Auto bool
UpdateDaily bool
// }
}

Expand All @@ -1833,6 +1837,8 @@ type updateCfg struct {
// "Date" field cannot be parsed, or if the timestamp in the "Date" field is older
// than 24 hours. If no errors occur, nil is returned.
func (p *PacksInstallationType) checkUpdateCfg(conf *updateCfg, WarningInsteadOfErrors bool) error {
conf.Auto = true
conf.UpdateDaily = true
f, err := os.Open(filepath.Join(p.WebDir, "update.cfg"))
if err != nil {
if WarningInsteadOfErrors {
Expand All @@ -1850,9 +1856,18 @@ func (p *PacksInstallationType) checkUpdateCfg(conf *updateCfg, WarningInsteadOf
if strings.HasPrefix(line, "Date=") {
conf.Date = strings.TrimPrefix(line, "Date=")
} else if strings.HasPrefix(line, "Auto=") {
conf.Auto = strings.TrimPrefix(line, "Auto=") == "true"
if auto, err := strconv.ParseBool(strings.TrimPrefix(line, "Auto=")); err == nil {
conf.Auto = auto
}
} else if strings.HasPrefix(line, "UpdateDaily=") {
if updateDaily, err := strconv.ParseBool(strings.TrimPrefix(line, "UpdateDaily=")); err == nil {
conf.UpdateDaily = updateDaily
}
}
}
if err := scanner.Err(); err != nil {
return err
}
if t, err := time.Parse("2-1-2006", conf.Date); err != nil {
return err
} else {
Expand All @@ -1873,32 +1888,35 @@ func (p *PacksInstallationType) checkUpdateCfg(conf *updateCfg, WarningInsteadOf
// - An error if there is an issue opening, writing to, or syncing the file; otherwise, nil.
func (p *PacksInstallationType) updateUpdateCfg(conf *updateCfg) error {
conf.Date = time.Now().Local().Format("2-1-2006")
flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY
f, err := os.OpenFile(filepath.Join(p.WebDir, "update.cfg"), flags, os.FileMode(0o644))
if err != nil {
return err
}
defer f.Close()
return p.writeUpdateCfg(conf)
}

if _, err := f.WriteString("Date=" + conf.Date + "\n"); err != nil {
return err
}
if _, err := f.WriteString("Auto="); err != nil {
func (p *PacksInstallationType) writeUpdateCfg(conf *updateCfg) (retErr error) {
content := "Date=" + conf.Date + "\n" +
"Auto=" + strconv.FormatBool(conf.Auto) + "\n" +
"UpdateDaily=" + strconv.FormatBool(conf.UpdateDaily) + "\n"
f, err := os.OpenFile(filepath.Join(p.WebDir, "update.cfg"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
if err != nil {
return err
}
if conf.Auto {
if _, err := f.WriteString("true\n"); err != nil {
return err
}
} else {
if _, err := f.WriteString("false\n"); err != nil {
return err
defer func() {
if cerr := f.Close(); retErr == nil && cerr != nil {
retErr = cerr
}
}()
if _, err := f.WriteString(content); err != nil {
return err
}

return f.Sync()
}

// RecordPublicIndexUpdate records a successful explicit public index update.
func RecordPublicIndexUpdate() error {
var updateConf updateCfg
_ = Installation.checkUpdateCfg(&updateConf, false)
return Installation.updateUpdateCfg(&updateConf)
}

// touchPackIdx updates the timestamp of the PackIdx file to the current time.
// If the skip touch flag is set, the function returns immediately without making any changes.
// The function temporarily removes the read-only attribute from the PackIdx file,
Expand Down
Loading
Loading