chore: coderabbit suggestions
This commit is contained in:
parent
c72d180fae
commit
c5f0387045
|
|
@ -38,28 +38,28 @@ repos:
|
|||
entry: make service-codestyle
|
||||
language: system
|
||||
pass_filenames: false
|
||||
files: ^(service/|proto/|lang/)
|
||||
files: ^(service/|proto/|lang/|Makefile)
|
||||
|
||||
- id: frontend-codestyle
|
||||
name: frontend-codestyle
|
||||
entry: make frontend-codestyle
|
||||
language: system
|
||||
pass_filenames: false
|
||||
files: ^frontend/
|
||||
files: ^(frontend/|Makefile)
|
||||
|
||||
- id: service-unittests
|
||||
name: service-unittests
|
||||
entry: make service-unittests
|
||||
language: system
|
||||
pass_filenames: false
|
||||
files: ^(service/|proto/|lang/)
|
||||
files: ^(service/|proto/|lang/|Makefile)
|
||||
|
||||
- id: service-build
|
||||
name: service-build
|
||||
entry: make service
|
||||
language: system
|
||||
pass_filenames: false
|
||||
files: ^(service/|proto/|lang/)
|
||||
files: ^(service/|proto/|lang/|Makefile)
|
||||
|
||||
- id: it
|
||||
name: integration-tests
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
"stylelint-config-standard": "^40.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0.0"
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,6 @@
|
|||
"vue-router": "^5.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0.0"
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,10 +247,16 @@ function updateFromJson (json) {
|
|||
if (json.datetimeRateLimitExpires) {
|
||||
const date = new Date(json.datetimeRateLimitExpires.replace(' ', 'T'))
|
||||
rateLimitExpires.value = date.getTime() / 1000
|
||||
if (bindingId.value) {
|
||||
rateLimits[bindingId.value] = rateLimitExpires.value
|
||||
}
|
||||
updateRateLimitStatus()
|
||||
} else if (json.datetimeRateLimitExpires === '') {
|
||||
// Explicitly clear if empty string
|
||||
rateLimitExpires.value = 0
|
||||
if (bindingId.value) {
|
||||
rateLimits[bindingId.value] = 0
|
||||
}
|
||||
updateRateLimitStatus()
|
||||
}
|
||||
}
|
||||
|
|
@ -359,6 +365,15 @@ async function pollExecutionUntilDone (trackingId) {
|
|||
}
|
||||
}
|
||||
|
||||
let stopButtonResultWatch = null
|
||||
|
||||
function stopWatchingButtonResult () {
|
||||
if (stopButtonResultWatch) {
|
||||
stopButtonResultWatch()
|
||||
stopButtonResultWatch = null
|
||||
}
|
||||
}
|
||||
|
||||
async function startAction (actionArgs) {
|
||||
buttonClasses.value = [] // Removes old animation classes
|
||||
|
||||
|
|
@ -376,7 +391,8 @@ async function startAction (actionArgs) {
|
|||
|
||||
console.log('Watching buttonResults for', startActionArgs.uniqueTrackingId)
|
||||
|
||||
watch(
|
||||
stopWatchingButtonResult()
|
||||
stopButtonResultWatch = watch(
|
||||
() => buttonResults[startActionArgs.uniqueTrackingId],
|
||||
(newResult, oldResult) => {
|
||||
onLogEntryChanged(newResult)
|
||||
|
|
@ -397,12 +413,18 @@ async function startAction (actionArgs) {
|
|||
await pollExecutionUntilDone(trackingId)
|
||||
}
|
||||
} catch (err) {
|
||||
stopWatchingButtonResult()
|
||||
console.error('Failed to start action:', err)
|
||||
}
|
||||
}
|
||||
|
||||
function onLogEntryChanged (logEntry) {
|
||||
if (!logEntry) {
|
||||
return
|
||||
}
|
||||
|
||||
if (logEntry.executionFinished) {
|
||||
stopWatchingButtonResult()
|
||||
onExecutionFinished(logEntry)
|
||||
} else if (logEntry.queued && !logEntry.executionStarted) {
|
||||
onExecutionQueued(logEntry)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<section
|
||||
v-if="!dashboard && !initError"
|
||||
v-if="!dashboard && !initError && !loadError"
|
||||
style="text-align: center; padding: 2em;"
|
||||
>
|
||||
<HugeiconsIcon
|
||||
|
|
@ -27,6 +27,19 @@
|
|||
Please check your configuration and try again.
|
||||
</p>
|
||||
</section>
|
||||
<section
|
||||
v-else-if="loadError"
|
||||
style="text-align: center; padding: 2em;"
|
||||
class="bad"
|
||||
>
|
||||
<h2 style="color: var(--error);">
|
||||
Failed to Load Dashboard
|
||||
</h2>
|
||||
<p>{{ loadError }}</p>
|
||||
<p style="color: var(--fg2);">
|
||||
Please check your configuration and try again.
|
||||
</p>
|
||||
</section>
|
||||
<template v-else-if="dashboard">
|
||||
<section v-if="dashboard.contents.length == 0">
|
||||
<div
|
||||
|
|
@ -135,6 +148,7 @@ const router = useRouter()
|
|||
const dashboard = ref(null)
|
||||
const loadingTime = ref(0)
|
||||
const initError = ref(null)
|
||||
const loadError = ref(null)
|
||||
let loadingTimer = null
|
||||
let checkInitInterval = null
|
||||
let dashboardRequestId = 0
|
||||
|
|
@ -194,8 +208,9 @@ async function getDashboard () {
|
|||
const pageTitle = window.initResponse?.pageTitle || 'OliveTin'
|
||||
document.title = ret.dashboard.title + ' - ' + pageTitle
|
||||
|
||||
// Clear any previous init error since we successfully loaded
|
||||
// Clear any previous errors since we successfully loaded
|
||||
initError.value = null
|
||||
loadError.value = null
|
||||
|
||||
// Stop the loading timer once dashboard is loaded
|
||||
if (loadingTimer) {
|
||||
|
|
@ -210,9 +225,9 @@ async function getDashboard () {
|
|||
return
|
||||
}
|
||||
|
||||
// On error, provide a safe fallback state
|
||||
console.error('Failed to load dashboard', e)
|
||||
dashboard.value = { title: title || 'Default', contents: [] }
|
||||
dashboard.value = null
|
||||
loadError.value = e.message || 'Failed to load dashboard'
|
||||
const pageTitle = window.initResponse?.pageTitle || 'OliveTin'
|
||||
document.title = 'Error - ' + pageTitle
|
||||
|
||||
|
|
@ -221,14 +236,13 @@ async function getDashboard () {
|
|||
clearInterval(loadingTimer)
|
||||
loadingTimer = null
|
||||
}
|
||||
|
||||
// Set attribute even on error so tests can proceed
|
||||
document.body.setAttribute('loaded-dashboard', title || 'error')
|
||||
}
|
||||
}
|
||||
|
||||
function waitForInitAndLoadDashboard () {
|
||||
document.body.removeAttribute('loaded-dashboard')
|
||||
loadError.value = null
|
||||
dashboard.value = null
|
||||
|
||||
if (loadingTimer) {
|
||||
clearInterval(loadingTimer)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ export default {
|
|||
required: true
|
||||
}
|
||||
},
|
||||
emits: ['show'],
|
||||
data () {
|
||||
return {
|
||||
trackingId: '',
|
||||
|
|
@ -51,11 +52,11 @@ export default {
|
|||
this.isWaiting = true
|
||||
},
|
||||
|
||||
show () {
|
||||
async show () {
|
||||
this.$emit('show')
|
||||
|
||||
if (window.executionDialog) {
|
||||
window.executionDialog.reset()
|
||||
await window.executionDialog.reset()
|
||||
window.executionDialog.show()
|
||||
window.executionDialog.fetchExecutionResult(this.trackingId)
|
||||
}
|
||||
|
|
@ -89,7 +90,10 @@ export default {
|
|||
// For execution button, we don't need to update classes as much
|
||||
// since it's a simpler component
|
||||
if (resultCssClass) {
|
||||
this.$el.classList.add(resultCssClass)
|
||||
const button = this.$el.querySelector('button')
|
||||
if (button) {
|
||||
button.classList.add(resultCssClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -319,11 +319,13 @@ function getArgumentValue (arg) {
|
|||
function handleJustificationInput (event) {
|
||||
justificationValue.value = event.target.value
|
||||
justificationEditedManually.value = true
|
||||
event.target.setCustomValidity('')
|
||||
}
|
||||
|
||||
function handleInput (arg, event) {
|
||||
const value = event.target.type === 'checkbox' ? event.target.checked : event.target.value
|
||||
argValues.value[arg.name] = value
|
||||
event.target.setCustomValidity('')
|
||||
updateUrlWithArg(arg.name, value)
|
||||
updateJustificationFromTemplate()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,9 +117,18 @@ async function fetchDiagnostics () {
|
|||
}
|
||||
|
||||
async function generateServerDiagnostics () {
|
||||
const response = await window.client.serverDiagnostics()
|
||||
console.log('response', response)
|
||||
serverDiagnostics.value = `\`\`\`\n${response.alert}\n\`\`\`\n`
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const response = await window.client.serverDiagnostics()
|
||||
console.log('response', response)
|
||||
serverDiagnostics.value = `\`\`\`\n${response.alert}\n\`\`\`\n`
|
||||
} catch (err) {
|
||||
console.error('Failed to generate server diagnostics:', err)
|
||||
serverDiagnostics.value = ''
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function copyServerDiagnostics () {
|
||||
|
|
|
|||
|
|
@ -24,10 +24,15 @@
|
|||
@click="loginWithOAuth(provider)"
|
||||
>
|
||||
<span
|
||||
v-if="provider.icon"
|
||||
v-if="providerIcon(provider)"
|
||||
class="provider-icon"
|
||||
v-html="provider.icon"
|
||||
/>
|
||||
>
|
||||
<iconify-icon
|
||||
v-if="providerIcon(provider).kind === 'iconify'"
|
||||
:icon="providerIcon(provider).id"
|
||||
/>
|
||||
<span v-else>{{ providerIcon(provider).text }}</span>
|
||||
</span>
|
||||
<span class="provider-name">Login with {{ provider.title }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -96,6 +101,38 @@ const hasOAuth = ref(false)
|
|||
const hasLocalLogin = ref(false)
|
||||
const oauthProviders = ref([])
|
||||
|
||||
const trustedProviderIconifyIds = {
|
||||
github: 'simple-icons:github',
|
||||
google: 'simple-icons:google'
|
||||
}
|
||||
|
||||
function providerIcon (provider) {
|
||||
const raw = (provider?.icon || '').trim()
|
||||
if (!raw) {
|
||||
return null
|
||||
}
|
||||
|
||||
const iconifyTagMatch = raw.match(/<iconify-icon\b[^>]*\bicon=["']([^"']+)["'][^>]*>/i)
|
||||
if (iconifyTagMatch) {
|
||||
return { kind: 'iconify', id: iconifyTagMatch[1] }
|
||||
}
|
||||
|
||||
if (/^[a-z0-9-]+:[a-z0-9-]+$/i.test(raw)) {
|
||||
return { kind: 'iconify', id: raw }
|
||||
}
|
||||
|
||||
const trustedId = trustedProviderIconifyIds[provider.key]
|
||||
if (trustedId && (raw.includes('<') || raw === provider.key)) {
|
||||
return { kind: 'iconify', id: trustedId }
|
||||
}
|
||||
|
||||
if (!raw.includes('<')) {
|
||||
return { kind: 'text', text: raw }
|
||||
}
|
||||
|
||||
return trustedId ? { kind: 'iconify', id: trustedId } : null
|
||||
}
|
||||
|
||||
function loadLoginOptions () {
|
||||
// Use the init response data that was loaded in App.vue
|
||||
if (window.initResponse) {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,9 @@ linters:
|
|||
- staticcheck
|
||||
- unconvert
|
||||
- unused
|
||||
|
||||
linters-settings:
|
||||
gocyclo:
|
||||
min-complexity: 5
|
||||
|
||||
issues:
|
||||
exclude-dirs:
|
||||
- gen
|
||||
settings:
|
||||
gocyclo:
|
||||
min-complexity: 5
|
||||
exclusions:
|
||||
paths:
|
||||
- gen
|
||||
|
|
|
|||
|
|
@ -53,11 +53,11 @@ find-flakey-tests-inf:
|
|||
go run ./scripts/find-flakey-tests-inf
|
||||
|
||||
go-tools:
|
||||
go install "github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest"
|
||||
go install "github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2"
|
||||
|
||||
.PHONY: unittests unittests-fast find-flakey-tests find-flakey-tests-inf
|
||||
|
||||
go-tools-all:
|
||||
go install "github.com/bufbuild/buf/cmd/buf"
|
||||
go install "github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest"
|
||||
go install "github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2"
|
||||
go install "google.golang.org/protobuf/cmd/protoc-gen-go"
|
||||
|
|
|
|||
|
|
@ -349,13 +349,13 @@ func getUserInfo(cfg *config.Config, client *http.Client, provider *config.OAuth
|
|||
return ret
|
||||
}
|
||||
|
||||
defer func() { _ = res.Body.Close() }()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
log.Errorf("Failed to get user data: %v", res.StatusCode)
|
||||
return ret
|
||||
}
|
||||
|
||||
defer func() { _ = res.Body.Close() }()
|
||||
|
||||
contents, err := io.ReadAll(res.Body)
|
||||
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/knadh/koanf/providers/file"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUserLoadingFromConfig(t *testing.T) {
|
||||
|
|
@ -30,13 +31,13 @@ actions:
|
|||
|
||||
// Create temporary file
|
||||
tmpFile, err := os.CreateTemp("", "test_config_*.yaml")
|
||||
assert.NoError(t, err, "Should create temporary file")
|
||||
require.NoError(t, err, "Should create temporary file")
|
||||
defer func() { _ = os.Remove(tmpFile.Name()) }()
|
||||
|
||||
// Write test config to file
|
||||
_, err = tmpFile.WriteString(testConfig)
|
||||
assert.NoError(t, err, "Should write test config to file")
|
||||
assert.NoError(t, tmpFile.Close())
|
||||
require.NoError(t, err, "Should write test config to file")
|
||||
require.NoError(t, tmpFile.Close())
|
||||
|
||||
// Load config using koanf
|
||||
k := koanf.New(".")
|
||||
|
|
@ -81,12 +82,12 @@ actions:
|
|||
`
|
||||
|
||||
tmpFile, err := os.CreateTemp("", "test_config_empty_*.yaml")
|
||||
assert.NoError(t, err, "Should create temporary file")
|
||||
require.NoError(t, err, "Should create temporary file")
|
||||
defer func() { _ = os.Remove(tmpFile.Name()) }()
|
||||
|
||||
_, err = tmpFile.WriteString(testConfig)
|
||||
assert.NoError(t, err, "Should write test config to file")
|
||||
assert.NoError(t, tmpFile.Close())
|
||||
require.NoError(t, err, "Should write test config to file")
|
||||
require.NoError(t, tmpFile.Close())
|
||||
|
||||
k := koanf.New(".")
|
||||
err = k.Load(file.Provider(tmpFile.Name()), yaml.Parser())
|
||||
|
|
@ -116,12 +117,12 @@ actions:
|
|||
`
|
||||
|
||||
tmpFile, err := os.CreateTemp("", "test_config_disabled_*.yaml")
|
||||
assert.NoError(t, err, "Should create temporary file")
|
||||
require.NoError(t, err, "Should create temporary file")
|
||||
defer func() { _ = os.Remove(tmpFile.Name()) }()
|
||||
|
||||
_, err = tmpFile.WriteString(testConfig)
|
||||
assert.NoError(t, err, "Should write test config to file")
|
||||
assert.NoError(t, tmpFile.Close())
|
||||
require.NoError(t, err, "Should write test config to file")
|
||||
require.NoError(t, tmpFile.Close())
|
||||
|
||||
k := koanf.New(".")
|
||||
err = k.Load(file.Provider(tmpFile.Name()), yaml.Parser())
|
||||
|
|
@ -147,12 +148,12 @@ actions:
|
|||
`
|
||||
|
||||
tmpFile, err := os.CreateTemp("", "test_config_no_auth_*.yaml")
|
||||
assert.NoError(t, err, "Should create temporary file")
|
||||
require.NoError(t, err, "Should create temporary file")
|
||||
defer func() { _ = os.Remove(tmpFile.Name()) }()
|
||||
|
||||
_, err = tmpFile.WriteString(testConfig)
|
||||
assert.NoError(t, err, "Should write test config to file")
|
||||
assert.NoError(t, tmpFile.Close())
|
||||
require.NoError(t, err, "Should write test config to file")
|
||||
require.NoError(t, tmpFile.Close())
|
||||
|
||||
k := koanf.New(".")
|
||||
err = k.Load(file.Provider(tmpFile.Name()), yaml.Parser())
|
||||
|
|
|
|||
|
|
@ -74,7 +74,11 @@ func watchPath(ctx *watchContext) {
|
|||
return
|
||||
}
|
||||
|
||||
defer func() { _ = watcher.Close() }()
|
||||
defer func() {
|
||||
if err := watcher.Close(); err != nil {
|
||||
log.Errorf("Failed to close file watcher: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
done := make(chan bool)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,12 @@ func WriteFile(filename string, out []byte) {
|
|||
return
|
||||
}
|
||||
|
||||
_ = handle.Close()
|
||||
if err := handle.Close(); err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": err,
|
||||
"filename": filename,
|
||||
}).Errorf("Failed to close %v", filename)
|
||||
}
|
||||
}
|
||||
|
||||
err := os.WriteFile(filename, out, 0600)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type RuntimeInfo struct {
|
||||
|
|
@ -85,6 +87,14 @@ func getOsReleasePrettyName() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if closeErr := handle.Close(); closeErr != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"error": closeErr,
|
||||
}).Warn("Failed to close /etc/os-release")
|
||||
}
|
||||
}()
|
||||
|
||||
scanner := bufio.NewScanner(handle)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
|
|
@ -96,7 +106,5 @@ func getOsReleasePrettyName() string {
|
|||
}
|
||||
}
|
||||
|
||||
_ = handle.Close()
|
||||
|
||||
return "notfound"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,10 +202,13 @@ func appendFile(path, content string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
_, err = file.WriteString(content)
|
||||
return err
|
||||
_, writeErr := file.WriteString(content)
|
||||
closeErr := file.Close()
|
||||
if writeErr != nil {
|
||||
return writeErr
|
||||
}
|
||||
return closeErr
|
||||
}
|
||||
|
||||
func newTestRunState() *testRunState {
|
||||
|
|
|
|||
Loading…
Reference in New Issue