Skip to content

Commit

Permalink
Improve linter messages in UI (woodpecker-ci#4351)
Browse files Browse the repository at this point in the history
  • Loading branch information
xoxys authored Nov 11, 2024
1 parent 04e8309 commit a1193f0
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 77 deletions.
34 changes: 17 additions & 17 deletions pipeline/frontend/yaml/linter/linter.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (l *Linter) lintFile(config *WorkflowConfig) error {
var linterErr error

if len(config.Workflow.Steps.ContainerList) == 0 {
linterErr = multierr.Append(linterErr, newLinterError("Invalid or missing steps section", config.File, "steps", false))
linterErr = multierr.Append(linterErr, newLinterError("Invalid or missing `steps` section", config.File, "steps", false))
}

if err := l.lintCloneSteps(config); err != nil {
Expand Down Expand Up @@ -123,7 +123,7 @@ func (l *Linter) lintCloneSteps(config *WorkflowConfig) error {
if !utils.MatchImageDynamic(container.Image, trustedClonePlugins...) {
linterErr = multierr.Append(linterErr,
newLinterError(
"Specified clone image does not match allow list, netrc will not be injected",
"Specified clone image does not match allow list, netrc is not injected",
config.File, fmt.Sprintf("clone.%s", container.Name), true),
)
}
Expand Down Expand Up @@ -173,7 +173,7 @@ func (l *Linter) lintImage(config *WorkflowConfig, c *types.Container, area stri
func (l *Linter) lintPrivilegedPlugins(config *WorkflowConfig, c *types.Container, area string) error {
// lint for conflicts of https://github.com/woodpecker-ci/woodpecker/pull/3918
if utils.MatchImage(c.Image, "plugins/docker", "plugins/gcr", "plugins/ecr", "woodpeckerci/plugin-docker-buildx") {
msg := fmt.Sprintf("The formerly privileged plugin '%s' is no longer privileged by default, if required, add it to WOODPECKER_PLUGINS_PRIVILEGED", c.Image)
msg := fmt.Sprintf("The formerly privileged plugin `%s` is no longer privileged by default, if required, add it to `WOODPECKER_PLUGINS_PRIVILEGED`", c.Image)
// check first if user did not add them back
if l.privilegedPlugins != nil && !utils.MatchImageDynamic(c.Image, *l.privilegedPlugins...) {
return newLinterError(msg, config.File, fmt.Sprintf("%s.%s", area, c.Name), false)
Expand All @@ -191,16 +191,16 @@ func (l *Linter) lintSettings(config *WorkflowConfig, c *types.Container, field
return nil
}
if len(c.Commands) != 0 {
return newLinterError("Cannot configure both commands and settings", config.File, fmt.Sprintf("%s.%s", field, c.Name), false)
return newLinterError("Cannot configure both `commands` and `settings`", config.File, fmt.Sprintf("%s.%s", field, c.Name), false)
}
if len(c.Entrypoint) != 0 {
return newLinterError("Cannot configure both entrypoint and settings", config.File, fmt.Sprintf("%s.%s", field, c.Name), false)
return newLinterError("Cannot configure both `entrypoint` and `settings`", config.File, fmt.Sprintf("%s.%s", field, c.Name), false)
}
if len(c.Environment) != 0 {
return newLinterError("Should not configure both environment and settings", config.File, fmt.Sprintf("%s.%s", field, c.Name), true)
return newLinterError("Should not configure both `environment` and `settings`", config.File, fmt.Sprintf("%s.%s", field, c.Name), true)
}
if len(c.Secrets) != 0 {
return newLinterError("Should not configure both secrets and settings", config.File, fmt.Sprintf("%s.%s", field, c.Name), true)
return newLinterError("Should not configure both `secrets` and `settings`", config.File, fmt.Sprintf("%s.%s", field, c.Name), true)
}
return nil
}
Expand All @@ -210,32 +210,32 @@ func (l *Linter) lintTrusted(config *WorkflowConfig, c *types.Container, area st
errors := []string{}
if !l.trusted.Security {
if c.Privileged {
errors = append(errors, "Insufficient privileges to use privileged mode")
errors = append(errors, "Insufficient trust level to use `privileged` mode")
}
}
if !l.trusted.Network {
if len(c.DNS) != 0 {
errors = append(errors, "Insufficient privileges to use custom dns")
errors = append(errors, "Insufficient trust level to use custom `dns`")
}
if len(c.DNSSearch) != 0 {
errors = append(errors, "Insufficient privileges to use dns_search")
errors = append(errors, "Insufficient trust level to use `dns_search`")
}
if len(c.ExtraHosts) != 0 {
errors = append(errors, "Insufficient privileges to use extra_hosts")
errors = append(errors, "Insufficient trust level to use `extra_hosts`")
}
if len(c.NetworkMode) != 0 {
errors = append(errors, "Insufficient privileges to use network_mode")
errors = append(errors, "Insufficient trust level to use `network_mode`")
}
}
if !l.trusted.Volumes {
if len(c.Devices) != 0 {
errors = append(errors, "Insufficient privileges to use devices")
errors = append(errors, "Insufficient trust level to use `devices`")
}
if len(c.Volumes.Volumes) != 0 {
errors = append(errors, "Insufficient privileges to use volumes")
errors = append(errors, "Insufficient trust level to use `volumes`")
}
if len(c.Tmpfs) != 0 {
errors = append(errors, "Insufficient privileges to use tmpfs")
errors = append(errors, "Insufficient trust level to use `tmpfs`")
}
}

Expand Down Expand Up @@ -279,7 +279,7 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
if len(container.Secrets) > 0 {
err = multierr.Append(err, &errorTypes.PipelineError{
Type: errorTypes.PipelineErrorTypeDeprecation,
Message: "Secrets are deprecated, use environment with from_secret",
Message: "Usage of `secrets` is deprecated, use `environment` with `from_secret`",
Data: errors.DeprecationErrorData{
File: config.File,
Field: fmt.Sprintf("steps.%s.secrets", container.Name),
Expand Down Expand Up @@ -328,7 +328,7 @@ func (l *Linter) lintBadHabits(config *WorkflowConfig) (err error) {
if field != "" {
err = multierr.Append(err, &errorTypes.PipelineError{
Type: errorTypes.PipelineErrorTypeBadHabit,
Message: "Please set an event filter for all steps or the whole workflow on all items of the when block",
Message: "Set an event filter for all steps or the entire workflow on all items of the `when` block",
Data: errors.BadHabitErrorData{
File: config.File,
Field: field,
Expand Down
32 changes: 16 additions & 16 deletions pipeline/frontend/yaml/linter/linter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,68 +114,68 @@ func TestLintErrors(t *testing.T) {
}{
{
from: "",
want: "Invalid or missing steps section",
want: "Invalid or missing `steps` section",
},
{
from: "steps: { build: { image: '' } }",
want: "Invalid or missing image",
},
{
from: "steps: { build: { image: golang, privileged: true } }",
want: "Insufficient privileges to use privileged mode",
want: "Insufficient trust level to use `privileged` mode",
},
{
from: "steps: { build: { image: golang, dns: [ 8.8.8.8 ] } }",
want: "Insufficient privileges to use custom dns",
want: "Insufficient trust level to use custom `dns`",
},

{
from: "steps: { build: { image: golang, dns_search: [ example.com ] } }",
want: "Insufficient privileges to use dns_search",
want: "Insufficient trust level to use `dns_search`",
},
{
from: "steps: { build: { image: golang, devices: [ '/dev/tty0:/dev/tty0' ] } }",
want: "Insufficient privileges to use devices",
want: "Insufficient trust level to use `devices`",
},
{
from: "steps: { build: { image: golang, extra_hosts: [ 'somehost:162.242.195.82' ] } }",
want: "Insufficient privileges to use extra_hosts",
want: "Insufficient trust level to use `extra_hosts`",
},
{
from: "steps: { build: { image: golang, network_mode: host } }",
want: "Insufficient privileges to use network_mode",
want: "Insufficient trust level to use `network_mode`",
},
{
from: "steps: { build: { image: golang, volumes: [ '/opt/data:/var/lib/mysql' ] } }",
want: "Insufficient privileges to use volumes",
want: "Insufficient trust level to use `volumes`",
},
{
from: "steps: { build: { image: golang, network_mode: 'container:name' } }",
want: "Insufficient privileges to use network_mode",
want: "Insufficient trust level to use `network_mode`",
},
{
from: "steps: { build: { image: golang, settings: { test: 'true' }, commands: [ 'echo ja', 'echo nein' ] } }",
want: "Cannot configure both commands and settings",
want: "Cannot configure both `commands` and `settings`",
},
{
from: "steps: { build: { image: golang, settings: { test: 'true' }, entrypoint: [ '/bin/fish' ] } }",
want: "Cannot configure both entrypoint and settings",
want: "Cannot configure both `entrypoint` and `settings`",
},
{
from: "steps: { build: { image: golang, settings: { test: 'true' }, environment: { 'TEST': 'true' } } }",
want: "Should not configure both environment and settings",
want: "Should not configure both `environment` and `settings`",
},
{
from: "{pipeline: { build: { image: golang, settings: { test: 'true' } } }, when: { branch: main, event: push } }",
want: "Additional property pipeline is not allowed",
},
{
from: "{steps: { build: { image: plugins/docker, settings: { test: 'true' } } }, when: { branch: main, event: push } } }",
want: "The formerly privileged plugin 'plugins/docker' is no longer privileged by default, if required, add it to WOODPECKER_PLUGINS_PRIVILEGED",
want: "The formerly privileged plugin `plugins/docker` is no longer privileged by default, if required, add it to `WOODPECKER_PLUGINS_PRIVILEGED`",
},
{
from: "{steps: { build: { image: golang, settings: { test: 'true' } } }, when: { branch: main, event: push }, clone: { git: { image: some-other/plugin-git:v1.1.0 } } }",
want: "Specified clone image does not match allow list, netrc will not be injected",
want: "Specified clone image does not match allow list, netrc is not injected",
},
}

Expand Down Expand Up @@ -209,11 +209,11 @@ func TestBadHabits(t *testing.T) {
}{
{
from: "steps: { build: { image: golang } }",
want: "Please set an event filter for all steps or the whole workflow on all items of the when block",
want: "Set an event filter for all steps or the entire workflow on all items of the `when` block",
},
{
from: "when: [{branch: xyz}, {event: push}]\nsteps: { build: { image: golang } }",
want: "Please set an event filter for all steps or the whole workflow on all items of the when block",
want: "Set an event filter for all steps or the entire workflow on all items of the `when` block",
},
}

Expand Down
2 changes: 2 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
"@vueuse/core": "^11.0.0",
"ansi_up": "^6.0.2",
"dayjs": "^1.11.12",
"dompurify": "^3.2.0",
"fuse.js": "^7.0.0",
"js-base64": "^3.7.7",
"lodash": "^4.17.21",
"marked": "^15.0.0",
"node-emoji": "^2.1.3",
"pinia": "^2.2.1",
"prismjs": "^1.29.0",
Expand Down
18 changes: 18 additions & 0 deletions web/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions web/src/components/atomic/DocsLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
:href="`${docsUrl}`"
:title="$t('documentation_for', { topic })"
target="_blank"
class="text-wp-link-100 hover:text-wp-link-200 cursor-pointer mt-1"
class="text-wp-link-100 hover:text-wp-link-200 cursor-pointer"
>
<Icon name="question" class="!w-4 !h-4" />
<Icon name="question" class="!w-5 !h-5" />
</a>
</template>

Expand Down
18 changes: 18 additions & 0 deletions web/src/components/atomic/RenderMarkdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<template>
<span v-html="contentHTML" />
</template>

<script setup lang="ts">
import DOMPurify from 'dompurify';
import { marked } from 'marked';
import { computed } from 'vue';
const props = defineProps<{
content: string;
}>();
const contentHTML = computed<string>(() => {
const dirtyHTML = marked.parse(props.content);
return DOMPurify.sanitize(dirtyHTML as string, { USE_PROFILES: { html: true } });
});
</script>
4 changes: 2 additions & 2 deletions web/src/components/repo/settings/GeneralTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
</template>
<template #description>
<i18n-t keypath="repo.settings.general.pipeline_path.desc" tag="p" class="text-sm text-wp-text-alt-100">
<span class="code-box-inline px-1">{{ $t('repo.settings.general.pipeline_path.desc_path_example') }}</span>
<span class="code-box-inline">{{ $t('repo.settings.general.pipeline_path.desc_path_example') }}</span>
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
<span class="code-box-inline px-1">/</span>
<span class="code-box-inline">/</span>
</i18n-t>
</template>
</InputField>
Expand Down
5 changes: 3 additions & 2 deletions web/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ body,
white-space: pre-wrap;
}

.code-box-inline {
@apply bg-wp-code-200 rounded-md text-wp-code-text-100;
.code-box-inline,
code:not(pre > code) {
@apply bg-wp-code-200 rounded-md text-wp-code-text-100 px-1 py-px;
}
Loading

0 comments on commit a1193f0

Please sign in to comment.