diff --git a/pipeline/frontend/yaml/linter/linter.go b/pipeline/frontend/yaml/linter/linter.go index 71c4efe6f..105109a58 100644 --- a/pipeline/frontend/yaml/linter/linter.go +++ b/pipeline/frontend/yaml/linter/linter.go @@ -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 { @@ -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), ) } @@ -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) @@ -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 } @@ -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`") } } @@ -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), @@ -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, diff --git a/pipeline/frontend/yaml/linter/linter_test.go b/pipeline/frontend/yaml/linter/linter_test.go index 943cd5593..c826cf96d 100644 --- a/pipeline/frontend/yaml/linter/linter_test.go +++ b/pipeline/frontend/yaml/linter/linter_test.go @@ -114,7 +114,7 @@ func TestLintErrors(t *testing.T) { }{ { from: "", - want: "Invalid or missing steps section", + want: "Invalid or missing `steps` section", }, { from: "steps: { build: { image: '' } }", @@ -122,48 +122,48 @@ func TestLintErrors(t *testing.T) { }, { 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 } }", @@ -171,11 +171,11 @@ func TestLintErrors(t *testing.T) { }, { 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", }, } @@ -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", }, } diff --git a/web/package.json b/web/package.json index bcf0e9d18..d21c106ba 100644 --- a/web/package.json +++ b/web/package.json @@ -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", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index b07962677..6d9f8c7ff 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: dayjs: specifier: ^1.11.12 version: 1.11.13 + dompurify: + specifier: ^3.2.0 + version: 3.2.0 fuse.js: specifier: ^7.0.0 version: 7.0.0 @@ -38,6 +41,9 @@ importers: lodash: specifier: ^4.17.21 version: 4.17.21 + marked: + specifier: ^15.0.0 + version: 15.0.0 node-emoji: specifier: ^2.1.3 version: 2.1.3 @@ -1310,6 +1316,9 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} + dompurify@3.2.0: + resolution: {integrity: sha512-AMdOzK44oFWqHEi0wpOqix/fUNY707OmoeFDnbi3Q5I8uOpy21ufUA5cDJPr0bosxrflOVD/H2DMSvuGKJGfmQ==} + domutils@3.1.0: resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} @@ -1921,6 +1930,11 @@ packages: markdown-table@3.0.3: resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} + marked@15.0.0: + resolution: {integrity: sha512-0mouKmBROJv/WSHJBPZZyYofUgawMChnD5je/g+aOBXsHDjb/IsnTQj7mnhQZu+qPJmRQ0ecX3mLGEUm3BgwYA==} + engines: {node: '>= 18'} + hasBin: true + mdast-util-find-and-replace@3.0.1: resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} @@ -4055,6 +4069,8 @@ snapshots: dependencies: domelementtype: 2.3.0 + dompurify@3.2.0: {} + domutils@3.1.0: dependencies: dom-serializer: 2.0.0 @@ -4766,6 +4782,8 @@ snapshots: markdown-table@3.0.3: {} + marked@15.0.0: {} + mdast-util-find-and-replace@3.0.1: dependencies: '@types/mdast': 4.0.4 diff --git a/web/src/components/atomic/DocsLink.vue b/web/src/components/atomic/DocsLink.vue index ea2e9fa74..f19a7d7f4 100644 --- a/web/src/components/atomic/DocsLink.vue +++ b/web/src/components/atomic/DocsLink.vue @@ -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" > - + diff --git a/web/src/components/atomic/RenderMarkdown.vue b/web/src/components/atomic/RenderMarkdown.vue new file mode 100644 index 000000000..df3a60be6 --- /dev/null +++ b/web/src/components/atomic/RenderMarkdown.vue @@ -0,0 +1,18 @@ + + + diff --git a/web/src/components/repo/settings/GeneralTab.vue b/web/src/components/repo/settings/GeneralTab.vue index 5c3460eaa..ba07097a5 100644 --- a/web/src/components/repo/settings/GeneralTab.vue +++ b/web/src/components/repo/settings/GeneralTab.vue @@ -14,9 +14,9 @@ diff --git a/web/src/style.css b/web/src/style.css index 0855c2ef1..ab6d64be3 100644 --- a/web/src/style.css +++ b/web/src/style.css @@ -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; } diff --git a/web/src/views/repo/pipeline/PipelineErrors.vue b/web/src/views/repo/pipeline/PipelineErrors.vue index ddf11be90..c5cea0e04 100644 --- a/web/src/views/repo/pipeline/PipelineErrors.vue +++ b/web/src/views/repo/pipeline/PipelineErrors.vue @@ -1,37 +1,47 @@