Skip to content

Conversation

@TumbleOwlee
Copy link

This PR introduces support of the query parameters workflow and step to be used to select a specific workflow and step when generating the badges.

Closes #2373

Solution

The support of workflow and step selection is added to GetBadge(..) by extracting the query parameters and using WorkflowGetTree(pipeline) to select the workflow and the children of the selected workflow to select the specified step. Afterwards the name of the workflow and step are used as the badge title. In case only the workflow is specified the name of the workflow is used. If a step is also given, the badge title is given as <workflow>: <step>.

Additionally the UI is extended to include the input fields for the workflow and step.
Image

Drawback

The solution requires the call to _store.WorkflowGetTree(..) to retrieve the workflow and step information.

Testing

I updated the test case of the badge generation and also deployed the patched woodpecker server on my personal server.

Remaining Issues

  • Support longer titles in badges to support workflow and workflow-step badge titles.
    • I'm currently not sure how to tackle it.
image
  • Add missing translations
  • Add tests for workflow-step-badge generation.
    • Currently not sure how to mock test data for a pipeline.

@TumbleOwlee
Copy link
Author

Hi @qwerty287, if you can take a look any time, that would be perfect. If you by chance have an idea how to tackle the badge width issue and test issue, I'm open to any suggestions. Thanks in advance.

Copy link
Contributor

@qwerty287 qwerty287 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Looks quite good already, I added some comments to the code.

About the badge width, I also don't know… There are libraries to handle this, because you have to dynamically change the width. And this could be quite complex as you have to know the font etc… Not sure if there's a simple way in SVG to not set a fixed width. Maybe this is possible as well.

@TumbleOwlee
Copy link
Author

Thanks for your feedback. While keeping your feedback in mind, I prepared some changes. Additionally I tried to look up some sources to get an idea how the label width could be determined dynamically. Based on my research, I'm currently not sure if the possible result would justify the introduced complexity for simple badge generation because I not only have to calculate the width of the label based on the used font, but also have to keep the width in mind when modifying the coordinates in the SVG x-positions of the labels.

Because on that I want to propose another solution. Instead of calculating the width of the badges dynamically, we just provide a second wider badge option that's only used if workflow and step is selected. They look like the current badges. I just doubled the width of the label area. The drawback remains the fixed width of the normal and wider badges that would enforce a label length limit, the user has to keep in mind to prevent unreadable badge labels.

diff --git a/server/api/badge.go b/server/api/badge.go
index 72735ed7a..606b645b7 100644
--- a/server/api/badge.go
+++ b/server/api/badge.go
@@ -98,11 +98,12 @@ func GetBadge(c *gin.Context) {
                if !errors.Is(err, types.RecordNotExist) {
                        log.Warn().Err(err).Msg("could not get last pipeline for badge")
                }
-               pipeline = nil
+               c.String(http.StatusOK, badges.Generate("pipeline", nil, false))
+               return
        }

        name := "pipeline"
-       status := pipeline.Status
+       useLargeBadge := false

        // we serve an SVG, so set content type appropriately.
        c.Writer.Header().Set("Content-Type", "image/svg+xml")
