Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ on:
type: boolean
required: false
default: false
allow-unsigned-in-dev:
description: |
Escape hatch: when true, allowing unsigned dev builds in untrusted contexts.
Defaults to false (untrusted dev builds hard-fail). See ci.yml for details.
type: boolean
required: false
default: false
signature-type:
description: Specify signature type to use when signing the plugin
type: string
Expand Down Expand Up @@ -795,6 +802,7 @@ jobs:
environment: ${{ inputs.environment }}

allow-unsigned: ${{ inputs.allow-unsigned }}
allow-unsigned-in-dev: ${{ inputs.allow-unsigned-in-dev }}
signature-type: ${{ inputs.signature-type }}

dist-artifacts-prefix: ${{ inputs.dist-artifacts-prefix }}
Expand Down
17 changes: 14 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@ on:
type: boolean
required: false
default: false
allow-unsigned-in-dev:
description: |
Escape hatch: when true, allow unsigned dev builds in untrusted contexts.
Defaults to false (untrusted dev builds hard-fail). PR/fork builds (env=`none`|`''`) keep the fallback either way.
type: boolean
Comment thread
manderson-dev marked this conversation as resolved.
required: false
default: false
signature-type:
description: Specify signature type to use when signing the plugin
type: string
Expand Down Expand Up @@ -386,7 +393,9 @@ jobs:
'dependabot[bot]',
'renovate-sh-app[bot]',
// Used by other shared workflows such as the version bump workflow
'grafana-plugins-platform-bot[bot]'
'grafana-plugins-platform-bot[bot]',
// Only the actor on post-merge pushes from the queue, which already required maintainer approval.
'github-merge-queue[bot]'
].map((s) => s.toLowerCase());

const isPR = context.eventName === 'pull_request';
Expand Down Expand Up @@ -545,6 +554,8 @@ jobs:
secrets: ${{ (fromJson(steps.workflow-context.outputs.result).isTrusted && inputs.backend-secrets != '') && inputs.backend-secrets || '' }}
build-target: ${{ inputs.backend-build-target }}

# Auto-allow-unsigned applies only to untrusted contexts AND envs where shipping unsigned is acceptable:
# `none`/`''` (CI-only) and, opt-in via `allow-unsigned-in-dev`, `dev`. Untrusted dev otherwise hard-fails here.
- name: Package universal ZIP
id: universal-zip
uses: grafana/plugin-ci-workflows/actions/internal/plugins/package@main
Expand All @@ -553,7 +564,7 @@ jobs:
dist-folder: ${{ inputs.plugin-directory }}/dist
output-folder: ${{ inputs.plugin-directory }}/dist-artifacts
access-policy-token: ${{ fromJson(steps.workflow-context.outputs.result).isTrusted && fromJSON(steps.get-secrets.outputs.secrets).SIGN_PLUGIN_ACCESS_POLICY_TOKEN || '' }}
allow-unsigned: ${{ ((inputs.environment == 'dev' || inputs.environment == '' || inputs.environment == 'none') && !(fromJson(steps.workflow-context.outputs.result).isTrusted)) || inputs.allow-unsigned }}
allow-unsigned: ${{ ((inputs.environment == '' || inputs.environment == 'none' || (inputs.environment == 'dev' && inputs.allow-unsigned-in-dev)) && !(fromJson(steps.workflow-context.outputs.result).isTrusted)) || inputs.allow-unsigned }}
signature-type: ${{ inputs.signature-type }}

