Skip to content
Merged
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
23 changes: 19 additions & 4 deletions .github/workflows/publish-on-merge.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# On merge to main, publish any new/changed submission: release the bundle on
# pilot-protocol/catalog and open a catalogue.json PR on TeoSlayer/pilotprotocol.
# On merge to main, publish any new/changed POINTER submission (pre-built,
# publisher-signed .bundle): release the bundle on pilot-protocol/catalog and
# open a catalogue.json PR on pilot-protocol/pilotprotocol. RICH submissions
# (backend/methods spec, no .bundle) are built+signed by the publisher's own
# key via pilot-app submit / publish-api and are skipped here with a notice.
#
# Requires repo secret CATALOG_PUBLISH_TOKEN — a PAT or GitHub App token with:
# - contents:write on pilot-protocol/catalog (create releases)
# - contents:write + pull_requests:write on TeoSlayer/pilotprotocol (catalogue PR)
# - contents:write + pull_requests:write on pilot-protocol/pilotprotocol (catalogue PR)
# Without it, this job no-ops with a clear message (submissions still validate on PR).
name: publish-on-merge

Expand All @@ -12,6 +15,12 @@ on:
branches: [main]
paths:
- "submissions/**"
workflow_dispatch:
inputs:
submission:
description: "submissions/<id> directory to (re)publish, e.g. submissions/io.pilot.tldr"
required: true
type: string

jobs:
publish:
Expand Down Expand Up @@ -47,7 +56,13 @@ jobs:
CATALOG_SIGN_KEY: ${{ secrets.CATALOG_SIGN_KEY }}
run: |
set -euo pipefail
changed="$(git diff --name-only HEAD~1 HEAD -- 'submissions/**/submission.json' || true)"
# Manual (re)publish of a single submission via workflow_dispatch.
if [ -n "${{ github.event.inputs.submission || '' }}" ]; then
scripts/publish-submission.sh "${{ github.event.inputs.submission }}"
exit 0
fi
# --diff-filter=d: a DELETED submission.json has nothing to publish.
changed="$(git diff --diff-filter=d --name-only HEAD~1 HEAD -- 'submissions/**/submission.json' || true)"
[ -n "$changed" ] || { echo "no submission.json changed"; exit 0; }
for sub in $changed; do
scripts/publish-submission.sh "$(dirname "$sub")"
Expand Down
2 changes: 1 addition & 1 deletion cmd/pilot-app/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ func cmdSubmit(args []string) {

fmt.Printf("\nTo publish via the single central repo, run `pilot-app submit -C %s --prepare <app-template-fork>`.\n", *dir)
fmt.Printf("\nDirect path (org maintainers only):\n── Step 1: release on pilot-protocol/catalog ──\n%s\n", relCmd)
fmt.Printf("\n── Step 2: add to catalogue/catalogue.json on TeoSlayer/pilotprotocol@main (PR) ──\n%s\n", catEntry)
fmt.Printf("\n── Step 2: add to catalogue/catalogue.json on pilot-protocol/pilotprotocol@main (PR) ──\n%s\n", catEntry)
}

// writeEnrichedMetadata reads the project's metadata.json (from init), fills the
Expand Down
2 changes: 1 addition & 1 deletion internal/scaffold/templates/Makefile.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ publish-help: package
@echo "Publish $(APP_ID) v$(VERSION):"
@echo " 1. gh release create {{.Namespace}}-v$(VERSION) -R pilot-protocol/catalog \\"
@echo " $(TARBALL) -t \"$(APP_ID) v$(VERSION)\" -n \"...\""
@echo " 2. add this entry to catalogue/catalogue.json on TeoSlayer/pilotprotocol (via PR):"
@echo " 2. add this entry to catalogue/catalogue.json on pilot-protocol/pilotprotocol (via PR):"
@echo " {\"id\":\"$(APP_ID)\",\"version\":\"$(VERSION)\","
@echo " \"description\":\"{{.Description}}\","
@echo " \"bundle_url\":\"https://github.com/pilot-protocol/catalog/releases/download/{{.Namespace}}-v$(VERSION)/$(TARBALL)\","
Expand Down
38 changes: 31 additions & 7 deletions scripts/publish-submission.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,40 @@
# publish-on-merge.yml with GH_TOKEN = CATALOG_PUBLISH_TOKEN. Idempotent on tag.
set -euo pipefail

DIR="$1"
DIR="${1%/}"
META="$DIR/submission.json"
test -f "$META" || { echo "no submission.json in $DIR"; exit 1; }

ID="$(jq -r .id "$META")"
VERSION="$(jq -r .version "$META")"
NS="$(jq -r .namespace "$META")"
DESC="$(jq -r .description "$META")"
# jq -r prints the literal string "null" for a missing field, which used to
# flow straight into paths/tags ("missing bundle submissions/<id>/null").
# Extract with `// empty` and validate every required field loudly instead.
ID="$(jq -r '.id // empty' "$META")"
VERSION="$(jq -r '.version // empty' "$META")"
DESC="$(jq -r '.description // empty' "$META")"
[ -n "$ID" ] || { echo "ERROR: $META has no .id"; exit 1; }
[ -n "$VERSION" ] || { echo "ERROR: $META ($ID) has no .version"; exit 1; }

# Two submission shapes exist (docs/APP-PUBLISHING-SPEC.md):
# POINTER — carries a pre-built, publisher-signed .bundle (+ .bundle_sha256 +
# .namespace). That is what this script releases.
# RICH — carries only the backend/methods spec; its bundles are built and
# signed with the PUBLISHER's own key (`pilot-app submit` /
# publish-api). CI holds no publisher key, so this pointer
# publisher cannot build or sign them — skip gracefully.
PRIMARY_FILE="$(jq -r '.bundle // empty' "$META")"
if [ -z "$PRIMARY_FILE" ]; then
if jq -e '(.backend | type == "object") and ((.methods // []) | length > 0)' "$META" >/dev/null; then
echo "==> $ID v$VERSION is a RICH submission (no pre-built .bundle pointer) — it is built+signed by the publisher's own key via the pilot-app submit / publish-api path, not by this pointer publisher. Skipping."
exit 0
fi
echo "ERROR: $META ($ID v$VERSION) has neither a .bundle pointer nor a rich backend/methods spec — nothing to publish"
exit 1
fi

NS="$(jq -r '.namespace // empty' "$META")"
SHA="$(jq -r '.bundle_sha256 // empty' "$META")"
[ -n "$NS" ] || { echo "ERROR: $META ($ID v$VERSION) declares .bundle but no .namespace (needed for the release tag)"; exit 1; }
[ -n "$SHA" ] || { echo "ERROR: $META ($ID v$VERSION) declares .bundle but no .bundle_sha256"; exit 1; }

CATALOG_REPO="pilot-protocol/catalog"
PLATFORM_REPO="pilot-protocol/pilotprotocol"
Expand All @@ -22,8 +48,6 @@ REL_BASE="https://github.com/${CATALOG_REPO}/releases/download/${TAG}"
# The linux/amd64 primary backs the top-level bundle_url (what pre-v3 clients
# fetch). Newer submissions also carry a .bundles map of every platform's
# {file,sha256}; older ones have only .bundle/.bundle_sha256 (single platform).
PRIMARY_FILE="$(jq -r .bundle "$META")"
SHA="$(jq -r .bundle_sha256 "$META")"
BUNDLE_URL="${REL_BASE}/${PRIMARY_FILE}"

# Every asset to release: the .bundles map's files, or just .bundle when absent.
Expand Down
Loading