@@ -111,32 +112,34 @@ func GetBadge(c *gin.Context) {
        workflowName := c.Query("workflow")
        if len(workflowName) != 0 {
                name = workflowName
-               status = model.StatusDeclined
-
                workflows, err := _store.WorkflowGetTree(pipeline)
                if err == nil {
                        for _, wf := range workflows {
                                if wf.Name == workflowName {
                                        stepName := c.Query("step")
                                        if len(stepName) == 0 {
-                                               status = wf.State
-                                       } else {
-                                               // If step is explicitly requested
-                                               name = name + ": " + stepName
-                                               for _, s := range wf.Children {
-                                                       if s.Name == stepName {
-                                                               status = s.State
-                                                               break
-                                                       }
+                                               c.String(http.StatusOK, badges.Generate(name, &wf.State, useLargeBadge))
+                                               return
+                                       }
+                                       // If step is explicitly requested
+                                       name = name + ": " + stepName
+                                       useLargeBadge = true
+                                       for _, s := range wf.Children {
+                                               if s.Name == stepName {
+                                                       c.String(http.StatusOK, badges.Generate(name, &s.State, useLargeBadge))
+                                                       return
                                                }
                                        }
                                        break
                                }
                        }
                }
+       } else if pipeline != nil {
+               c.String(http.StatusOK, badges.Generate(name, &pipeline.Status, useLargeBadge))
+               return
        }

-       c.String(http.StatusOK, badges.Generate(name, status))
+       c.String(http.StatusOK, badges.Generate(name, nil, useLargeBadge))
 }

 // GetCC
diff --git a/server/badges/badges.go b/server/badges/badges.go
index f64b2592d..86efddc93 100644
--- a/server/badges/badges.go
+++ b/server/badges/badges.go
@@ -28,6 +28,12 @@ var (
        badgeStarted = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="102" height="20" role="img" aria-label="pipeline: started"><title>pipeline: started</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="102" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="53" height="20" fill="#555"/><rect x="53" width="49" height="20" fill="#dfb317"/><rect width="102" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="275" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">pipeline</text><text x="275" y="140" transform="scale(.1)" fill="#fff" textLength="430">pipeline</text><text aria-hidden="true" x="765" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="390">started</text><text x="765" y="140" transform="scale(.1)" fill="#fff" textLength="390">started</text></g></svg>`
        badgeError   = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20" role="img" aria-label="pipeline: error"><title>pipeline: error</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="53" height="20" fill="#555"/><rect x="53" width="37" height="20" fill="#9f9f9f"/><rect width="90" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="275" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">pipeline</text><text x="275" y="140" transform="scale(.1)" fill="#fff" textLength="430">pipeline</text><text aria-hidden="true" x="705" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">error</text><text x="705" y="140" transform="scale(.1)" fill="#fff" textLength="270">error</text></g></svg>`
        badgeNone    = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20" role="img" aria-label="pipeline: none"><title>pipeline: none</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="53" height="20" fill="#555"/><rect x="53" width="37" height="20" fill="#9f9f9f"/><rect width="90" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="275" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">pipeline</text><text x="275" y="140" transform="scale(.1)" fill="#fff" textLength="430">pipeline</text><text aria-hidden="true" x="705" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">none</text><text x="705" y="140" transform="scale(.1)" fill="#fff" textLength="270">none</text></g></svg>`
+
+       largeBadgeSuccess = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="159" height="20" role="img" aria-label="pipeline: success"><title>pipeline: success</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="159" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="106" height="20" fill="#555"/><rect x="106" width="53" height="20" fill="#44cc11"/><rect width="212" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="560" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="860">pipeline</text><text x="560" y="140" transform="scale(.1)" fill="#fff" textLength="860">pipeline</text><text aria-hidden="true" x="1320" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">success</text><text x="1320" y="140" transform="scale(.1)" fill="#fff" textLength="430">success</text></g></svg>`
+       largeBadgeFailure = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="159" height="20" role="img" aria-label="pipeline: failure"><title>pipeline</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="159" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="106" height="20" fill="#555"/><rect x="106" width="53" height="20" fill="#e05d44"/><rect width="212" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="560" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="860">pipeline</text><text x="560" y="140" transform="scale(.1)" fill="#fff" textLength="860">pipeline</text><text aria-hidden="true" x="1320" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">failure</text><text x="1320" y="140" transform="scale(.1)" fill="#fff" textLength="350">failure</text></g></svg>`
+       largeBadgeStarted = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="159" height="20" role="img" aria-label="pipeline: started"><title>pipeline</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="159" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="106" height="20" fill="#555"/><rect x="106" width="53" height="20" fill="#dfb317"/><rect width="212" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="560" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="860">pipeline</text><text x="560" y="140" transform="scale(.1)" fill="#fff" textLength="860">pipeline</text><text aria-hidden="true" x="1320" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="390">started</text><text x="1320" y="140" transform="scale(.1)" fill="#fff" textLength="390">started</text></g></svg>`
+       largeBadgeError   = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="143" height="20" role="img" aria-label="pipeline: error"><title>pipeline</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="143" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="106" height="20" fill="#555"/><rect x="106" width="37" height="20" fill="#9f9f9f"/><rect width="212" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="560" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="860">pipeline</text><text x="560" y="140" transform="scale(.1)" fill="#fff" textLength="860">pipeline</text><text aria-hidden="true" x="1250" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">error</text><text x="1250" y="140" transform="scale(.1)" fill="#fff" textLength="270">error</text></g></svg>`
+       largeBadgeNone    = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="143" height="20" role="img" aria-label="pipeline: none"><title>pipeline</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="143" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="106" height="20" fill="#555"/><rect x="106" width="37" height="20" fill="#9f9f9f"/><rect width="212" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="560" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="860">pipeline</text><text x="560" y="140" transform="scale(.1)" fill="#fff" textLength="860">pipeline</text><text aria-hidden="true" x="1250" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">none</text><text x="1250" y="140" transform="scale(.1)" fill="#fff" textLength="270">none</text></g></svg>`
 )

 // Replace placeholder with specific name.
@@ -36,17 +42,40 @@ func SetBadgeName(badge, name string) string {
 }

 // Generate an SVG badge based on a pipeline.
-func Generate(name string, status model.StatusValue) string {
-       switch status {
-       case model.StatusSuccess:
-               return SetBadgeName(badgeSuccess, name)
-       case model.StatusFailure:
-               return SetBadgeName(badgeFailure, name)
-       case model.StatusError, model.StatusKilled:
-               return SetBadgeName(badgeError, name)
-       case model.StatusPending, model.StatusRunning:
-               return SetBadgeName(badgeStarted, name)
-       default:
-               return SetBadgeName(badgeNone, name)
+func Generate(name string, status *model.StatusValue, useLargeBadge bool) string {
+       if useLargeBadge {
+               if status == nil {
+                       return SetBadgeName(largeBadgeNone, name)
+               }
+
+               switch *status {
+               case model.StatusSuccess:
+                       return SetBadgeName(largeBadgeSuccess, name)
+               case model.StatusFailure:
+                       return SetBadgeName(largeBadgeFailure, name)
+               case model.StatusError, model.StatusKilled:
+                       return SetBadgeName(largeBadgeError, name)
+               case model.StatusPending, model.StatusRunning:
+                       return SetBadgeName(largeBadgeStarted, name)
+               default:
+                       return SetBadgeName(largeBadgeNone, name)
+               }
+       } else {
+               if status == nil {
+                       return SetBadgeName(badgeNone, name)
+               }
+
+               switch *status {
+               case model.StatusSuccess:
+                       return SetBadgeName(badgeSuccess, name)
+               case model.StatusFailure:
+                       return SetBadgeName(badgeFailure, name)
+               case model.StatusError, model.StatusKilled:
+                       return SetBadgeName(badgeError, name)
+               case model.StatusPending, model.StatusRunning:
+                       return SetBadgeName(badgeStarted, name)
+               default:
+                       return SetBadgeName(badgeNone, name)
+               }
        }
 }

Since Go doesn't support an equivalent of the ternary operator ?, I multiplied the switch-case. If any more idiomatic equivalent exists, please let me know.

With the diff above, the badge generation would produce these outputs:

  • No workflow, no step: status-badge
  • Workflow, no step: status-badge
  • Worknow and step: status-badge
  • Unknown workflow: status-badge

I can also increase/decrease the label width area if requested.

Would this be a good direction to follow or should I do more research into the dynamic label width calculation and work out a solution based on that?

@qwerty287
Copy link
Contributor

The width is still fixed in the large ones, right? Because then we might solve it for most labels, but as workflow and step names can be of any length, it's easy to have a bad one again. Dynamic width would be the best...

@TumbleOwlee
Copy link
Author

I hope this goes in the right direction. I reworked the badge generation. I added the necessary steps to generate the badges dynamically based on the required width for the labels. To retrieve the width for each glyph I used the DejaVuSans.ttf since it's used in the badge labels. I couldn't find an easier/cleaner way to do it, so if this should be changed, I will take additional time to research another option.

With this approach the labels can be as long as the user likes it to be. Here some examples:

  • Short label: status-badge
    • As you can see here, I enforced a minimal width of the label to prevent really short badges. In these cases the spacing of the characters will increase.
  • Long label: status-badge
  • Very long label: status-badge
    • Should a maximum limit be applied? Currently only a minimum is enforced.

Please keep in mind, these examples are given by my own server and unfortunately I don't have any workflows in my repository with such long names. Therefore the status is none for these examples.

@qwerty287
Copy link
Contributor

Nice - without looking at the code this looks good already. I don't think we need a minimum at all for the width.

Maybe you could also check out some lib like https://github.com/narqo/go-badge to generate this? So we don't have to reinvent the wheel…

@qwerty287 qwerty287 added server enhancement improve existing features labels Jan 19, 2026
@TumbleOwlee
Copy link
Author

Maybe you got the notification for my last comment that I deleted. I was just dumb and messed it up.
As of now I added the use of narqo/go-badge for simple badge generation. I also updated my instance, so the badges in my last comment that link to my instance are generated by the library now. This also allowed the removal of a lot of my previously added generation code.

@TumbleOwlee
Copy link
Author

I applied the requested changes. If anything else comes to mind, please let me know and I try to provide the changes as quick as possible.

@qwerty287 qwerty287 marked this pull request as ready for review January 22, 2026 07:18
@qwerty287
Copy link
Contributor

Thanks! Looks good to me so far, haven't tested yet though.

@woodpecker-ci/maintainers somebody else maybe should take a look as well

@woodpecker-bot
Copy link
Contributor

woodpecker-bot commented Jan 22, 2026

Surge PR preview deployment succeeded. View it at https://woodpecker-ci-woodpecker-pr-5977.surge.sh

@qwerty287
Copy link
Contributor

qwerty287 commented Jan 22, 2026

@TumbleOwlee can you check out the image lib? It seems to have a security issue: https://ci.woodpecker-ci.org/repos/3780/pipeline/30874/22#L41

Maybe the badge library is not that nice, it's unmaintained for 3 years… :(

And the linter fails as well: https://ci.woodpecker-ci.org/repos/3780/pipeline/30874/35

@TumbleOwlee
Copy link
Author

@TumbleOwlee can you check out the image lib? It seems to have a security issue: https://ci.woodpecker-ci.org/repos/3780/pipeline/30874/22#L41

I can take a look later.

Maybe the badge library is not that nice, it's unmaintained for 3 years… :(

Maybe use https://github.com/essentialkaos/go-badge instead? It looks like it would require to add the .ttf file again. Last release is from last year. But as of now it only has 22 stars, so no idea what the future will bring regarding maintenance.

Or I try to get in contact with the maintainer of narqo/go-badge.

And the linter fails as well: https://ci.woodpecker-ci.org/repos/3780/pipeline/30874/35

I will fix it later when I'm back home. I should have made sure to run it myself to prevent it...

@xoxys
Copy link
Member

xoxys commented Jan 22, 2026

The implementation in github.com/narqo/go-badge is quite basic, I would vote to copy it into wp code.

TumbleOwlee and others added 3 commits January 22, 2026 20:53
This commit copies the sources of `narqo/go-badge` (MIT) into woodpecker.
Since the library was previously not updated for 3 years, additional minor
changes had to be introduced to fullfill all linting requirements. This
includes an update of the additional dependencies to prevent security issues.
@TumbleOwlee
Copy link
Author

@qwerty287 @xoxys As I had to update the dependencies to fix the security issues and you both voted for the copy of the narqo/go-badge library into the woodpecker repository, I just did that. Since the library hasn't received an update in the past 3 years, I had to apply minor changes to prevent security issues reported by trivy and linting issues:

  • Updated the dependencies
  • Minor rework of the initialization of the badge drawer to prevent issues with lint because of use of panic
  • Minor update of the tests to use assert.NoError and assert.Equal

If I did anything wrong or if I should apply additional changes, please let me know.

I already run make lint and trivy fs --skip-dirs web/,docs/ . locally with no remaining errors.

@xoxys
Copy link
Member

xoxys commented Jan 23, 2026

@TumbleOwlee thanks for the effort. Can you check the remaining linter issued? server/badges/fonts/Bitstream Vera License.txt might need an exclusion rule.

@TumbleOwlee
Copy link
Author

@TumbleOwlee thanks for the effort. Can you check the remaining linter issued? server/badges/fonts/Bitstream Vera License.txt might need an exclusion rule.

Sure, I will take a look this evening.

@TumbleOwlee
Copy link
Author

@xoxys I fixed the lint issues.

The only issue remaining is https://ci.woodpecker-ci.org/repos/3780/pipeline/30889/23 and I'm not sure how to fix it correctly. I've never really worked with npm, pnpm and so on. So I just tried it with pnpm update lodash, but that did nothing. So I just removed the lines mentioning lodash@4.17.21. Running make build-docs, make generate-docs worked without any issue.
Additionally, I checked if I just have to remove the file and run pnpm install --no-frozen-lockfile. That worked but the content of the pnpm-lock.yaml changed a lot. So not sure if that's the correct use.

Is the dependency lodash@4.17.21 not used as of now and can simply be removed in the lock file?

@TumbleOwlee
Copy link
Author

I will have to take another look at the spellcheck. Not sure why nothing was reported locally by running the same command as specified in the pipeline file, but now the same command fails in the pipleine...

@qwerty287
Copy link
Contributor

qwerty287 commented Jan 23, 2026

This securitycheck is not related I think. Just ignore it.

But could you remove the unused stuff from the go-badge lib? And adding a license header to these files would be good as well, check that from the go-badge license. Similar to https://github.com/woodpecker-ci/woodpecker/blob/main/server/services/utils/hostmatcher/hostmatcher.go#L1

Sorry if I'm opening this again, but would it be possible to not embed the full font? And instead somehow only store the char widths? Because we shouldn't need most of the font itself - just the chars. (Wait with changing that till we finished discussion on the point)

@qwerty287
Copy link
Contributor

I will have to take another look at the spellcheck. Not sure why nothing was reported locally by running the same command as specified in the pipeline file, but now the same command fails in the pipleine...

That's from the file name "Bitstream Vera License"

@TumbleOwlee
Copy link
Author

TumbleOwlee commented Jan 23, 2026

But could you remove the unused stuff from the go-badge lib? And adding a license header to these files would be good as well, check that from the go-badge license. Similar to https://github.com/woodpecker-ci/woodpecker/blob/main/server/services/utils/hostmatcher/hostmatcher.go#L1

Would it be something like

// Copyright 2023 The narqo/go-badge Authors. All rights reserved.
// SPDX-License-Identifier: MIT.

and for the files with additional changes something like this?

// Copyright 2026 Woodpecker Authors
// Copyright 2023 The narqo/go-badge Authors. All rights reserved.
// SPDX-License-Identifier: MIT.

See: https://github.com/woodpecker-ci/woodpecker/blob/main/server/ccmenu/cc.go

Sorry if I'm opening this again, but would it be possible to not embed the full font? And instead somehow only store the char widths? Because we shouldn't need most of the font itself - just the chars. (Wait with changing that till we finished discussion on the point)

That's from the file name "Bitstream Vera License"

Sure, I'll wait before tackling this. If the plan will be to extract the glyph advance width and just store the map of glyphs and their advance width, the issue with the filename is also automatically solved. I'll wait until hearing back from you. Thanks in advance for all the time you take.

Edit: Just out of curiosity, I already looked into the freetype library to extract the necessary advance widths of all glyphs of any .ttf file, and to generate the necessary go map. So if you decide to embed the advance widths directly instead of the .ttf file, the preparation is in place and I could work on that.
Besides that and independent of your decision, maybe we have to switch the font to support more glyphs? Currently the badge generation will fail to calculate the correct width if many cyrillic characters are used, since the vera.ttf doesn't provide them (only 268 glyphs). I'm not sure if we have to keep that in mind or not. The DejaVuSans.ttf, I used previously, contains 6188 glyphs in total.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement improve existing features server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Per-workflow badge

4 participants