Scan() everything functions

Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
Jeff Carr 2024-01-23 15:20:54 -06:00
parent 2f4cba36dd
commit dae15b5931
7 changed files with 93 additions and 134 deletions

94
draw.go
View File

@ -1,11 +1,9 @@
package repostatus package repostatus
import ( import (
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"go.wit.com/gui"
"go.wit.com/lib/gadgets" "go.wit.com/lib/gadgets"
"go.wit.com/log" "go.wit.com/log"
"go.wit.com/widget" "go.wit.com/widget"
@ -13,22 +11,19 @@ import (
// creates the actual widgets. // creates the actual widgets.
// it's assumed you are always passing in a box // it's assumed you are always passing in a box
func (rs *RepoStatus) draw() { func (rs *RepoStatus) draw(goSrcPath string, realPath string) {
if !rs.Ready() { if !rs.Ready() {
return return
} }
// display the status of the git repository // display the status of the git repository
rs.drawGitStatus() rs.drawGitStatus(goSrcPath, realPath)
// display the git branches and options // display the git branches and options
rs.drawGitBranches() rs.drawGitBranches()
// show standard git commit and merge controls // show standard git commit and merge controls
rs.drawGitCommands() rs.drawGitCommands()
// figure out what the state of the git repository is
// rs.Update()
} }
func (rs *RepoStatus) drawGitBranches() { func (rs *RepoStatus) drawGitBranches() {
@ -36,19 +31,19 @@ func (rs *RepoStatus) drawGitBranches() {
newgrid := rs.gitBranchesGroup.NewGrid("gridnuts", 2, 2) newgrid := rs.gitBranchesGroup.NewGrid("gridnuts", 2, 2)
rs.masterDrop = gadgets.NewBasicDropdown(newgrid, "main branch") rs.masterDrop = gadgets.NewBasicDropdown(newgrid, "main branch")
rs.masterDrop.Custom = func () { rs.masterDrop.Custom = func() {
log.Log(WARN, "Switching main branch to:", rs.masterDrop.String()) log.Log(WARN, "Switching main branch to:", rs.masterDrop.String())
rs.masterBranchVersion.SetLabel(rs.masterDrop.String()) rs.masterBranchVersion.SetLabel(rs.masterDrop.String())
rs.mainWorkingName.SetValue(rs.masterDrop.String()) rs.mainWorkingName.SetValue(rs.masterDrop.String())
} }
rs.develDrop = gadgets.NewBasicDropdown(newgrid, "devel branch") rs.develDrop = gadgets.NewBasicDropdown(newgrid, "devel branch")
rs.develDrop.Custom = func () { rs.develDrop.Custom = func() {
log.Log(WARN, "Switching devel branch to:", rs.develDrop.String()) log.Log(WARN, "Switching devel branch to:", rs.develDrop.String())
rs.develBranchVersion.SetLabel(rs.develDrop.String()) rs.develBranchVersion.SetLabel(rs.develDrop.String())
rs.develWorkingName.SetValue(rs.develDrop.String()) rs.develWorkingName.SetValue(rs.develDrop.String())
} }
rs.userDrop = gadgets.NewBasicDropdown(newgrid, "user branch") rs.userDrop = gadgets.NewBasicDropdown(newgrid, "user branch")
rs.userDrop.Custom = func () { rs.userDrop.Custom = func() {
log.Log(WARN, "Switching user branch to:", rs.userDrop.String()) log.Log(WARN, "Switching user branch to:", rs.userDrop.String())
rs.userBranchVersion.SetLabel(rs.userDrop.String()) rs.userBranchVersion.SetLabel(rs.userDrop.String())
rs.userWorkingName.SetValue(rs.userDrop.String()) rs.userWorkingName.SetValue(rs.userDrop.String())
@ -63,6 +58,7 @@ func (rs *RepoStatus) drawGitBranches() {
rs.currentBranch = gadgets.NewOneLiner(newgrid, "current branch") rs.currentBranch = gadgets.NewOneLiner(newgrid, "current branch")
rs.currentVersion = gadgets.NewOneLiner(newgrid, "current version") rs.currentVersion = gadgets.NewOneLiner(newgrid, "current version")
/*
var master = "" var master = ""
all := rs.getBranches() all := rs.getBranches()
for _, branch := range all { for _, branch := range all {
@ -77,29 +73,24 @@ func (rs *RepoStatus) drawGitBranches() {
master = "main" master = "main"
} }
} }
*/
// relabel the various gadgets with the right branch name // relabel the various gadgets with the right branch name
rs.masterBranchVersion.SetLabel(master) // rs.masterBranchVersion.SetLabel(master)
var count *gui.Node rs.showBranchesButton = newgrid.NewButton("getBranches()", func() {
rs.showBranchesButton = newgrid.NewButton("show branches", func() {
all := rs.getBranches() all := rs.getBranches()
i := len(all) i := len(all)
count.SetText(strconv.Itoa(i) + " branches") log.Log(WARN, "branch count =", i)
}) })
count = newgrid.NewLabel("")
rs.checkBranchesButton = newgrid.NewButton("check branches", func() { rs.checkBranchesButton = newgrid.NewButton("CheckBranches()", func() {
if rs.CheckBranches() { if rs.CheckBranches() {
log.Log(INFO, "Branches are perfect") log.Log(WARN, "Branches are perfect")
} else { } else {
log.Log(INFO, "Branches are not perfect") log.Log(WARN, "Branches are not perfect")
} }
}) })
newgrid.NewButton("parse git and go config", func() {
ScanGoSrc()
})
newgrid.NewButton("show .git/config", func() { newgrid.NewButton("show .git/config", func() {
if rs.gitConfig == nil { if rs.gitConfig == nil {
@ -110,7 +101,7 @@ func (rs *RepoStatus) drawGitBranches() {
// The info: // The info:
for name, remote := range rs.gitConfig.remotes { for name, remote := range rs.gitConfig.remotes {
log.Log(WARN, " ", name, remote.url) log.Log(WARN, " ", name, "url:", remote.url)
} }
for name, branch := range rs.gitConfig.branches { for name, branch := range rs.gitConfig.branches {
log.Log(WARN, " ", name, "remote:", branch.remote, "merge", branch.merge) log.Log(WARN, " ", name, "remote:", branch.remote, "merge", branch.merge)
@ -131,54 +122,15 @@ func (rs *RepoStatus) drawGitBranches() {
}) })
} }
/* func (rs *RepoStatus) drawGitStatus(goSrcPath string, realPath string) {
func (rs *RepoStatus) ScanConfig(path string) {
log.Log(WARN, "repo =", path)
filename := filepath.Join(path, ".git/config")
rs.gitConfig, err := ReadGitConfig(filename)
if err != nil {
log.Log(WARN, "Error reading .git/config:", err)
continue
}
rs.goConfig, err := ReadGoMod(path)
}
*/
func ScanGoSrc() {
for i, path := range listGitDirectories() {
log.Log(WARN, "repo =", i, path)
filename := filepath.Join(path, ".git/config")
gitConfig, err := readGitConfig(filename)
if err != nil {
log.Log(WARN, "Error reading .git/config:", err)
continue
}
// Example of printing the parsed config
for section, options := range gitConfig.branches {
log.Log(WARN, "\t", section, options)
}
/*
_, err = ReadGoMod(path) // map[string]string {
if err != nil {
log.Log(WARN, "\tgo.sum scan failed")
}
*/
}
}
func (rs *RepoStatus) drawGitStatus() {
rs.gitStatusGroup = rs.window.Box().NewGroup("What GO Knows It Has") rs.gitStatusGroup = rs.window.Box().NewGroup("What GO Knows It Has")
newgrid := rs.gitStatusGroup.NewGrid("gridnuts", 2, 2) newgrid := rs.gitStatusGroup.NewGrid("gridnuts", 2, 2)
newgrid.Margin() newgrid.Margin()
newgrid.Pad() newgrid.Pad()
rs.path = gadgets.NewOneLiner(newgrid, "path") rs.path = gadgets.NewOneLiner(newgrid, "path")
rs.goSrcPath = gadgets.NewOneLiner(newgrid, "go/src") rs.goSrcPath = gadgets.NewOneLiner(newgrid, goSrcPath)
rs.realPath = gadgets.NewOneLiner(newgrid, "fullpath") rs.realPath = gadgets.NewOneLiner(newgrid, realPath)
rs.realPath.Hide() rs.realPath.Hide()
rs.mainWorkingName = gadgets.NewOneLiner(newgrid, "main working branch") rs.mainWorkingName = gadgets.NewOneLiner(newgrid, "main working branch")
rs.mainWorkingName.SetValue("???") rs.mainWorkingName.SetValue("???")
@ -191,15 +143,15 @@ func (rs *RepoStatus) drawGitStatus() {
// git for-each-ref --sort=taggerdate --format '%(tag) ,,,_,,, %(subject)' refs/tags // git for-each-ref --sort=taggerdate --format '%(tag) ,,,_,,, %(subject)' refs/tags
var cmd []string var cmd []string
cmd = append(cmd, "git", "for-each-ref", "--sort=taggerdate", "--format", "%(tag) %(subject)", "refs/tags") cmd = append(cmd, "git", "for-each-ref", "--sort=taggerdate", "--format", "%(tag) %(subject)", "refs/tags")
_, _, output := RunCmd("/home/jcarr/go/src/"+rs.repopath, cmd) _, _, output := RunCmd(rs.realPath.String(), cmd)
log.Log(INFO, output) log.Log(INFO, output)
for _, line := range strings.Split(output, "\n") { for _, line := range strings.Split(output, "\n") {
rs.tagsDrop.AddText(line) rs.tagsDrop.AddText(line)
} }
// rs.masterBranchVersion = gadgets.NewOneLiner(newgrid, "master") // rs.masterBranchVersion = gadgets.NewOneLiner(newgrid, "master")
// rs.develBranchVersion = gadgets.NewOneLiner(newgrid, "devel") // rs.develBranchVersion = gadgets.NewOneLiner(newgrid, "devel")
// rs.userBranchVersion = gadgets.NewOneLiner(newgrid, "user") // rs.userBranchVersion = gadgets.NewOneLiner(newgrid, "user")
rs.dirtyLabel = gadgets.NewOneLiner(newgrid, "dirty") rs.dirtyLabel = gadgets.NewOneLiner(newgrid, "dirty")
@ -218,7 +170,7 @@ func (rs *RepoStatus) drawGitCommands() {
newgrid.NewButton("git pull", func() { newgrid.NewButton("git pull", func() {
var cmd []string var cmd []string
cmd = append(cmd, "git", "pull") cmd = append(cmd, "git", "pull")
err, b, output := RunCmd("/home/jcarr/go/src/"+rs.repopath, cmd) err, b, output := RunCmd(rs.realPath.String(), cmd)
log.Warn("Did git pull here", err, b, output) log.Warn("Did git pull here", err, b, output)
}) })
@ -432,7 +384,7 @@ func (rs *RepoStatus) runGitCommands() bool {
s := strings.Join(line, " ") s := strings.Join(line, " ")
log.Log(INFO, "NEED TO RUN:", s) log.Log(INFO, "NEED TO RUN:", s)
rs.develMerge.SetText(s) rs.develMerge.SetText(s)
err, b, output := runCmd(rs.repopath, line) err, b, output := runCmd(rs.realPath.String(), line)
if err != nil { if err != nil {
log.Warn("ABEND EXECUTION") log.Warn("ABEND EXECUTION")
log.Warn("error =", err) log.Warn("error =", err)

52
git.go
View File

@ -10,11 +10,11 @@ import (
) )
func (rs *RepoStatus) String() string { func (rs *RepoStatus) String() string {
return rs.repopath return rs.goSrcPath.String()
} }
func (rs *RepoStatus) GetPath() string { func (rs *RepoStatus) GetPath() string {
return rs.repopath return rs.goSrcPath.String()
} }
func (rs *RepoStatus) GetCurrentBranchName() string { func (rs *RepoStatus) GetCurrentBranchName() string {
@ -30,27 +30,27 @@ func (rs *RepoStatus) GetLastTagVersion() string {
} }
func (rs *RepoStatus) getCurrentBranchName() string { func (rs *RepoStatus) getCurrentBranchName() string {
out := run(rs.repopath, "git", "branch --show-current") out := run(rs.realPath.String(), "git", "branch --show-current")
log.Log(INFO, "getCurrentBranchName() =", out) log.Log(INFO, "getCurrentBranchName() =", out)
rs.currentBranch.SetValue(out) rs.currentBranch.SetValue(out)
return out return out
} }
func (rs *RepoStatus) getCurrentBranchVersion() string { func (rs *RepoStatus) getCurrentBranchVersion() string {
out := run(rs.repopath, "git", "describe --tags") out := run(rs.realPath.String(), "git", "describe --tags")
log.Log(INFO, "getCurrentBranchVersion()", out) log.Log(INFO, "getCurrentBranchVersion()", out)
rs.currentVersion.SetValue(out) rs.currentVersion.SetValue(out)
return out return out
} }
func (rs *RepoStatus) getLastTagVersion() string { func (rs *RepoStatus) getLastTagVersion() string {
out := run(rs.repopath, "git", "rev-list --tags --max-count=1") out := run(rs.realPath.String(), "git", "rev-list --tags --max-count=1")
log.Log(INFO, "getLastTagVersion()", out) log.Log(INFO, "getLastTagVersion()", out)
rs.lasttagrev = out rs.lasttagrev = out
lastreal := "describe --tags " + out lastreal := "describe --tags " + out
// out = run(r.path, "git", "describe --tags c871d5ecf051a7dc4e3a77157cdbc0a457eb9ae1") // out = run(r.path, "git", "describe --tags c871d5ecf051a7dc4e3a77157cdbc0a457eb9ae1")
out = run(rs.repopath, "git", lastreal) out = run(rs.realPath.String(), "git", lastreal)
rs.lasttag.SetValue(out) rs.lasttag.SetValue(out)
rs.tagsDrop.SetText(out) rs.tagsDrop.SetText(out)
// rs.lastLabel.SetText(out) // rs.lastLabel.SetText(out)
@ -58,7 +58,7 @@ func (rs *RepoStatus) getLastTagVersion() string {
} }
func (rs *RepoStatus) populateTags() { func (rs *RepoStatus) populateTags() {
tmp := fullpath(rs.repopath + "/.git/refs/tags") tmp := rs.realPath.String() + "/.git/refs/tags"
log.Log(INFO, "populateTags() path =", tmp) log.Log(INFO, "populateTags() path =", tmp)
for _, tag := range listFiles(tmp) { for _, tag := range listFiles(tmp) {
if rs.tags[tag] == "" { if rs.tags[tag] == "" {
@ -70,24 +70,12 @@ func (rs *RepoStatus) populateTags() {
// rs.tagsDrop.SetText(rs.lasttagrev) // rs.tagsDrop.SetText(rs.lasttagrev)
} }
/*
.git/refs/remotes
.git/refs/remotes/github
.git/refs/remotes/github/devel
.git/refs/remotes/github/main
.git/refs/remotes/origin
.git/refs/remotes/origin/devel
.git/refs/remotes/origin/main
.git/refs/remotes/origin/jcarr
.git/refs/heads
*/
func (rs *RepoStatus) getBranches() []string { func (rs *RepoStatus) getBranches() []string {
var all []string var all []string
var heads []string var heads []string
var remotes []string var remotes []string
heads = listFiles(fullpath(rs.repopath + "/.git/refs/heads")) heads = listFiles(rs.realPath.String() + "/.git/refs/heads")
remotes = listFiles(fullpath(rs.repopath + "/.git/refs/remotes")) remotes = listFiles(rs.realPath.String() + "/.git/refs/remotes")
all = heads all = heads
@ -101,7 +89,7 @@ func (rs *RepoStatus) getBranches() []string {
func (rs *RepoStatus) CheckDirty() bool { func (rs *RepoStatus) CheckDirty() bool {
cmd := []string{"git", "diff-index", "--quiet", "HEAD"} cmd := []string{"git", "diff-index", "--quiet", "HEAD"}
path := "/home/jcarr/go/src/" + rs.repopath path := rs.realPath.String()
err, b, out := RunCmd(path, cmd) err, b, out := RunCmd(path, cmd)
if err != nil { if err != nil {
log.Warn("CheckDirty() b =", b) log.Warn("CheckDirty() b =", b)
@ -114,36 +102,36 @@ func (rs *RepoStatus) CheckDirty() bool {
} }
if b { if b {
log.Log(INFO, "CheckDirty() b =", b, "path =", path, "out =", out) log.Log(INFO, "CheckDirty() b =", b, "path =", path, "out =", out)
log.Log(INFO, "CheckDirty() no", rs.repopath) log.Log(INFO, "CheckDirty() no", rs.realPath.String())
rs.dirtyLabel.SetValue("no") rs.dirtyLabel.SetValue("no")
return false return false
} }
log.Log(INFO, "CheckDirty() true", rs.repopath) log.Log(INFO, "CheckDirty() true", rs.realPath.String())
rs.dirtyLabel.SetValue("dirty") rs.dirtyLabel.SetValue("dirty")
return true return true
} }
func (rs *RepoStatus) CheckoutBranch(branch string) (string, string) { func (rs *RepoStatus) CheckoutBranch(branch string) (string, string) {
// run(rs.repopath, "git", "checkout " + branch) // run(rs.realPath.String(), "git", "checkout " + branch)
realname := rs.getCurrentBranchName() realname := rs.getCurrentBranchName()
realversion := rs.getCurrentBranchVersion() realversion := rs.getCurrentBranchVersion()
log.Log(INFO, rs.repopath, "realname =", realname, "realversion =", realversion) log.Log(INFO, rs.realPath.String(), "realname =", realname, "realversion =", realversion)
return realname, realversion return realname, realversion
} }
func (rs *RepoStatus) checkoutBranch(level string, branch string) { func (rs *RepoStatus) checkoutBranch(level string, branch string) {
if rs.CheckDirty() { if rs.CheckDirty() {
log.Log(INFO, "checkoutBranch() checkDirty() == true for repo", rs.repopath, "looking for branch:", branch) log.Log(INFO, "checkoutBranch() checkDirty() == true for repo", rs.realPath.String(), "looking for branch:", branch)
return return
} }
out := run(rs.repopath, "git", "checkout "+branch) out := run(rs.realPath.String(), "git", "checkout "+branch)
log.Log(INFO, rs.repopath, "git checkout "+branch, "returned", out) log.Log(INFO, rs.realPath.String(), "git checkout "+branch, "returned", out)
realname := rs.getCurrentBranchName() realname := rs.getCurrentBranchName()
realversion := rs.getCurrentBranchVersion() realversion := rs.getCurrentBranchVersion()
log.Log(INFO, rs.repopath, "realname =", realname, "realversion =", realversion) log.Log(INFO, rs.realPath.String(), "realname =", realname, "realversion =", realversion)
switch level { switch level {
case "master": case "master":
@ -259,7 +247,7 @@ func (rs *RepoStatus) CheckBranches() bool {
var hashCheck string var hashCheck string
var perfect bool = true var perfect bool = true
all := rs.getBranches() all := rs.getBranches()
path := fullpath(rs.repopath + "/.git/refs/") path := rs.realPath.String() + "/.git/refs/"
for _, b := range all { for _, b := range all {
parts := strings.Split(b, "/") parts := strings.Split(b, "/")
rdir := "heads" rdir := "heads"
@ -286,7 +274,7 @@ func (rs *RepoStatus) CheckBranches() bool {
} }
var cmd []string var cmd []string
cmd = append(cmd, "git", "show", "-s", "--format=%ci", hash) cmd = append(cmd, "git", "show", "-s", "--format=%ci", hash)
_, _, output := RunCmd("/home/jcarr/go/src/"+rs.repopath, cmd) _, _, output := RunCmd(rs.realPath.String(), cmd)
// git show -s --format=%ci <hash> will give you the time // git show -s --format=%ci <hash> will give you the time
// log.Log(INFO, fullfile) // log.Log(INFO, fullfile)
if hash == hashCheck { if hash == hashCheck {

View File

@ -105,7 +105,7 @@ func readGitConfig(filePath string) (*GitConfig, error) {
if len(parts) == 2 { if len(parts) == 2 {
line = strings.Trim(line, "[]") line = strings.Trim(line, "[]")
currentName = strings.Trim(parts[1], "[]") currentName = strings.Trim(parts[1], "\"")
} }
continue continue
} }
@ -116,7 +116,10 @@ func readGitConfig(filePath string) (*GitConfig, error) {
} }
key := strings.TrimSpace(partsNew[0]) key := strings.TrimSpace(partsNew[0])
key = strings.TrimSuffix(key, "\"")
value := strings.TrimSpace(partsNew[1]) value := strings.TrimSpace(partsNew[1])
value = strings.TrimSuffix(value, "\"")
switch currentSection { switch currentSection {
case "core": case "core":
@ -233,3 +236,25 @@ func (rs *RepoStatus) ReadGoMod() bool {
rs.goConfig = deps rs.goConfig = deps
return true return true
} }
func ScanGoSrc() {
log.Log(WARN, "Scanning all go.sum files")
for path, rs := range windowMap {
if rs.ReadGoMod() {
// everything is ok
} else {
log.Log(WARN, "failed reading go.sum repo:", path)
}
}
}
func ScanGitConfig() {
for i, path := range listGitDirectories() {
filename := filepath.Join(path, ".git/config")
_, err := readGitConfig(filename)
if err != nil {
log.Log(WARN, "repo =", i, path)
log.Log(WARN, "Error reading .git/config:", err)
}
}
}

3
new.go
View File

@ -45,6 +45,7 @@ func NewRepoStatusWindow(path string) *RepoStatus {
} }
rs := New(gui.TreeRoot(), path) rs := New(gui.TreeRoot(), path)
rs.draw(goSrcDir, realpath)
// save ~/go/src & the whole path strings // save ~/go/src & the whole path strings
rs.goSrcPath.SetValue(goSrcDir) rs.goSrcPath.SetValue(goSrcDir)
@ -64,14 +65,12 @@ func New(p *gui.Node, path string) *RepoStatus {
hidden: true, hidden: true,
ready: false, ready: false,
parent: p, parent: p,
repopath: path,
} }
rs.tags = make(map[string]string) rs.tags = make(map[string]string)
rs.window = gadgets.NewBasicWindow(p, "GO Repo Details "+path) rs.window = gadgets.NewBasicWindow(p, "GO Repo Details "+path)
rs.window.Horizontal() rs.window.Horizontal()
rs.window.Make() rs.window.Make()
rs.ready = true rs.ready = true
rs.draw()
rs.window.Custom = func() { rs.window.Custom = func() {
// rs.hidden = true // rs.hidden = true
rs.Hide() rs.Hide()

View File

@ -10,7 +10,7 @@ type RepoStatus struct {
hidden bool hidden bool
changed bool changed bool
repopath string // repopath string
lasttagrev string lasttagrev string
tags map[string]string tags map[string]string

View File

@ -13,17 +13,13 @@ import (
"go.wit.com/log" "go.wit.com/log"
) )
func fullpath(repo string) string {
return "/home/jcarr/go/src/" + repo
}
func run(path string, thing string, cmdline string) string { func run(path string, thing string, cmdline string) string {
parts := strings.Split(cmdline, " ") parts := strings.Split(cmdline, " ")
// Create the command // Create the command
cmd := exec.Command(thing, parts...) cmd := exec.Command(thing, parts...)
// Set the working directory // Set the working directory
cmd.Dir = fullpath(path) cmd.Dir = path
// Execute the command // Execute the command
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
@ -120,8 +116,7 @@ func splitVersion(version string) (a, b, c string) {
// temp hack. fix this // temp hack. fix this
func runCmd(path string, parts []string) (error, bool, string) { func runCmd(path string, parts []string) (error, bool, string) {
fulldir := fullpath(path) return RunCmd(path, parts)
return RunCmd(fulldir, parts)
} }
func RunCmd(workingpath string, parts []string) (error, bool, string) { func RunCmd(workingpath string, parts []string) (error, bool, string) {

View File

@ -17,9 +17,9 @@ func (rs *RepoStatus) Update() {
log.Log(INFO, "Update() START") log.Log(INFO, "Update() START")
duration := timeFunction(func() { duration := timeFunction(func() {
// do things that are safe even if the git tree is dirty // do things that are safe even if the git tree is dirty
rs.path.SetValue(rs.repopath) // rs.path.SetValue(rs.repopath)
rs.getCurrentBranchName() rs.getCurrentBranchName()
rs.window.SetTitle(rs.repopath + " GO repo Details") // rs.window.SetTitle(rs.repopath + " GO repo Details")
rs.getCurrentBranchVersion() rs.getCurrentBranchVersion()
rs.getLastTagVersion() rs.getLastTagVersion()
rs.populateTags() rs.populateTags()