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
67 changes: 67 additions & 0 deletions .github/workflows/publish-production.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Publish to marketplace (production)

# Publishes the template packages to the PRODUCTION marketplace
# (https://cloud.objectos.ai). Target URL is hard-coded; the credential is the
# production-only secret `OS_CLOUD_API_KEY_PRODUCTION` (set once at the ORG
# level), fully separate from the staging key.
#
# MANUAL ONLY — there is intentionally no `release`/`push` trigger. Production
# is a deliberate promotion step: cut to staging first, verify, then dispatch
# this.
on:
workflow_dispatch:
inputs:
templates:
description: 'Comma-separated package names to publish (empty = all)'
required: false
default: ''
dry_run:
description: 'Dry-run (print payloads, no HTTP)'
type: boolean
default: false

concurrency:
group: publish-marketplace-production
cancel-in-progress: false

permissions:
contents: read

jobs:
publish:
name: Build + publish templates → production
runs-on: ubuntu-latest
# Only run on the canonical repo, never on forks.
if: github.repository == 'objectstack-ai/templates'
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10

- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build all templates
run: pnpm -r build

- name: Resolve template filter
id: filter
run: echo "filter=${{ inputs.templates }}" >> "$GITHUB_OUTPUT"

- name: Publish to PRODUCTION marketplace
env:
OS_CLOUD_URL: https://cloud.objectos.ai
# Distinct, production-only key — set once as an ORG-level secret,
# fully separate from the staging key. A staging dispatch can never
# carry the prod credential and vice-versa.
OS_CLOUD_API_KEY: ${{ secrets.OS_CLOUD_API_KEY_PRODUCTION }}
PUBLISH_TEMPLATES: ${{ steps.filter.outputs.filter }}
DRY_RUN: ${{ inputs.dry_run && '1' || '' }}
run: node scripts/publish-template.mjs
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
name: Publish to marketplace
name: Publish to marketplace (staging)

# Trigger ONLY on explicit intent — never on plain push to main.
# Publishes the template packages to the STAGING marketplace
# (https://cloud.objectos.app). Target URL is hard-coded — there is no
# ambiguous `vars.OS_CLOUD_URL`, so a dispatch can never accidentally hit
# production. The credential is the staging-only secret
# `OS_CLOUD_API_KEY_STAGING` (set once at the ORG level — no per-repo config).
#
# 1. Manual dispatch — from Actions UI, with optional template filter +
# dry-run toggle. Use this for normal releases.
# 2. GitHub Release published — tag like `todo-v0.2.0` or a multi-template
# release. The workflow filters via the tag name (if it looks like
# `<template>-v<semver>`) or just publishes whatever has a bumped
# version (the cloud side is idempotent — 409 on unchanged versions).
#
# Auto-publish on every push to main was intentionally removed: most pushes
# touch unrelated code, versions don't bump in lockstep with commits, and a
# bad publish lands on every user instantly.
# Triggers:
# 1. Manual dispatch — normal releases, with optional template filter + dry-run.
# 2. GitHub Release published — auto-publish to staging (cloud is idempotent —
# 409 on unchanged versions). Production is a separate, manual-only workflow.
on:
workflow_dispatch:
inputs:
Expand All @@ -27,20 +25,17 @@ on:
types: [published]

concurrency:
group: publish-marketplace
# Don't cancel in-flight publishes — they're idempotent but a partial run
# is preferable to a cancelled one (you'd retry the next dispatch anyway).
group: publish-marketplace-staging
cancel-in-progress: false

permissions:
contents: read

jobs:
publish:
name: Build + publish templates
name: Build + publish templates → staging
runs-on: ubuntu-latest
# Only run on the canonical repo, never on forks (where
# OS_CLOUD_API_KEY is unavailable). Adjust to your org slug.
# Only run on the canonical repo, never on forks.
if: github.repository == 'objectstack-ai/templates'
steps:
- uses: actions/checkout@v4
Expand All @@ -63,8 +58,8 @@ jobs:
- name: Resolve template filter from release tag
id: filter
# If the tag looks like `<name>-v<semver>` (e.g. todo-v0.2.0), narrow
# the publish to just that template. Otherwise leave the filter
# empty so all templates are evaluated (each one is idempotent).
# the publish to just that template. Otherwise leave the filter empty
# so all templates are evaluated (each one is idempotent).
run: |
FILTER="${{ inputs.templates }}"
if [ -z "$FILTER" ] && [ "${{ github.event_name }}" = "release" ]; then
Expand All @@ -76,10 +71,13 @@ jobs:
fi
echo "filter=$FILTER" >> "$GITHUB_OUTPUT"

- name: Publish to marketplace
- name: Publish to STAGING marketplace
env:
OS_CLOUD_URL: ${{ vars.OS_CLOUD_URL }}
OS_CLOUD_API_KEY: ${{ secrets.OS_CLOUD_API_KEY }}
OS_CLOUD_URL: https://cloud.objectos.app
# Distinct, staging-only key — set once as an ORG-level secret so
# every repo's staging publish shares it (no per-repo / per-environment
# setup). Keeps staging credentials fully separate from production.
OS_CLOUD_API_KEY: ${{ secrets.OS_CLOUD_API_KEY_STAGING }}
PUBLISH_TEMPLATES: ${{ steps.filter.outputs.filter }}
DRY_RUN: ${{ inputs.dry_run && '1' || '' }}
run: node scripts/publish-template.mjs
Loading