- name: Package os/arch ZIPs
Expand All @@ -564,7 +575,7 @@ jobs:
dist-folder: ${{ inputs.plugin-directory }}/dist
output-folder: ${{ inputs.plugin-directory }}/dist-artifacts
access-policy-token: ${{ fromJson(steps.workflow-context.outputs.result).isTrusted && fromJSON(steps.get-secrets.outputs.secrets).SIGN_PLUGIN_ACCESS_POLICY_TOKEN || '' }}
allow-unsigned: ${{ ((inputs.environment == 'dev' || inputs.environment == '' || inputs.environment == 'none') && !(fromJson(steps.workflow-context.outputs.result).isTrusted)) || inputs.allow-unsigned }}
allow-unsigned: ${{ ((inputs.environment == '' || inputs.environment == 'none' || (inputs.environment == 'dev' && inputs.allow-unsigned-in-dev)) && !(fromJson(steps.workflow-context.outputs.result).isTrusted)) || inputs.allow-unsigned }}
signature-type: ${{ inputs.signature-type }}

- name: Trufflehog secrets scanning
Expand Down
2 changes: 2 additions & 0 deletions tests/act/internal/workflow/ci/ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ type WorkflowInputs struct {
RunTruffleHog *bool

AllowUnsigned *bool
AllowUnsignedInDev *bool
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Might makes sense to add a test case covering the new input.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

added a test!

Testing *bool
BackendBuildTarget *string

Expand Down Expand Up @@ -138,6 +139,7 @@ func SetCIInputs(dst *workflow.Job, inputs WorkflowInputs) {
workflow.SetJobInput(dst, "run-trufflehog", inputs.RunTruffleHog)

workflow.SetJobInput(dst, "allow-unsigned", inputs.AllowUnsigned)
workflow.SetJobInput(dst, "allow-unsigned-in-dev", inputs.AllowUnsignedInDev)
workflow.SetJobInput(dst, "testing", inputs.Testing)
workflow.SetJobInput(dst, "backend-build-target", inputs.BackendBuildTarget)

Expand Down
67 changes: 67 additions & 0 deletions tests/act/internal/workflow/ci/ci_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package ci

import (
"testing"

"github.com/grafana/plugin-ci-workflows/tests/act/internal/workflow"
"github.com/stretchr/testify/require"
)

// newJobWithEmptyInputs returns a fresh Job with an initialized With map.
// SetCIInputs writes into job.With, so callers must ensure it is non-nil.
func newJobWithEmptyInputs() *workflow.Job {
return &workflow.Job{With: map[string]any{}}
}

func TestSetCIInputs_AllowUnsignedInDev(t *testing.T) {
t.Parallel()

const inputKey = "allow-unsigned-in-dev"

t.Run("nil leaves input unset (workflow default applies)", func(t *testing.T) {
t.Parallel()

job := newJobWithEmptyInputs()
SetCIInputs(job, WorkflowInputs{})

require.NotContains(t, job.With, inputKey,
"unset AllowUnsignedInDev should not propagate %q so the workflow's default (false) applies", inputKey)
})

t.Run("true is forwarded as the escape-hatch opt-in", func(t *testing.T) {
t.Parallel()

job := newJobWithEmptyInputs()
SetCIInputs(job, WorkflowInputs{
AllowUnsignedInDev: workflow.Input(true),
})

require.Equal(t, true, job.With[inputKey],
"AllowUnsignedInDev=true must propagate as %q=true to opt back into the pre-v8 untrusted-dev fallback", inputKey)
})

t.Run("false is forwarded explicitly", func(t *testing.T) {
t.Parallel()

job := newJobWithEmptyInputs()
SetCIInputs(job, WorkflowInputs{
AllowUnsignedInDev: workflow.Input(false),
})

require.Equal(t, false, job.With[inputKey],
"AllowUnsignedInDev=false must propagate as %q=false to keep the hard-fail default", inputKey)
})

t.Run("does not collide with allow-unsigned", func(t *testing.T) {
t.Parallel()

job := newJobWithEmptyInputs()
SetCIInputs(job, WorkflowInputs{
AllowUnsigned: workflow.Input(true),
AllowUnsignedInDev: workflow.Input(false),
})

require.Equal(t, true, job.With["allow-unsigned"], "AllowUnsigned should map to %q", "allow-unsigned")
require.Equal(t, false, job.With[inputKey], "AllowUnsignedInDev should map to %q", inputKey)
})
}
Loading