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
70 changes: 70 additions & 0 deletions .github/workflows/publish-plugin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
name: "Publish plugin to grc.store"

# Manual-only on purpose. The first successful publish TOFU-pins the signer
# identity of ossf/github-repo to THIS workflow file's path, so it has to be a
# deliberate act and every later publish must come from this same file. Add a
# `release: {types: [published]}` trigger once the manual run is proven.
on:
workflow_dispatch:
inputs:
ref:
description: "Tag or ref to build and publish (e.g. v0.28.0)"
required: true
hub-url:
description: "grc.store hub to publish to"
required: true
default: "https://hub.preview.grc.store"

permissions:
contents: read

jobs:
publish:
name: publish
runs-on: ubuntu-latest
permissions:
contents: read
# Mints two distinct OIDC tokens: the hub push token (audience =
# ci_audience) and the Sigstore signing identity (audience = sigstore,
# requested by pvtr itself). The hub must list this repo as a
# ci_publishers trusted publisher for the ossf namespace.
id-token: write
env:
# TODO: must be a pvtr release that contains `pvtr publish` — see REV-345.
PVTR_VERSION: REPLACE_WITH_PVTR_RELEASE_TAG
Comment thread
eddie-knight marked this conversation as resolved.
PVTR_HUB_URL: ${{ inputs.hub-url }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.ref }}
persist-credentials: false
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod
- name: install pvtr
run: go install github.com/privateerproj/privateer@${PVTR_VERSION}
- uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3
with:
version: "~> v2"
args: build --clean
- name: publish
# pvtr reads the plugin's coordinate, license and catalog linkage from
# the built binary itself.
run: |
set -euo pipefail
audience=$(curl -fsSL "${PVTR_HUB_URL}/.well-known/grc-store-configuration" | jq -r '.ci_audience')
if [ -z "$audience" ] || [ "$audience" = "null" ]; then
echo "hub ${PVTR_HUB_URL} advertises no ci_audience" >&2
exit 1
fi
PVTR_TOKEN=$(curl -fsSL \
-H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${audience}" | jq -r '.value')
if [ -z "$PVTR_TOKEN" ] || [ "$PVTR_TOKEN" = "null" ]; then
echo "failed to mint a hub OIDC token for audience ${audience}" >&2
exit 1
fi
echo "::add-mask::${PVTR_TOKEN}"
export PVTR_TOKEN
privateer publish
66 changes: 41 additions & 25 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ var (
"repo",
"token",
}
// The vendored Baseline YAML carries no metadata.author.id, so declare the owner.
catalogNamespaces = map[string]string{
"osps-baseline": "ossf",
"osps-baseline-2025-10": "ossf",
"osps-baseline-2026-02": "ossf",
}
//go:embed data/catalogs
files embed.FS
dataDir = filepath.Join("data", "catalogs")
Expand All @@ -43,10 +49,36 @@ func main() {
Version = fmt.Sprintf("%s-%s", Version, VersionPostfix)
}

orchestrator := pluginkit.EvaluationOrchestrator{
PluginName: PluginName,
PluginVersion: Version,
PluginUri: "https://github.com/ossf/pvtr-github-repo-scanner",
orchestrator, err := newOrchestrator()
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(shared.InternalError)
}

runCmd := command.NewPluginCommands(
PluginName,
Version,
GitCommitHash,
BuiltAt,
orchestrator,
)
Comment thread
eddie-knight marked this conversation as resolved.

err = runCmd.Execute()
if err != nil {
os.Exit(shared.InternalError)
}
}

// newOrchestrator builds the orchestrator. Publisher, License and
// catalogNamespaces are unused at run time; `pvtr publish` reads them.
func newOrchestrator() (*pluginkit.EvaluationOrchestrator, error) {
orchestrator := &pluginkit.EvaluationOrchestrator{
PluginName: PluginName,
PluginVersion: Version,
PluginUri: "https://github.com/ossf/pvtr-github-repo-scanner",
Publisher: "ossf",
License: "Apache-2.0",
CatalogNamespaces: catalogNamespaces,
}
orchestrator.AddLoader(data.Loader)
orchestrator.AddTargetBuilder(func(c *config.Config) gemara.Resource {
Expand All @@ -59,30 +91,14 @@ func main() {
}
})

err := orchestrator.AddReferenceCatalogs(dataDir, files)
if err != nil {
fmt.Printf("Error loading catalog: %v\n", err)
os.Exit(shared.InternalError)
if err := orchestrator.AddReferenceCatalogs(dataDir, files); err != nil {
return nil, fmt.Errorf("error loading catalog: %w", err)
}

orchestrator.AddRequiredVars(RequiredVars)

err = pluginkit.AddEvaluationSuiteTypedForAllCatalogs(&orchestrator, nil, evaluation_plans.AllSteps())
if err != nil {
fmt.Printf("Error adding evaluation suites: %v\n", err)
os.Exit(shared.InternalError)
}

runCmd := command.NewPluginCommands(
PluginName,
Version,
VersionPostfix,
GitCommitHash,
&orchestrator,
)

err = runCmd.Execute()
if err != nil {
os.Exit(shared.InternalError)
if err := pluginkit.AddEvaluationSuiteTypedForAllCatalogs(orchestrator, nil, evaluation_plans.AllSteps()); err != nil {
return nil, fmt.Errorf("error adding evaluation suites: %w", err)
}
return orchestrator, nil
}
36 changes: 36 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"strings"
"testing"
)

// The publish metadata (Publisher, License, catalogNamespaces) is inert at run
// time, so nothing else in this repo notices when it is wrong or when a newly
// embedded catalog is missing a namespace — the release publish is the first
// thing that would fail. Assert the manifest `pvtr publish` reads instead.
func TestPublishManifest(t *testing.T) {
orchestrator, err := newOrchestrator()
if err != nil {
t.Fatalf("newOrchestrator: %v", err)
}

manifest, err := orchestrator.PublishManifest()
if err != nil {
t.Fatalf("PublishManifest: %v", err)
}
if manifest.Coordinate != "ossf/github-repo" {
t.Errorf("coordinate = %q, want ossf/github-repo", manifest.Coordinate)
}
if manifest.License != "Apache-2.0" {
t.Errorf("license = %q, want Apache-2.0", manifest.License)
}
if len(manifest.Evaluates) != len(catalogNamespaces) {
t.Fatalf("evaluated %d catalogs, want %d", len(manifest.Evaluates), len(catalogNamespaces))
}
for _, e := range manifest.Evaluates {
if !strings.HasPrefix(e.Catalog, "ossf/") {
t.Errorf("catalog %q is not namespaced under ossf", e.Catalog)
}
}
}