mirror of
https://github.com/awalsh128/cache-apt-pkgs-action.git
synced 2025-09-20 18:17:15 +00:00
First version of a Golang version of command handling in general.
This commit is contained in:
parent
85fe267c5d
commit
297ef0180d
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
src/cmd/apt_query/apt_query*
|
|
@ -1,29 +1,17 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"awalsh128.com/cache-apt-pkgs-action/src/internal/common"
|
||||
"awalsh128.com/cache-apt-pkgs-action/src/internal/exec"
|
||||
"awalsh128.com/cache-apt-pkgs-action/src/internal/logging"
|
||||
)
|
||||
|
||||
func contains(arr []string, element string) bool {
|
||||
for _, x := range arr {
|
||||
if x == element {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Writes a message to STDERR and exits with status 1.
|
||||
func exitOnError(format string, arg ...any) {
|
||||
fmt.Fprintln(os.Stderr, fmt.Errorf(format+"\n", arg...))
|
||||
fmt.Println("Usage: apt_query normalized-list <package names>")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
type AptPackage struct {
|
||||
Name string
|
||||
Version string
|
||||
|
@ -39,26 +27,20 @@ func (ps AptPackages) serialize() string {
|
|||
return strings.Join(tokens, " ")
|
||||
}
|
||||
|
||||
// Executes a command and either returns the output or exits the programs and writes the output (including error) to STDERR.
|
||||
func execCommand(name string, arg ...string) string {
|
||||
cmd := exec.Command(name, arg...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error code %d encountered while running %s\n%s\n", cmd.ProcessState.ExitCode(), strings.Join(cmd.Args, " "), string(out))
|
||||
os.Exit(2)
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// Gets the APT based packages as a sorted by name list (normalized).
|
||||
func getPackages(names []string) AptPackages {
|
||||
func getPackages(executor exec.Executor, names []string) AptPackages {
|
||||
prefixArgs := []string{"--quiet=0", "--no-all-versions", "show"}
|
||||
out := execCommand("apt-cache", append(prefixArgs, names...)...)
|
||||
execution := executor.Exec("apt-cache", append(prefixArgs, names...)...)
|
||||
|
||||
err := execution.Error()
|
||||
if err != nil {
|
||||
logging.Fatal(err)
|
||||
}
|
||||
|
||||
pkgs := []AptPackage{}
|
||||
errorMessages := []string{}
|
||||
|
||||
for _, paragraph := range strings.Split(string(out), "\n\n") {
|
||||
for _, paragraph := range strings.Split(execution.Stdout, "\n\n") {
|
||||
pkg := AptPackage{}
|
||||
for _, line := range strings.Split(paragraph, "\n") {
|
||||
if strings.HasPrefix(line, "Package: ") {
|
||||
|
@ -66,7 +48,7 @@ func getPackages(names []string) AptPackages {
|
|||
} else if strings.HasPrefix(line, "Version: ") {
|
||||
pkg.Version = strings.TrimSpace(strings.SplitN(line, ":", 2)[1])
|
||||
} else if strings.HasPrefix(line, "N: Unable to locate package ") || strings.HasPrefix(line, "E: ") {
|
||||
if !contains(errorMessages, line) {
|
||||
if !common.ContainsString(errorMessages, line) {
|
||||
errorMessages = append(errorMessages, line)
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +59,7 @@ func getPackages(names []string) AptPackages {
|
|||
}
|
||||
|
||||
if len(errorMessages) > 0 {
|
||||
exitOnError("Errors encountered in apt-cache output (see below):\n%s", strings.Join(errorMessages, "\n"))
|
||||
logging.Fatalf("Errors encountered in apt-cache output (see below):\n%s", strings.Join(errorMessages, "\n"))
|
||||
}
|
||||
|
||||
sort.Slice(pkgs, func(i, j int) bool {
|
||||
|
@ -87,22 +69,41 @@ func getPackages(names []string) AptPackages {
|
|||
return pkgs
|
||||
}
|
||||
|
||||
func getExecutor(replayFilename string) exec.Executor {
|
||||
if len(replayFilename) == 0 {
|
||||
return &exec.BinExecutor{}
|
||||
}
|
||||
return exec.NewReplayExecutor(replayFilename)
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 3 {
|
||||
exitOnError("Expected at least 2 arguments but found %d.", len(os.Args)-1)
|
||||
debug := flag.Bool("debug", false, "Log diagnostic information to a file alongside the binary.")
|
||||
|
||||
replayFilename := flag.String("replayfile", "",
|
||||
"Replay command output from a specified file rather than executing a binary."+
|
||||
"The file should be in the same format as the log generated by the debug flag.")
|
||||
|
||||
flag.Parse()
|
||||
unparsedFlags := flag.Args()
|
||||
|
||||
logging.Init(os.Args[0]+".log", *debug)
|
||||
|
||||
executor := getExecutor(*replayFilename)
|
||||
|
||||
if len(unparsedFlags) < 2 {
|
||||
logging.Fatalf("Expected at least 2 non-flag arguments but found %d.", len(unparsedFlags))
|
||||
return
|
||||
}
|
||||
|
||||
command := os.Args[1]
|
||||
pkgNames := os.Args[2:]
|
||||
command := unparsedFlags[0]
|
||||
pkgNames := unparsedFlags[1:]
|
||||
|
||||
switch command {
|
||||
|
||||
case "normalized-list":
|
||||
pkgs := getPackages(pkgNames)
|
||||
pkgs := getPackages(executor, pkgNames)
|
||||
fmt.Println(pkgs.serialize())
|
||||
|
||||
default:
|
||||
exitOnError("Command '%s' not recognized.", command)
|
||||
logging.Fatalf("Command '%s' not recognized.", command)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,94 +1,65 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os/exec"
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"awalsh128.com/cache-apt-pkgs-action/src/internal/cmdtesting"
|
||||
)
|
||||
|
||||
type RunResult struct {
|
||||
TestContext *testing.T
|
||||
Stdout string
|
||||
Stderr string
|
||||
Err error
|
||||
var createReplayLogs bool = false
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&createReplayLogs, "createreplaylogs", false, "Execute the test commands, save the command output for future replay and skip the tests themselves.")
|
||||
}
|
||||
|
||||
func run(t *testing.T, command string, pkgNames ...string) RunResult {
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &bytes.Buffer{}
|
||||
cmd := exec.Command("go", append([]string{"run", "main.go", command}, pkgNames...)...)
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
err := cmd.Run()
|
||||
return RunResult{TestContext: t, Stdout: stdout.String(), Stderr: stderr.String(), Err: err}
|
||||
}
|
||||
|
||||
func (r *RunResult) expectSuccessfulOut(expected string) {
|
||||
if r.Err != nil {
|
||||
r.TestContext.Errorf("Error running command: %v\n%s", r.Err, r.Stderr)
|
||||
return
|
||||
}
|
||||
if r.Stderr != "" {
|
||||
r.TestContext.Errorf("Unexpected stderr messages found.\nExpected: none\nActual:\n'%s'", r.Stderr)
|
||||
}
|
||||
fullExpected := expected + "\n" // Output will always have a end of output newline.
|
||||
if r.Stdout != fullExpected { // Output will always have a end of output newline.
|
||||
r.TestContext.Errorf("Unexpected stdout found.\nExpected:\n'%s'\nActual:\n'%s'", fullExpected, r.Stdout)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RunResult) expectError(expected string) {
|
||||
fullExpected := expected + "\n" // Output will always have a end of output newline.
|
||||
if r.Stderr != fullExpected {
|
||||
r.TestContext.Errorf("Unexpected stderr found.\nExpected:\n'%s'\nActual:\n'%s'", fullExpected, r.Stderr)
|
||||
}
|
||||
func TestMain(m *testing.M) {
|
||||
cmdtesting.TestMain(m)
|
||||
}
|
||||
|
||||
func TestNormalizedList_MultiplePackagesExists_StdoutsAlphaSortedPackageNameVersionPairs(t *testing.T) {
|
||||
result := run(t, "normalized-list", "xdot", "rolldice")
|
||||
result.expectSuccessfulOut("rolldice=1.16-1build1 xdot=1.2-3")
|
||||
result := cmdtesting.New(t, createReplayLogs).Run("normalized-list", "xdot", "rolldice")
|
||||
result.ExpectSuccessfulOut("rolldice=1.16-1build1 xdot=1.2-3")
|
||||
}
|
||||
|
||||
func TestNormalizedList_SamePackagesDifferentOrder_StdoutsMatch(t *testing.T) {
|
||||
expected := "rolldice=1.16-1build1 xdot=1.2-3"
|
||||
|
||||
result := run(t, "normalized-list", "rolldice", "xdot")
|
||||
result.expectSuccessfulOut(expected)
|
||||
ct := cmdtesting.New(t, createReplayLogs)
|
||||
|
||||
result = run(t, "normalized-list", "xdot", "rolldice")
|
||||
result.expectSuccessfulOut(expected)
|
||||
result := ct.Run("normalized-list", "rolldice", "xdot")
|
||||
result.ExpectSuccessfulOut(expected)
|
||||
|
||||
result = ct.Run("normalized-list", "xdot", "rolldice")
|
||||
result.ExpectSuccessfulOut(expected)
|
||||
}
|
||||
|
||||
func TestNormalizedList_MultiVersionWarning_StdoutSingleVersion(t *testing.T) {
|
||||
var result = run(t, "normalized-list", "libosmesa6-dev", "libgl1-mesa-dev")
|
||||
result.expectSuccessfulOut("libgl1-mesa-dev=23.0.4-0ubuntu1~23.04.1 libosmesa6-dev=23.0.4-0ubuntu1~23.04.1")
|
||||
var result = cmdtesting.New(t, createReplayLogs).Run("normalized-list", "libosmesa6-dev", "libgl1-mesa-dev")
|
||||
result.ExpectSuccessfulOut("libgl1-mesa-dev=23.0.4-0ubuntu1~23.04.1 libosmesa6-dev=23.0.4-0ubuntu1~23.04.1")
|
||||
}
|
||||
|
||||
func TestNormalizedList_SinglePackageExists_StdoutsSinglePackageNameVersionPair(t *testing.T) {
|
||||
var result = run(t, "normalized-list", "xdot")
|
||||
result.expectSuccessfulOut("xdot=1.2-3")
|
||||
var result = cmdtesting.New(t, createReplayLogs).Run("normalized-list", "xdot")
|
||||
result.ExpectSuccessfulOut("xdot=1.2-3")
|
||||
}
|
||||
|
||||
func TestNormalizedList_VersionContainsColon_StdoutsEntireVersion(t *testing.T) {
|
||||
var result = run(t, "normalized-list", "default-jre")
|
||||
result.expectSuccessfulOut("default-jre=2:1.17-74")
|
||||
var result = cmdtesting.New(t, createReplayLogs).Run("normalized-list", "default-jre")
|
||||
result.ExpectSuccessfulOut("default-jre=2:1.17-74")
|
||||
}
|
||||
|
||||
func TestNormalizedList_NonExistentPackageName_StderrsAptCacheErrors(t *testing.T) {
|
||||
var result = run(t, "normalized-list", "nonexistentpackagename")
|
||||
result.expectError(
|
||||
`Error code 100 encountered while running apt-cache --quiet=0 --no-all-versions show nonexistentpackagename
|
||||
var result = cmdtesting.New(t, createReplayLogs).Run("normalized-list", "nonexistentpackagename")
|
||||
result.ExpectError(
|
||||
`Error encountered running apt-cache --quiet=0 --no-all-versions show nonexistentpackagename
|
||||
Exited with status code 100; see combined output below:
|
||||
N: Unable to locate package nonexistentpackagename
|
||||
N: Unable to locate package nonexistentpackagename
|
||||
E: No packages found
|
||||
|
||||
exit status 2`)
|
||||
E: No packages found`)
|
||||
}
|
||||
|
||||
func TestNormalizedList_NoPackagesGiven_StderrsArgMismatch(t *testing.T) {
|
||||
var result = run(t, "normalized-list")
|
||||
result.expectError(
|
||||
`Expected at least 2 arguments but found 1.
|
||||
|
||||
exit status 1`)
|
||||
var result = cmdtesting.New(t, createReplayLogs).Run("normalized-list")
|
||||
result.ExpectError("Expected at least 2 non-flag arguments but found 1.")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
2023/12/10 14:09:15 Debug log created at /home/awalsh128/code/cache-apt-pkgs-action/src/cmd/apt_query/apt_query.log
|
||||
2023/12/10 14:09:15 EXECUTION-OBJ-START
|
||||
{
|
||||
"Cmd": "apt-cache --quiet=0 --no-all-versions show xdot rolldice",
|
||||
"Stdout": "Package: xdot\nArchitecture: all\nVersion: 1.2-3\nPriority: optional\nSection: universe/python\nOrigin: Ubuntu\nMaintainer: Ubuntu Developers \u003cubuntu-devel-discuss@lists.ubuntu.com\u003e\nOriginal-Maintainer: Debian Python Team \u003cteam+python@tracker.debian.org\u003e\nBugs: https://bugs.launchpad.net/ubuntu/+filebug\nInstalled-Size: 160\nDepends: gir1.2-gtk-3.0, graphviz, python3-gi, python3-gi-cairo, python3-numpy, python3:any\nFilename: pool/universe/x/xdot/xdot_1.2-3_all.deb\nSize: 28504\nMD5sum: 9fd56a82e8e6dc2b18fad996dd35bcdb\nSHA1: 9dfa42283c7326da18b132e61369fc8d70534f4b\nSHA256: 0dd59c1840b7f2322cf408f9634c83c4f9766a5e609813147cd67e44755bc011\nSHA512: 33d418475bb9977341007528d8622d236b59a5da4f7fb7f6a36aa05256c4789af30868fc2aad7fc17fb726c8814333cdc579a4ae01597f8f5864b4fbfd8dff14\nHomepage: https://github.com/jrfonseca/xdot.py\nDescription-en: interactive viewer for Graphviz dot files\n xdot is an interactive viewer for graphs written in Graphviz's dot language.\n It uses internally the graphviz's xdot output format as an intermediate\n format, and PyGTK and Cairo for rendering. xdot can be used either as a\n standalone application from command line, or as a library embedded in your\n Python 3 application.\n .\n Features:\n * Since it doesn't use bitmaps it is fast and has a small memory footprint.\n * Arbitrary zoom.\n * Keyboard/mouse navigation.\n * Supports events on the nodes with URLs.\n * Animated jumping between nodes.\n * Highlights node/edge under mouse.\nDescription-md5: eb58f25a628b48a744f1b904af3b9282\n\nPackage: rolldice\nArchitecture: amd64\nVersion: 1.16-1build1\nPriority: optional\nSection: universe/games\nOrigin: Ubuntu\nMaintainer: Ubuntu Developers \u003cubuntu-devel-discuss@lists.ubuntu.com\u003e\nOriginal-Maintainer: Thomas Ross \u003cthomasross@thomasross.io\u003e\nBugs: https://bugs.launchpad.net/ubuntu/+filebug\nInstalled-Size: 31\nDepends: libc6 (\u003e= 2.7), libreadline8 (\u003e= 6.0)\nFilename: pool/universe/r/rolldice/rolldice_1.16-1build1_amd64.deb\nSize: 9628\nMD5sum: af6390bf2d5d5b4710d308ac06d4913a\nSHA1: 1d87ccac5b20f4e2a217a0e058408f46cfe5caff\nSHA256: 2e076006200057da0be52060e3cc2f4fc7c51212867173e727590bd7603a0337\nSHA512: a2fa75cfc6f9fc0f1fce3601668bc060f9e10bcf94887025af755ef73db84c55383ed34e4199de53dfbb34377b050c1f9947f29b28d8e527509900f2ec872826\nHomepage: https://github.com/sstrickl/rolldice\nDescription-en: virtual dice roller\n rolldice is a virtual dice roller that takes a string on the command\n line in the format of some fantasy role playing games like Advanced\n Dungeons \u0026 Dragons [1] and returns the result of the dice rolls.\n .\n [1] Advanced Dungeons \u0026 Dragons is a registered trademark of TSR, Inc.\nDescription-md5: fc24e9e12c794a8f92ab0ca6e1058501\n\n",
|
||||
"Stderr": "",
|
||||
"ExitCode": 0
|
||||
}
|
||||
EXECUTION-OBJ-END
|
|
@ -0,0 +1,9 @@
|
|||
2023/12/10 14:09:17 Debug log created at /home/awalsh128/code/cache-apt-pkgs-action/src/cmd/apt_query/apt_query.log
|
||||
2023/12/10 14:09:18 EXECUTION-OBJ-START
|
||||
{
|
||||
"Cmd": "apt-cache --quiet=0 --no-all-versions show libosmesa6-dev libgl1-mesa-dev",
|
||||
"Stdout": "Package: libosmesa6-dev\nArchitecture: amd64\nVersion: 23.0.4-0ubuntu1~23.04.1\nMulti-Arch: same\nPriority: extra\nSection: devel\nSource: mesa\nOrigin: Ubuntu\nMaintainer: Ubuntu Developers \u003cubuntu-devel-discuss@lists.ubuntu.com\u003e\nOriginal-Maintainer: Debian X Strike Force \u003cdebian-x@lists.debian.org\u003e\nBugs: https://bugs.launchpad.net/ubuntu/+filebug\nInstalled-Size: 51\nProvides: libosmesa-dev\nDepends: libosmesa6 (= 23.0.4-0ubuntu1~23.04.1), mesa-common-dev (= 23.0.4-0ubuntu1~23.04.1) | libgl-dev\nConflicts: libosmesa-dev\nReplaces: libosmesa-dev\nFilename: pool/main/m/mesa/libosmesa6-dev_23.0.4-0ubuntu1~23.04.1_amd64.deb\nSize: 9016\nMD5sum: f2a03da1adaa37afc32493bc52913fe1\nSHA1: 3b0f26ba8438fe6f590cc38d412e0bc1b27f9831\nSHA256: 3fcd3b5c80bde4af1765e588e8f1301ddccae4e052cbfca1a32120d9d8697656\nSHA512: 0a88153218f263511cacffbfe9afd928679f7fa4aacefebd21bafae0517ef1cd3b4bef4fc676e7586728ce1257fbbeb8bd0dda02ddc54fe83c90800f5ce0a660\nHomepage: https://mesa3d.org/\nDescription-en: Mesa Off-screen rendering extension -- development files\n This package provides the required environment for developing programs\n that use the off-screen rendering extension of Mesa.\n .\n For more information on OSmesa see the libosmesa6 package.\nDescription-md5: 9b1d7a0b3e6a2ea021f4443f42dcff4f\n\nPackage: libgl1-mesa-dev\nArchitecture: amd64\nVersion: 23.0.4-0ubuntu1~23.04.1\nMulti-Arch: same\nPriority: extra\nSection: libdevel\nSource: mesa\nOrigin: Ubuntu\nMaintainer: Ubuntu Developers \u003cubuntu-devel-discuss@lists.ubuntu.com\u003e\nOriginal-Maintainer: Debian X Strike Force \u003cdebian-x@lists.debian.org\u003e\nBugs: https://bugs.launchpad.net/ubuntu/+filebug\nInstalled-Size: 33\nDepends: libgl-dev, libglvnd-dev\nFilename: pool/main/m/mesa/libgl1-mesa-dev_23.0.4-0ubuntu1~23.04.1_amd64.deb\nSize: 13908\nMD5sum: af15873cbc37ae8719487216f2465a7f\nSHA1: 0a04c22bd32578c65da6cab759347d22cbe2601f\nSHA256: 7b4b377658aff6aaa42af0cf220a64fe7ccd20b53ed0226b6b96edae5ce65dbe\nSHA512: f5b7c9324e7f1f8764a4a035182514559a17a96ce3d43ba570ebd32746de219bea035de3600850134b4f0ba62f12caf0e281fcf7c5490d93e1bd93ca27bfdd22\nHomepage: https://mesa3d.org/\nDescription-en: transitional dummy package\n This is a transitional dummy package, it can be safely removed.\nDescription-md5: 635a93bcd1440d16621693fe064c2aa9\n\n",
|
||||
"Stderr": "N: There are 2 additional records. Please use the '-a' switch to see them.\n",
|
||||
"ExitCode": 0
|
||||
}
|
||||
EXECUTION-OBJ-END
|
|
@ -0,0 +1,14 @@
|
|||
2023/12/10 14:09:19 Debug log created at /home/awalsh128/code/cache-apt-pkgs-action/src/cmd/apt_query/apt_query.log
|
||||
2023/12/10 14:09:20 EXECUTION-OBJ-START
|
||||
{
|
||||
"Cmd": "apt-cache --quiet=0 --no-all-versions show nonexistentpackagename",
|
||||
"Stdout": "",
|
||||
"Stderr": "N: Unable to locate package nonexistentpackagename\nN: Unable to locate package nonexistentpackagename\nE: No packages found\n",
|
||||
"ExitCode": 100
|
||||
}
|
||||
EXECUTION-OBJ-END
|
||||
2023/12/10 14:09:20 Error encountered running apt-cache --quiet=0 --no-all-versions show nonexistentpackagename
|
||||
Exited with status code 100; see combined output below:
|
||||
N: Unable to locate package nonexistentpackagename
|
||||
N: Unable to locate package nonexistentpackagename
|
||||
E: No packages found
|
|
@ -0,0 +1,2 @@
|
|||
2023/12/10 14:09:20 Debug log created at /home/awalsh128/code/cache-apt-pkgs-action/src/cmd/apt_query/apt_query.log
|
||||
2023/12/10 14:09:20 Expected at least 2 non-flag arguments but found 1.
|
|
@ -0,0 +1,18 @@
|
|||
2023/12/10 14:09:15 Debug log created at /home/awalsh128/code/cache-apt-pkgs-action/src/cmd/apt_query/apt_query.log
|
||||
2023/12/10 14:09:16 EXECUTION-OBJ-START
|
||||
{
|
||||
"Cmd": "apt-cache --quiet=0 --no-all-versions show rolldice xdot",
|
||||
"Stdout": "Package: rolldice\nArchitecture: amd64\nVersion: 1.16-1build1\nPriority: optional\nSection: universe/games\nOrigin: Ubuntu\nMaintainer: Ubuntu Developers \u003cubuntu-devel-discuss@lists.ubuntu.com\u003e\nOriginal-Maintainer: Thomas Ross \u003cthomasross@thomasross.io\u003e\nBugs: https://bugs.launchpad.net/ubuntu/+filebug\nInstalled-Size: 31\nDepends: libc6 (\u003e= 2.7), libreadline8 (\u003e= 6.0)\nFilename: pool/universe/r/rolldice/rolldice_1.16-1build1_amd64.deb\nSize: 9628\nMD5sum: af6390bf2d5d5b4710d308ac06d4913a\nSHA1: 1d87ccac5b20f4e2a217a0e058408f46cfe5caff\nSHA256: 2e076006200057da0be52060e3cc2f4fc7c51212867173e727590bd7603a0337\nSHA512: a2fa75cfc6f9fc0f1fce3601668bc060f9e10bcf94887025af755ef73db84c55383ed34e4199de53dfbb34377b050c1f9947f29b28d8e527509900f2ec872826\nHomepage: https://github.com/sstrickl/rolldice\nDescription-en: virtual dice roller\n rolldice is a virtual dice roller that takes a string on the command\n line in the format of some fantasy role playing games like Advanced\n Dungeons \u0026 Dragons [1] and returns the result of the dice rolls.\n .\n [1] Advanced Dungeons \u0026 Dragons is a registered trademark of TSR, Inc.\nDescription-md5: fc24e9e12c794a8f92ab0ca6e1058501\n\nPackage: xdot\nArchitecture: all\nVersion: 1.2-3\nPriority: optional\nSection: universe/python\nOrigin: Ubuntu\nMaintainer: Ubuntu Developers \u003cubuntu-devel-discuss@lists.ubuntu.com\u003e\nOriginal-Maintainer: Debian Python Team \u003cteam+python@tracker.debian.org\u003e\nBugs: https://bugs.launchpad.net/ubuntu/+filebug\nInstalled-Size: 160\nDepends: gir1.2-gtk-3.0, graphviz, python3-gi, python3-gi-cairo, python3-numpy, python3:any\nFilename: pool/universe/x/xdot/xdot_1.2-3_all.deb\nSize: 28504\nMD5sum: 9fd56a82e8e6dc2b18fad996dd35bcdb\nSHA1: 9dfa42283c7326da18b132e61369fc8d70534f4b\nSHA256: 0dd59c1840b7f2322cf408f9634c83c4f9766a5e609813147cd67e44755bc011\nSHA512: 33d418475bb9977341007528d8622d236b59a5da4f7fb7f6a36aa05256c4789af30868fc2aad7fc17fb726c8814333cdc579a4ae01597f8f5864b4fbfd8dff14\nHomepage: https://github.com/jrfonseca/xdot.py\nDescription-en: interactive viewer for Graphviz dot files\n xdot is an interactive viewer for graphs written in Graphviz's dot language.\n It uses internally the graphviz's xdot output format as an intermediate\n format, and PyGTK and Cairo for rendering. xdot can be used either as a\n standalone application from command line, or as a library embedded in your\n Python 3 application.\n .\n Features:\n * Since it doesn't use bitmaps it is fast and has a small memory footprint.\n * Arbitrary zoom.\n * Keyboard/mouse navigation.\n * Supports events on the nodes with URLs.\n * Animated jumping between nodes.\n * Highlights node/edge under mouse.\nDescription-md5: eb58f25a628b48a744f1b904af3b9282\n\n",
|
||||
"Stderr": "",
|
||||
"ExitCode": 0
|
||||
}
|
||||
EXECUTION-OBJ-END
|
||||
2023/12/10 14:09:16 Debug log created at /home/awalsh128/code/cache-apt-pkgs-action/src/cmd/apt_query/apt_query.log
|
||||
2023/12/10 14:09:17 EXECUTION-OBJ-START
|
||||
{
|
||||
"Cmd": "apt-cache --quiet=0 --no-all-versions show xdot rolldice",
|
||||
"Stdout": "Package: xdot\nArchitecture: all\nVersion: 1.2-3\nPriority: optional\nSection: universe/python\nOrigin: Ubuntu\nMaintainer: Ubuntu Developers \u003cubuntu-devel-discuss@lists.ubuntu.com\u003e\nOriginal-Maintainer: Debian Python Team \u003cteam+python@tracker.debian.org\u003e\nBugs: https://bugs.launchpad.net/ubuntu/+filebug\nInstalled-Size: 160\nDepends: gir1.2-gtk-3.0, graphviz, python3-gi, python3-gi-cairo, python3-numpy, python3:any\nFilename: pool/universe/x/xdot/xdot_1.2-3_all.deb\nSize: 28504\nMD5sum: 9fd56a82e8e6dc2b18fad996dd35bcdb\nSHA1: 9dfa42283c7326da18b132e61369fc8d70534f4b\nSHA256: 0dd59c1840b7f2322cf408f9634c83c4f9766a5e609813147cd67e44755bc011\nSHA512: 33d418475bb9977341007528d8622d236b59a5da4f7fb7f6a36aa05256c4789af30868fc2aad7fc17fb726c8814333cdc579a4ae01597f8f5864b4fbfd8dff14\nHomepage: https://github.com/jrfonseca/xdot.py\nDescription-en: interactive viewer for Graphviz dot files\n xdot is an interactive viewer for graphs written in Graphviz's dot language.\n It uses internally the graphviz's xdot output format as an intermediate\n format, and PyGTK and Cairo for rendering. xdot can be used either as a\n standalone application from command line, or as a library embedded in your\n Python 3 application.\n .\n Features:\n * Since it doesn't use bitmaps it is fast and has a small memory footprint.\n * Arbitrary zoom.\n * Keyboard/mouse navigation.\n * Supports events on the nodes with URLs.\n * Animated jumping between nodes.\n * Highlights node/edge under mouse.\nDescription-md5: eb58f25a628b48a744f1b904af3b9282\n\nPackage: rolldice\nArchitecture: amd64\nVersion: 1.16-1build1\nPriority: optional\nSection: universe/games\nOrigin: Ubuntu\nMaintainer: Ubuntu Developers \u003cubuntu-devel-discuss@lists.ubuntu.com\u003e\nOriginal-Maintainer: Thomas Ross \u003cthomasross@thomasross.io\u003e\nBugs: https://bugs.launchpad.net/ubuntu/+filebug\nInstalled-Size: 31\nDepends: libc6 (\u003e= 2.7), libreadline8 (\u003e= 6.0)\nFilename: pool/universe/r/rolldice/rolldice_1.16-1build1_amd64.deb\nSize: 9628\nMD5sum: af6390bf2d5d5b4710d308ac06d4913a\nSHA1: 1d87ccac5b20f4e2a217a0e058408f46cfe5caff\nSHA256: 2e076006200057da0be52060e3cc2f4fc7c51212867173e727590bd7603a0337\nSHA512: a2fa75cfc6f9fc0f1fce3601668bc060f9e10bcf94887025af755ef73db84c55383ed34e4199de53dfbb34377b050c1f9947f29b28d8e527509900f2ec872826\nHomepage: https://github.com/sstrickl/rolldice\nDescription-en: virtual dice roller\n rolldice is a virtual dice roller that takes a string on the command\n line in the format of some fantasy role playing games like Advanced\n Dungeons \u0026 Dragons [1] and returns the result of the dice rolls.\n .\n [1] Advanced Dungeons \u0026 Dragons is a registered trademark of TSR, Inc.\nDescription-md5: fc24e9e12c794a8f92ab0ca6e1058501\n\n",
|
||||
"Stderr": "",
|
||||
"ExitCode": 0
|
||||
}
|
||||
EXECUTION-OBJ-END
|
|
@ -0,0 +1,9 @@
|
|||
2023/12/10 14:09:18 Debug log created at /home/awalsh128/code/cache-apt-pkgs-action/src/cmd/apt_query/apt_query.log
|
||||
2023/12/10 14:09:18 EXECUTION-OBJ-START
|
||||
{
|
||||
"Cmd": "apt-cache --quiet=0 --no-all-versions show xdot",
|
||||
"Stdout": "Package: xdot\nArchitecture: all\nVersion: 1.2-3\nPriority: optional\nSection: universe/python\nOrigin: Ubuntu\nMaintainer: Ubuntu Developers \u003cubuntu-devel-discuss@lists.ubuntu.com\u003e\nOriginal-Maintainer: Debian Python Team \u003cteam+python@tracker.debian.org\u003e\nBugs: https://bugs.launchpad.net/ubuntu/+filebug\nInstalled-Size: 160\nDepends: gir1.2-gtk-3.0, graphviz, python3-gi, python3-gi-cairo, python3-numpy, python3:any\nFilename: pool/universe/x/xdot/xdot_1.2-3_all.deb\nSize: 28504\nMD5sum: 9fd56a82e8e6dc2b18fad996dd35bcdb\nSHA1: 9dfa42283c7326da18b132e61369fc8d70534f4b\nSHA256: 0dd59c1840b7f2322cf408f9634c83c4f9766a5e609813147cd67e44755bc011\nSHA512: 33d418475bb9977341007528d8622d236b59a5da4f7fb7f6a36aa05256c4789af30868fc2aad7fc17fb726c8814333cdc579a4ae01597f8f5864b4fbfd8dff14\nHomepage: https://github.com/jrfonseca/xdot.py\nDescription-en: interactive viewer for Graphviz dot files\n xdot is an interactive viewer for graphs written in Graphviz's dot language.\n It uses internally the graphviz's xdot output format as an intermediate\n format, and PyGTK and Cairo for rendering. xdot can be used either as a\n standalone application from command line, or as a library embedded in your\n Python 3 application.\n .\n Features:\n * Since it doesn't use bitmaps it is fast and has a small memory footprint.\n * Arbitrary zoom.\n * Keyboard/mouse navigation.\n * Supports events on the nodes with URLs.\n * Animated jumping between nodes.\n * Highlights node/edge under mouse.\nDescription-md5: eb58f25a628b48a744f1b904af3b9282\n\n",
|
||||
"Stderr": "",
|
||||
"ExitCode": 0
|
||||
}
|
||||
EXECUTION-OBJ-END
|
|
@ -0,0 +1,9 @@
|
|||
2023/12/10 14:09:18 Debug log created at /home/awalsh128/code/cache-apt-pkgs-action/src/cmd/apt_query/apt_query.log
|
||||
2023/12/10 14:09:19 EXECUTION-OBJ-START
|
||||
{
|
||||
"Cmd": "apt-cache --quiet=0 --no-all-versions show default-jre",
|
||||
"Stdout": "Package: default-jre\nArchitecture: amd64\nVersion: 2:1.17-74\nPriority: optional\nSection: interpreters\nSource: java-common (0.74)\nOrigin: Ubuntu\nMaintainer: Ubuntu Developers \u003cubuntu-devel-discuss@lists.ubuntu.com\u003e\nOriginal-Maintainer: Debian Java Maintainers \u003cpkg-java-maintainers@lists.alioth.debian.org\u003e\nBugs: https://bugs.launchpad.net/ubuntu/+filebug\nInstalled-Size: 6\nProvides: java-runtime (= 17), java10-runtime, java11-runtime, java12-runtime, java13-runtime, java14-runtime, java15-runtime, java16-runtime, java17-runtime, java2-runtime, java5-runtime, java6-runtime, java7-runtime, java8-runtime, java9-runtime\nDepends: default-jre-headless (= 2:1.17-74), openjdk-17-jre\nFilename: pool/main/j/java-common/default-jre_1.17-74_amd64.deb\nSize: 912\nMD5sum: e1c24f152396655f96dbaa749bd9cd2e\nSHA1: b1eeca19c6a29448ecc81df249bafdedbe49ee04\nSHA256: 02b1e27de90f05af42d61af927c35f17f7ba1ffe19bf64598ec68216075f623f\nSHA512: 81577c1e4b6920a3fb296a2cdb301131283762efa9fa371e6aa8bc31c7ee1292bbbc442984b2e2ce5d8788507f396eb3e03c1ff8c4dda671f329ac08ec0b2057\nHomepage: https://wiki.debian.org/Java/\nDescription-en: Standard Java or Java compatible Runtime\n This dependency package points to the Java runtime, or Java compatible\n runtime recommended for this architecture, which is\n openjdk-17-jre for amd64.\nDescription-md5: e747dcb24f92ffabcbdfba1db72f26e8\nTask: edubuntu-desktop-gnome\nCnf-Extra-Commands: java,jexec\n\n",
|
||||
"Stderr": "",
|
||||
"ExitCode": 0
|
||||
}
|
||||
EXECUTION-OBJ-END
|
88
src/internal/cmdtesting/cmdtesting.go
Normal file
88
src/internal/cmdtesting/cmdtesting.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package cmdtesting
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"awalsh128.com/cache-apt-pkgs-action/src/internal/common"
|
||||
)
|
||||
|
||||
const binaryName = "apt_query"
|
||||
|
||||
type CmdTesting struct {
|
||||
*testing.T
|
||||
createReplayLogs bool
|
||||
replayFilename string
|
||||
}
|
||||
|
||||
func New(t *testing.T, createReplayLogs bool) *CmdTesting {
|
||||
replayFilename := "testlogs/" + strings.ToLower(t.Name()) + ".log"
|
||||
if createReplayLogs {
|
||||
os.Remove(replayFilename)
|
||||
os.Remove(binaryName + ".log")
|
||||
}
|
||||
return &CmdTesting{t, createReplayLogs, replayFilename}
|
||||
}
|
||||
|
||||
type RunResult struct {
|
||||
Testing *CmdTesting
|
||||
Combinedout string
|
||||
Err error
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
cmd := exec.Command("go", "build")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
panic(string(out))
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func (t *CmdTesting) Run(command string, pkgNames ...string) RunResult {
|
||||
replayfile := "testlogs/" + strings.ToLower(t.Name()) + ".log"
|
||||
|
||||
flags := []string{"-debug=true"}
|
||||
if !t.createReplayLogs {
|
||||
flags = append(flags, "-replayfile="+replayfile)
|
||||
}
|
||||
|
||||
cmd := exec.Command("./"+binaryName, append(append(flags, command), pkgNames...)...)
|
||||
combinedout, err := cmd.CombinedOutput()
|
||||
|
||||
if t.createReplayLogs {
|
||||
common.AppendFile(binaryName+".log", t.replayFilename)
|
||||
}
|
||||
|
||||
return RunResult{Testing: t, Combinedout: string(combinedout), Err: err}
|
||||
}
|
||||
|
||||
func (r *RunResult) ExpectSuccessfulOut(expected string) {
|
||||
if r.Testing.createReplayLogs {
|
||||
r.Testing.Log("Skipping test while creating replay logs.")
|
||||
return
|
||||
}
|
||||
|
||||
if r.Err != nil {
|
||||
r.Testing.Errorf("Error running command: %v\n%s", r.Err, r.Combinedout)
|
||||
return
|
||||
}
|
||||
fullExpected := expected + "\n" // Output will always have a end of output newline.
|
||||
if r.Combinedout != fullExpected {
|
||||
r.Testing.Errorf("Unexpected combined std[err,out] found.\nExpected:\n'%s'\nActual:\n'%s'", fullExpected, r.Combinedout)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RunResult) ExpectError(expectedCombinedout string) {
|
||||
if r.Testing.createReplayLogs {
|
||||
r.Testing.Log("Skipping test while creating replay logs.")
|
||||
return
|
||||
}
|
||||
|
||||
fullExpectedCombinedout := expectedCombinedout + "\n" // Output will always have a end of output newline.
|
||||
if r.Combinedout != fullExpectedCombinedout {
|
||||
r.Testing.Errorf("Unexpected combined std[err,out] found.\nExpected:\n'%s'\nActual:\n'%s'", fullExpectedCombinedout, r.Combinedout)
|
||||
}
|
||||
}
|
85
src/internal/common/common.go
Normal file
85
src/internal/common/common.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func AppendFile(source string, destination string) error {
|
||||
createDirectoryIfNotPresent(filepath.Dir(destination))
|
||||
in, err := os.Open(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.OpenFile(destination, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
data, err := io.ReadAll(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = out.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CopyFile(source string, destination string) error {
|
||||
createDirectoryIfNotPresent(filepath.Dir(destination))
|
||||
in, err := os.Open(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
data, err := io.ReadAll(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = out.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MoveFile(source string, destination string) error {
|
||||
createDirectoryIfNotPresent(filepath.Dir(destination))
|
||||
return os.Rename(source, destination)
|
||||
}
|
||||
|
||||
func ContainsString(arr []string, element string) bool {
|
||||
for _, x := range arr {
|
||||
if x == element {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func createDirectoryIfNotPresent(path string) error {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
err := os.MkdirAll(path, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
41
src/internal/exec/binexecutor.go
Normal file
41
src/internal/exec/binexecutor.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package exec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"awalsh128.com/cache-apt-pkgs-action/src/internal/logging"
|
||||
)
|
||||
|
||||
// An executor that proxies command executions from the OS.
|
||||
//
|
||||
// NOTE: Extra abstraction layer needed for testing and replay.
|
||||
type BinExecutor struct{}
|
||||
|
||||
func (c *BinExecutor) Exec(name string, arg ...string) *Execution {
|
||||
cmd := exec.Command(name, arg...)
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
var stdout bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
|
||||
err := cmd.Run()
|
||||
execution := &Execution{
|
||||
Cmd: name + " " + strings.Join(arg, " "),
|
||||
Stdout: stdout.String(),
|
||||
Stderr: stderr.String(),
|
||||
ExitCode: cmd.ProcessState.ExitCode(),
|
||||
}
|
||||
|
||||
logging.DebugLazy(func() string {
|
||||
return fmt.Sprintf("EXECUTION-OBJ-START\n%s\nEXECUTION-OBJ-END", execution.Serialize())
|
||||
})
|
||||
if err != nil {
|
||||
logging.Fatal(execution.Error())
|
||||
}
|
||||
return execution
|
||||
}
|
47
src/internal/exec/executor.go
Normal file
47
src/internal/exec/executor.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package exec
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"awalsh128.com/cache-apt-pkgs-action/src/internal/logging"
|
||||
)
|
||||
|
||||
type Executor interface {
|
||||
// Executes a command and either returns the output or exits the programs and writes the output (including error) to STDERR.
|
||||
Exec(name string, arg ...string) *Execution
|
||||
}
|
||||
|
||||
type Execution struct {
|
||||
Cmd string
|
||||
Stdout string
|
||||
Stderr string
|
||||
ExitCode int
|
||||
}
|
||||
|
||||
// Gets the error, if the command ran with a non-zero exit code.
|
||||
func (e *Execution) Error() error {
|
||||
if e.ExitCode == 0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(
|
||||
"Error encountered running %s\nExited with status code %d; see combined output below:\n%s",
|
||||
e.Cmd,
|
||||
e.ExitCode,
|
||||
e.Stdout+e.Stderr,
|
||||
)
|
||||
}
|
||||
|
||||
func DeserializeExecution(payload string) *Execution {
|
||||
var execution Execution
|
||||
json.Unmarshal([]byte(payload), &execution)
|
||||
return &execution
|
||||
}
|
||||
|
||||
func (e *Execution) Serialize() string {
|
||||
bytes, err := json.MarshalIndent(e, "", " ")
|
||||
if err != nil {
|
||||
logging.Fatalf("Error encountered serializing Execution object.\n%s", err)
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
74
src/internal/exec/replayexecutor.go
Normal file
74
src/internal/exec/replayexecutor.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package exec
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"awalsh128.com/cache-apt-pkgs-action/src/internal/logging"
|
||||
)
|
||||
|
||||
// An executor that replays execution results from a recorded result.
|
||||
type ReplayExecutor struct {
|
||||
logFilepath string
|
||||
cmdExecs map[string]*Execution
|
||||
}
|
||||
|
||||
func NewReplayExecutor(logFilepath string) *ReplayExecutor {
|
||||
file, err := os.Open(logFilepath)
|
||||
if err != nil {
|
||||
logging.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
cmdExecs := make(map[string]*Execution)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.Contains(line, "EXECUTION-OBJ-START") {
|
||||
payload := ""
|
||||
for scanner.Scan() {
|
||||
line = scanner.Text()
|
||||
if strings.Contains(line, "EXECUTION-OBJ-END") {
|
||||
execution := DeserializeExecution(payload)
|
||||
cmdExecs[execution.Cmd] = execution
|
||||
break
|
||||
} else {
|
||||
payload += line + "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
logging.Fatal(err)
|
||||
}
|
||||
return &ReplayExecutor{logFilepath, cmdExecs}
|
||||
}
|
||||
|
||||
func (e *ReplayExecutor) getCmds() []string {
|
||||
cmds := []string{}
|
||||
for cmd := range e.cmdExecs {
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
return cmds
|
||||
}
|
||||
|
||||
func (e *ReplayExecutor) Exec(name string, arg ...string) *Execution {
|
||||
cmd := name + " " + strings.Join(arg, " ")
|
||||
value, ok := e.cmdExecs[cmd]
|
||||
if !ok {
|
||||
var available string
|
||||
if len(e.getCmds()) > 0 {
|
||||
available = "\n" + strings.Join(e.getCmds(), "\n")
|
||||
} else {
|
||||
available = " NONE"
|
||||
}
|
||||
logging.Fatalf(
|
||||
"Unable to replay command '%s'.\n"+
|
||||
"No command found in the debug log; available commands:%s", cmd, available)
|
||||
}
|
||||
return value
|
||||
}
|
57
src/internal/logging/logger.go
Normal file
57
src/internal/logging/logger.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Logger struct {
|
||||
wrapped *log.Logger
|
||||
Filename string
|
||||
Debug bool
|
||||
}
|
||||
|
||||
var logger *Logger
|
||||
|
||||
var LogFilepath = os.Args[0] + ".log"
|
||||
|
||||
func Init(filename string, debug bool) *Logger {
|
||||
os.Remove(LogFilepath)
|
||||
file, err := os.OpenFile(LogFilepath, os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
os.Exit(2)
|
||||
}
|
||||
cwd, _ := os.Getwd()
|
||||
logger = &Logger{
|
||||
wrapped: log.New(file, "", log.LstdFlags),
|
||||
Filename: filepath.Join(cwd, file.Name()),
|
||||
Debug: debug,
|
||||
}
|
||||
Debug("Debug log created at %s", logger.Filename)
|
||||
return logger
|
||||
}
|
||||
|
||||
func DebugLazy(getLine func() string) {
|
||||
if logger.Debug {
|
||||
logger.wrapped.Println(getLine())
|
||||
}
|
||||
}
|
||||
|
||||
func Debug(format string, a ...any) {
|
||||
if logger.Debug {
|
||||
logger.wrapped.Println(fmt.Sprintf(format, a...))
|
||||
}
|
||||
}
|
||||
|
||||
func Fatal(err error) {
|
||||
fmt.Fprintf(os.Stderr, "%s", err.Error())
|
||||
logger.wrapped.Fatal(err)
|
||||
}
|
||||
|
||||
func Fatalf(format string, a ...any) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", a...)
|
||||
logger.wrapped.Fatal(fmt.Sprintf(format, a...))
|
||||
}
|
Loading…
Reference in a new issue