Initial project
This commit is contained in:
commit
e715024cbd
23 files changed
+1547
No files matched your search
+43
@@ -0,0 +1,43 @@
|
|||||||
|
# ---> Go
|
||||||
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
|
#
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
go.work.sum
|
||||||
|
|
||||||
|
# env file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# config file(s)
|
||||||
|
config.yaml
|
||||||
|
config.json
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode/*
|
||||||
|
|
||||||
|
# Build directory
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Release directory
|
||||||
|
release/
|
||||||
|
|
||||||
|
# Generated code directories
|
||||||
|
internal/config/util/
|
||||||
|
!internal/config/util/util.go
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"version": "2",
|
||||||
|
"run": {
|
||||||
|
"timeout": "5m",
|
||||||
|
"tests": true
|
||||||
|
},
|
||||||
|
"linters": {
|
||||||
|
"enable": [
|
||||||
|
"cyclop",
|
||||||
|
"dogsled",
|
||||||
|
"errcheck",
|
||||||
|
"godox",
|
||||||
|
"govet",
|
||||||
|
"gosec",
|
||||||
|
"ineffassign",
|
||||||
|
"nilerr",
|
||||||
|
"nilnil",
|
||||||
|
"nolintlint",
|
||||||
|
"unused",
|
||||||
|
"whitespace"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"formatters": {
|
||||||
|
"enable": [
|
||||||
|
"gci",
|
||||||
|
"gofmt",
|
||||||
|
"goimports",
|
||||||
|
"golines"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"golines": {
|
||||||
|
"max-len": 100,
|
||||||
|
"tab-len": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"issues": {
|
||||||
|
"max-same-issues": 0,
|
||||||
|
"max-issues-per-linter": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# JobSearch
|
||||||
|
|
||||||
|
JobSearch is a service for scraping for contract opportunities. It is primarily
|
||||||
|
a re-write of the existing nodeJS JobSearch codebase.
|
||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.avsdev.uk/Go/job-search/internal/cli"
|
||||||
|
cmdConfig "git.avsdev.uk/Go/job-search/internal/cli/config"
|
||||||
|
cmdVersion "git.avsdev.uk/Go/job-search/internal/cli/version"
|
||||||
|
"git.avsdev.uk/Go/job-search/internal/config"
|
||||||
|
"git.avsdev.uk/Go/job-search/internal/log"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "job-search",
|
||||||
|
Short: "JobSearch - AVSDev Future Opportunities!",
|
||||||
|
Long: `JobSearch is a scraping tool for searching for possible contract
|
||||||
|
opportunities.
|
||||||
|
`,
|
||||||
|
CompletionOptions: cobra.CompletionOptions{
|
||||||
|
HiddenDefaultCmd: true,
|
||||||
|
},
|
||||||
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// Check if the command is expecting to use terminal for messages
|
||||||
|
if val, ok := cmd.Annotations["terminal_log"]; ok && val == "1" {
|
||||||
|
log.LogToTerminal()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for debug level via args
|
||||||
|
dbgCount, _ := cmd.Flags().GetCount("debug")
|
||||||
|
conf := config.JobSearchConfig{
|
||||||
|
Logging: config.JobSearchConfigLogging{},
|
||||||
|
}
|
||||||
|
if dbgCount > 1 {
|
||||||
|
conf.Logging.Level = "debug"
|
||||||
|
} else if dbgCount > 0 {
|
||||||
|
conf.Logging.Level = "info"
|
||||||
|
}
|
||||||
|
if conf.Logging.LogLevel() <= slog.LevelDebug {
|
||||||
|
log.EnableDebug()
|
||||||
|
} else if conf.Logging.LogLevel() <= slog.LevelInfo {
|
||||||
|
log.EnableInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
// With logging set up, these will be visible if trace is active
|
||||||
|
slog.Debug("root.PersistentPreRun")
|
||||||
|
slog.Debug(
|
||||||
|
"command: \"" +
|
||||||
|
strings.Join(strings.Split(cmd.CommandPath(), " ")[1:], " ") +
|
||||||
|
"\"",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Always load config unless command indicates not to
|
||||||
|
if val, ok := cmd.Annotations["no_config"]; !ok || val == "0" {
|
||||||
|
// Load config - from specified file if provided
|
||||||
|
cfgFile, _ := cmd.Flags().GetString("config")
|
||||||
|
if err := config.Init(cfgFile, &conf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach config to command context
|
||||||
|
ctx := cli.WithConfig(cmd.Context(), conf)
|
||||||
|
cmd.SetContext(ctx)
|
||||||
|
|
||||||
|
// Change logging level based off newly loaded config
|
||||||
|
if conf.Logging.LogLevel() <= slog.LevelDebug {
|
||||||
|
log.EnableDebug()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("root.PersistentPostRun")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute, called by main.main() handles all child commands and appropriate
|
||||||
|
// flags.
|
||||||
|
func Execute() {
|
||||||
|
err := rootCmd.Execute()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cobra.EnableTraverseRunHooks = true
|
||||||
|
|
||||||
|
log.Init()
|
||||||
|
|
||||||
|
pFlags := rootCmd.PersistentFlags()
|
||||||
|
pFlags.StringP(
|
||||||
|
"config", "c", "",
|
||||||
|
"config file (default: "+config.EtcConfigPath+"/config.yaml)")
|
||||||
|
pFlags.CountP("debug", "d", "enable debug output (repeat for trace)")
|
||||||
|
|
||||||
|
rootCmd.AddCommand(cmdVersion.NewCommand())
|
||||||
|
rootCmd.AddCommand(cmdConfig.NewCommand())
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
module git.avsdev.uk/Go/job-search
|
||||||
|
|
||||||
|
go 1.26.8
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/joho/godotenv v1.5.1
|
||||||
|
github.com/lmittmann/tint v1.2.0
|
||||||
|
github.com/spf13/cobra v1.10.2
|
||||||
|
github.com/spf13/viper v1.21.0
|
||||||
|
go.yaml.in/yaml/v3 v3.0.5
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||||
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
|
golang.org/x/text v0.28.0 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
github.com/lmittmann/tint v1.2.0 h1:AogHRHy8HUJUnNJBHJlYa+fR4YY8mko2cnCp67xn9JY=
|
||||||
|
github.com/lmittmann/tint v1.2.0/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||||
|
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
|
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||||
|
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||||
|
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||||
|
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||||
|
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||||
|
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||||
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||||
|
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||||
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||||
|
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCommand() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "config",
|
||||||
|
Short: "Configuration management",
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("config.PersistentPreRun")
|
||||||
|
},
|
||||||
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("config.PersistentPostRun")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.AddCommand(commandShow())
|
||||||
|
cmd.AddCommand(commandGet())
|
||||||
|
cmd.AddCommand(commandSet())
|
||||||
|
cmd.AddCommand(commandInit())
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"git.avsdev.uk/Go/job-search/internal/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func commandGet() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "get [config_key]",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
// Args: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// // Optionally run one of the validators provided by cobra
|
||||||
|
// if err := cobra.ExactArgs(1)(cmd, args); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// conf, ok := cli.ConfigFromContext(cmd.Context())
|
||||||
|
// if !ok {
|
||||||
|
// return errors.New("no configuration loaded")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// flatConf := config.FlattenConfig(conf)
|
||||||
|
// if !util.HasKey(flatConf, args[0]) {
|
||||||
|
// return fmt.Errorf("invalid config key specified: %s", args[0])
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return nil
|
||||||
|
// },
|
||||||
|
Short: "Get a value from configuration",
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("config.get.PersistentPreRun")
|
||||||
|
},
|
||||||
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("config.get.PersistentPostRun")
|
||||||
|
},
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
slog.Debug("config.get.Run")
|
||||||
|
|
||||||
|
if err := cobra.ExactArgs(1)(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conf, ok := cli.ConfigFromContext(cmd.Context())
|
||||||
|
if !ok {
|
||||||
|
return errors.New("no configuration loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := conf.ValueOf(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t, _ := conf.TypeOf(args[0])
|
||||||
|
fmt.Println(v, "<"+t.String()+">")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"terminal_log": "1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.avsdev.uk/Go/job-search/internal/config"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func commandInit() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "init [config_path]",
|
||||||
|
Args: cobra.MatchAll(cobra.MinimumNArgs(0), cobra.MaximumNArgs(1)),
|
||||||
|
Short: "Create an empty configuration file",
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("config.init.PersistentPreRun")
|
||||||
|
},
|
||||||
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("config.init.PersistentPostRun")
|
||||||
|
},
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
slog.Debug("config.init.Run")
|
||||||
|
|
||||||
|
if err := cobra.MinimumNArgs(0)(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := cobra.MaximumNArgs(1)(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfgFile, _ := cmd.Flags().GetString("config")
|
||||||
|
if cfgFile != "" {
|
||||||
|
// Use config file from the flag.
|
||||||
|
viper.SetConfigFile(cfgFile)
|
||||||
|
} else {
|
||||||
|
// Search config in /etc or local directory.
|
||||||
|
viper.AddConfigPath(config.EtcConfigPath)
|
||||||
|
viper.AddConfigPath(".")
|
||||||
|
viper.SetConfigName("config")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := viper.ReadInConfig()
|
||||||
|
if err == nil {
|
||||||
|
return errors.New("configuration file already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := err.(viper.ConfigFileNotFoundError)
|
||||||
|
if !errors.Is(err, os.ErrNotExist) && !ok {
|
||||||
|
// Config file was found but another error was produced
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load default configuration
|
||||||
|
config.LoadDefaultConfig()
|
||||||
|
|
||||||
|
if viper.ConfigFileUsed() == "" {
|
||||||
|
viper.SetConfigFile(config.EtcConfigPath + "/config.yaml")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print(
|
||||||
|
"writing default configuration to file: " +
|
||||||
|
viper.ConfigFileUsed() +
|
||||||
|
"\n")
|
||||||
|
|
||||||
|
if err := viper.WriteConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"no_config": "1",
|
||||||
|
"terminal_log": "1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.avsdev.uk/Go/job-search/internal/cli"
|
||||||
|
"git.avsdev.uk/Go/job-search/internal/config"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setConfig(conf config.JobSearchConfig, key string, value string) error {
|
||||||
|
var confValue any = nil
|
||||||
|
var confType reflect.Kind
|
||||||
|
var err error
|
||||||
|
|
||||||
|
confType, err = conf.TypeOf(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch confType {
|
||||||
|
case reflect.Bool:
|
||||||
|
confValue, err = strconv.ParseBool(value)
|
||||||
|
case reflect.String:
|
||||||
|
confValue = value
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
confValue, err = strconv.ParseInt(value, 10, 64)
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
confValue, err = strconv.ParseUint(value, 10, 64)
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
confValue, err = strconv.ParseFloat(value, 64)
|
||||||
|
default:
|
||||||
|
err = errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("config value type not accepted, expected <%s>", confType)
|
||||||
|
}
|
||||||
|
|
||||||
|
viper.Set(key, confValue)
|
||||||
|
if err := viper.WriteConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Print(key, " = ", confValue, " <", confType, ">\n")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func unsetConfig(conf config.JobSearchConfig, key string) error {
|
||||||
|
if _, err := conf.TypeOf(key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := viper.New()
|
||||||
|
v.SetConfigFile(viper.ConfigFileUsed())
|
||||||
|
|
||||||
|
keys := viper.AllKeys()
|
||||||
|
for i := range keys {
|
||||||
|
k := keys[i]
|
||||||
|
if k != key {
|
||||||
|
v.Set(k, viper.Get(k))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := v.WriteConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func commandSet() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "set [config_key] [config_value]",
|
||||||
|
Args: cobra.MatchAll(cobra.MinimumNArgs(1), cobra.MaximumNArgs(2)),
|
||||||
|
Short: "Set a value to configuration",
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("config.set.PersistentPreRun")
|
||||||
|
},
|
||||||
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("config.set.PersistentPostRun")
|
||||||
|
},
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
slog.Debug("config.set.Run")
|
||||||
|
|
||||||
|
if err := cobra.MinimumNArgs(1)(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := cobra.MaximumNArgs(2)(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
unset, unsetFlagErr := cmd.Flags().GetBool("unset")
|
||||||
|
if unsetFlagErr != nil {
|
||||||
|
return unsetFlagErr
|
||||||
|
}
|
||||||
|
if err := cobra.ExactArgs(2)(cmd, args); err != nil && !unset {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conf, ok := cli.ConfigFromContext(cmd.Context())
|
||||||
|
if !ok {
|
||||||
|
return errors.New("no configuration loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
if viper.ConfigFileUsed() == "" {
|
||||||
|
return errors.New("no configuration file exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
if unset {
|
||||||
|
return unsetConfig(conf, args[0])
|
||||||
|
} else {
|
||||||
|
return setConfig(conf, args[0], args[1])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"terminal_log": "1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.Flags().BoolP("unset", "x", false, "clear configuration setting")
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"git.avsdev.uk/Go/job-search/internal/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func commandShow() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "show",
|
||||||
|
Short: "Print config information",
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("config.show.PersistentPreRun")
|
||||||
|
},
|
||||||
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("config.show.PersistentPostRun")
|
||||||
|
},
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
slog.Debug("config.show.Run")
|
||||||
|
|
||||||
|
conf, ok := cli.ConfigFromContext(cmd.Context())
|
||||||
|
if !ok {
|
||||||
|
return errors.New("no configuration loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
if yaml, _ := cmd.Flags().GetBool("yaml"); yaml {
|
||||||
|
conf.PrintYaml()
|
||||||
|
} else if json, _ := cmd.Flags().GetBool("json"); json {
|
||||||
|
conf.PrintJson()
|
||||||
|
} else {
|
||||||
|
conf.PrintTerminal()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"terminal_log": "1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.Flags().Bool("json", false, "format configuration as json")
|
||||||
|
cmd.Flags().Bool("yaml", false, "format configuration as yaml")
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"git.avsdev.uk/Go/job-search/internal/version"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCommand() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Print version information",
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("version.PersistentPreRun")
|
||||||
|
},
|
||||||
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("version.PersistentPostRun")
|
||||||
|
},
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Debug("version.Run")
|
||||||
|
|
||||||
|
fmt.Printf("PBCNinja - %s\n", version.Version)
|
||||||
|
if version.BuildTime != "unknown" {
|
||||||
|
fmt.Printf("Built: %s\n", version.BuildTime)
|
||||||
|
}
|
||||||
|
if version.GitCommit != "unknown" {
|
||||||
|
fmt.Printf("Commit: %s\n", version.GitCommit)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"no_config": "1",
|
||||||
|
"terminal_log": "1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.avsdev.uk/Go/job-search/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Define a private key type to avoid collisions
|
||||||
|
type configContextKey struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define the key
|
||||||
|
var configKey = &configContextKey{"config"}
|
||||||
|
|
||||||
|
// Public accessor to store a value to ctx
|
||||||
|
func WithConfig(ctx context.Context, conf config.JobSearchConfig) context.Context {
|
||||||
|
// No allocation here since userIDKey is a pointer to a struct
|
||||||
|
return context.WithValue(ctx, configKey, conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public accessor to fetch a value from ctx
|
||||||
|
func ConfigFromContext(ctx context.Context) (config.JobSearchConfig, bool) {
|
||||||
|
v, ok := ctx.Value(configKey).(config.JobSearchConfig)
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log/slog"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JobSearchConfigLogging struct {
|
||||||
|
Level string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default configuration
|
||||||
|
func defaultConfigLogging() JobSearchConfigLogging {
|
||||||
|
return JobSearchConfigLogging{
|
||||||
|
Level: "warn",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts configuration to a map
|
||||||
|
func (conf JobSearchConfigLogging) Map() map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"level": conf.Level,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts configuration to a flat dotted format
|
||||||
|
func (conf JobSearchConfigLogging) Flatten() map[string]any {
|
||||||
|
confMap := conf.Map()
|
||||||
|
retMap := map[string]any{}
|
||||||
|
for k, v := range confMap {
|
||||||
|
retMap["logging."+k] = v
|
||||||
|
}
|
||||||
|
return retMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the expected type of a configuration key value
|
||||||
|
func (c JobSearchConfigLogging) TypeOf(key string) (reflect.Kind, error) {
|
||||||
|
switch key {
|
||||||
|
case "level":
|
||||||
|
return reflect.String, nil
|
||||||
|
default:
|
||||||
|
return reflect.Invalid, errors.New("unknown configuration key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the value of a configuration key
|
||||||
|
func (c JobSearchConfigLogging) ValueOf(key string) (any, error) {
|
||||||
|
switch key {
|
||||||
|
case "level":
|
||||||
|
return c.Level, nil
|
||||||
|
default:
|
||||||
|
return "", errors.New("unknown configuration key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the value of a configuration key
|
||||||
|
func (c *JobSearchConfigLogging) SetValue(key string, value any) error {
|
||||||
|
switch key {
|
||||||
|
case "level":
|
||||||
|
newLevel := value.(string)
|
||||||
|
switch newLevel {
|
||||||
|
case "debug", "info", "warn", "error":
|
||||||
|
c.Level = value.(string)
|
||||||
|
default:
|
||||||
|
return errors.New(
|
||||||
|
"invalid log level. Must be one of: debug, info, warn, error")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("unknown configuration key")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfigLevelToLogLevel(level string) slog.Level {
|
||||||
|
switch level {
|
||||||
|
case "debug":
|
||||||
|
return slog.LevelDebug
|
||||||
|
case "info":
|
||||||
|
return slog.LevelInfo
|
||||||
|
case "warn":
|
||||||
|
return slog.LevelWarn
|
||||||
|
case "error":
|
||||||
|
return slog.LevelError
|
||||||
|
default:
|
||||||
|
return 99
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *JobSearchConfigLogging) LogLevel() slog.Level {
|
||||||
|
return ConfigLevelToLogLevel(c.Level)
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"maps"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
const EnvPrefix string = "JOBSEARCH"
|
||||||
|
const EtcConfigPath string = "/etc/job-search"
|
||||||
|
|
||||||
|
type JobSearchConfig struct {
|
||||||
|
Logging JobSearchConfigLogging
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default configuration
|
||||||
|
func LoadDefaultConfig() {
|
||||||
|
conf := JobSearchConfig{
|
||||||
|
Logging: defaultConfigLogging(),
|
||||||
|
}
|
||||||
|
LoadToViper(conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts configuration to a map
|
||||||
|
func (conf JobSearchConfig) Map() map[string]any {
|
||||||
|
confMap := map[string]any{}
|
||||||
|
confMap["logging"] = conf.Logging.Map()
|
||||||
|
return confMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts configuration to a flat dotted format
|
||||||
|
func (conf JobSearchConfig) Flatten() map[string]any {
|
||||||
|
confFlat := map[string]any{}
|
||||||
|
maps.Copy(confFlat, conf.Logging.Flatten())
|
||||||
|
return confFlat
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the expected type of a configuration key value
|
||||||
|
func (c JobSearchConfig) TypeOf(key string) (reflect.Kind, error) {
|
||||||
|
keyParts := strings.Split(key, ".")
|
||||||
|
switch keyParts[0] {
|
||||||
|
case "logging":
|
||||||
|
return c.Logging.TypeOf(strings.Join(keyParts[1:], "."))
|
||||||
|
default:
|
||||||
|
return reflect.Invalid, errors.New("unknown configuration key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the expected type of a configuration value
|
||||||
|
func (c JobSearchConfig) ValueOf(key string) (any, error) {
|
||||||
|
keyParts := strings.Split(key, ".")
|
||||||
|
switch keyParts[0] {
|
||||||
|
case "logging":
|
||||||
|
return c.Logging.ValueOf(strings.Join(keyParts[1:], "."))
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unknown configuration key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the expected type of a configuration value
|
||||||
|
func (c *JobSearchConfig) SetValue(key string, value any) error {
|
||||||
|
keyParts := strings.Split(key, ".")
|
||||||
|
switch keyParts[0] {
|
||||||
|
case "logging":
|
||||||
|
return c.Logging.SetValue(strings.Join(keyParts[1:], "."), value)
|
||||||
|
default:
|
||||||
|
return errors.New("unknown configuration key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadFromViper(conf *JobSearchConfig) {
|
||||||
|
keys := viper.AllKeys()
|
||||||
|
for idx := range keys {
|
||||||
|
key := keys[idx]
|
||||||
|
keyType, _ := conf.TypeOf(key)
|
||||||
|
if keyType == reflect.Invalid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch keyType {
|
||||||
|
case reflect.String:
|
||||||
|
conf.SetValue(key, viper.GetString(key))
|
||||||
|
case reflect.Int:
|
||||||
|
conf.SetValue(key, viper.GetInt(key))
|
||||||
|
case reflect.Bool:
|
||||||
|
conf.SetValue(key, viper.GetBool(key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadToViper(conf JobSearchConfig) {
|
||||||
|
flatMap := conf.Flatten()
|
||||||
|
for key := range flatMap {
|
||||||
|
viper.SetDefault(key, flatMap[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Debug: Prints detected environment variables
|
||||||
|
func printEnv() {
|
||||||
|
slog.Debug("Environment:")
|
||||||
|
env := os.Environ()
|
||||||
|
slices.Sort(env)
|
||||||
|
for _, e := range env {
|
||||||
|
pair := strings.SplitN(e, "=", 2)
|
||||||
|
if strings.HasPrefix(pair[0], EnvPrefix) {
|
||||||
|
slog.Debug("\t" + pair[0] + "=" + pair[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug: Prints loaded configuration
|
||||||
|
func printLoadedConfig() {
|
||||||
|
slog.Debug("Loaded config:")
|
||||||
|
keys := viper.AllKeys()
|
||||||
|
for i := range keys {
|
||||||
|
k := keys[i]
|
||||||
|
v := viper.Get(k)
|
||||||
|
t := reflect.TypeOf(v)
|
||||||
|
slog.Debug(
|
||||||
|
"\t" + k + " = " + fmt.Sprintf("%#v", v) + " <" + t.Kind().String() + ">")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug: Prints parsed configuration
|
||||||
|
func printConfig(conf JobSearchConfig) {
|
||||||
|
slog.Debug("Config:")
|
||||||
|
conf.PrintLog(slog.LevelDebug)
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reads in dotenv file for configuration
|
||||||
|
func loadEnv() error {
|
||||||
|
if err := godotenv.Load(); err != nil {
|
||||||
|
return fmt.Errorf("invalid .env file: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads in config file and ENV variables if set to form a configuration.
|
||||||
|
func loadConfig(cfgFile string) error {
|
||||||
|
if cfgFile != "" {
|
||||||
|
// Use config file from the flag.
|
||||||
|
viper.SetConfigFile(cfgFile)
|
||||||
|
} else {
|
||||||
|
// Search config in /etc or local directory.
|
||||||
|
viper.AddConfigPath(EtcConfigPath)
|
||||||
|
viper.AddConfigPath(".")
|
||||||
|
|
||||||
|
viper.SetConfigName("config")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load default configuration
|
||||||
|
LoadDefaultConfig()
|
||||||
|
|
||||||
|
viper.SetEnvPrefix(EnvPrefix)
|
||||||
|
viper.AutomaticEnv() // read in environment variables that match
|
||||||
|
|
||||||
|
err := viper.ReadInConfig()
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
|
// Indicates an explicitly set config file is not found or
|
||||||
|
// that no config file was found in any search path
|
||||||
|
slog.Warn("No config file found!")
|
||||||
|
} else {
|
||||||
|
// Config file was found but another error was produced
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Config file found and successfully parsed
|
||||||
|
slog.Info("Using config file: " + viper.ConfigFileUsed())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initilises environment file configuration and general app configuration
|
||||||
|
func Init(cfgFile string, conf *JobSearchConfig) error {
|
||||||
|
if err := loadEnv(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if conf.Logging.LogLevel() <= slog.LevelDebug {
|
||||||
|
printEnv()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := loadConfig(cfgFile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
oldLogLevel := conf.Logging.Level
|
||||||
|
LoadFromViper(conf)
|
||||||
|
if ConfigLevelToLogLevel(oldLogLevel) < conf.Logging.LogLevel() {
|
||||||
|
conf.Logging.Level = oldLogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Logging.LogLevel() <= slog.LevelDebug {
|
||||||
|
printLoadedConfig()
|
||||||
|
printConfig(*conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"go.yaml.in/yaml/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Prints configuration to stdout in flat dotted format
|
||||||
|
func (conf JobSearchConfig) PrintTerminal() {
|
||||||
|
for k, v := range conf.Flatten() {
|
||||||
|
typ, _ := conf.TypeOf(k)
|
||||||
|
fmt.Print(k, " = ", v, " <", typ, ">\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints configuration to stdout as JSON
|
||||||
|
func (conf JobSearchConfig) PrintJson() {
|
||||||
|
confMap := conf.Map()
|
||||||
|
jbytes, _ := json.MarshalIndent(confMap, "", " ")
|
||||||
|
fmt.Print(string(jbytes) + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints configuration to stdout as YAML
|
||||||
|
func (conf JobSearchConfig) PrintYaml() {
|
||||||
|
confMap := conf.Map()
|
||||||
|
jbytes, _ := yaml.Marshal(confMap)
|
||||||
|
fmt.Print(string(jbytes) + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints configuration to logrus log at debug, info (default) or error level
|
||||||
|
func (conf JobSearchConfig) PrintLog(atLevel slog.Level) {
|
||||||
|
var logFn func(args ...interface{})
|
||||||
|
|
||||||
|
switch atLevel {
|
||||||
|
case slog.LevelDebug:
|
||||||
|
logFn = func(args ...interface{}) {
|
||||||
|
slog.Debug(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
case slog.LevelError:
|
||||||
|
logFn = func(args ...interface{}) {
|
||||||
|
slog.Error(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
case slog.LevelWarn:
|
||||||
|
logFn = func(args ...interface{}) {
|
||||||
|
slog.Warn(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
logFn = func(args ...interface{}) {
|
||||||
|
slog.Info(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range conf.Flatten() {
|
||||||
|
logFn("\t", k, " = ", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/lmittmann/tint"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logLevel slog.LevelVar
|
||||||
|
|
||||||
|
// Set up the logger using defaults
|
||||||
|
func Init() {
|
||||||
|
logLevel.Set(slog.LevelWarn)
|
||||||
|
opts := &slog.HandlerOptions{
|
||||||
|
Level: &logLevel,
|
||||||
|
}
|
||||||
|
handler := slog.NewTextHandler(os.Stdout, opts)
|
||||||
|
logger := slog.New(handler)
|
||||||
|
slog.SetDefault(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable info level logging
|
||||||
|
func EnableInfo() {
|
||||||
|
if logLevel.Level() > slog.LevelInfo {
|
||||||
|
logLevel.Set(slog.LevelInfo)
|
||||||
|
slog.Info("Info logging enabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable debug level logging
|
||||||
|
func EnableDebug() {
|
||||||
|
if logLevel.Level() > slog.LevelDebug {
|
||||||
|
logLevel.Set(slog.LevelDebug)
|
||||||
|
slog.Debug("Debug logging enabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogToTerminal() {
|
||||||
|
opts := &tint.Options{
|
||||||
|
Level: &logLevel,
|
||||||
|
TimeFormat: "*",
|
||||||
|
}
|
||||||
|
handler := tint.NewTextHandler(os.Stdout, opts)
|
||||||
|
logger := slog.New(handler)
|
||||||
|
slog.SetDefault(logger)
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package version
|
||||||
|
|
||||||
|
// Version information (set at build time with -ldflags)
|
||||||
|
var (
|
||||||
|
Version = "dev"
|
||||||
|
BuildTime = "unknown"
|
||||||
|
GitCommit = "unknown"
|
||||||
|
)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2026 Craig Williams <craig@avsdev.uk>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.avsdev.uk/Go/job-search/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cmd.Execute()
|
||||||
|
}
|
||||||
Executable
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Build script for JobSearch release
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BUILD_DIR="build"
|
||||||
|
RELEASE_DIR="release"
|
||||||
|
|
||||||
|
DO_RELEASE=1
|
||||||
|
|
||||||
|
BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S')
|
||||||
|
GIT_COMMIT=
|
||||||
|
VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo 'unknown')
|
||||||
|
VERSION_TAG=$(git describe --tags 2>/dev/null || echo 'unknown')
|
||||||
|
|
||||||
|
if [ "$VERSION" != "$VERSION_TAG" ]; then
|
||||||
|
echo "Development build detected! (not installing to release)"
|
||||||
|
GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')
|
||||||
|
VERSION="${VERSION}.${GIT_COMMIT}"
|
||||||
|
DO_RELEASE=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
LD_FLAGS=${LD_FLAGS:-""}
|
||||||
|
LD_FLAGS="$LD_FLAGS -X git.avsdev.uk/Go/JobSearch/internal/version.Version=${VERSION}"
|
||||||
|
LD_FLAGS="$LD_FLAGS -X git.avsdev.uk/Go/JobSearch/internal/version.BuildTime=${BUILD_TIME}"
|
||||||
|
if [ "$GIT_COMMIT" != "" ]; then
|
||||||
|
LD_FLAGS="$LD_FLAGS -X git.avsdev.uk/Go/JobSearch/internal/version.GitCommit=${GIT_COMMIT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build binary with version info
|
||||||
|
go build \
|
||||||
|
-ldflags="$LD_FLAGS" \
|
||||||
|
-trimpath \
|
||||||
|
-o $BUILD_DIR/job-search-$VERSION \
|
||||||
|
./
|
||||||
|
|
||||||
|
# If proper release, copy to output directory for packaging
|
||||||
|
if [[ $DO_RELEASE -eq 1 ]]; then
|
||||||
|
install -DT $BUILD_DIR/job-search-$VERSION $RELEASE_DIR/job-search
|
||||||
|
fi
|
||||||
Reference in new issue
Block a user