diff --git a/.github/workflows/starter.yml b/.github/workflows/starter.yml index 3947bca..289d944 100644 --- a/.github/workflows/starter.yml +++ b/.github/workflows/starter.yml @@ -138,7 +138,7 @@ jobs: call-update-catalog-workflow: needs: call-assign-from-json-workflow if: needs.call-assign-from-json-workflow.outputs.update_catalog == 'true' && github.ref_name == 'main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') - uses: Keyfactor/actions/.github/workflows/update-catalog.yml@v5 + uses: Keyfactor/actions/.github/workflows/update-catalog.yml@port-update-catalog-to-v5 secrets: token: ${{ secrets.token }} diff --git a/.github/workflows/update-catalog.yml b/.github/workflows/update-catalog.yml index 9b58aae..cacee15 100644 --- a/.github/workflows/update-catalog.yml +++ b/.github/workflows/update-catalog.yml @@ -1,41 +1,227 @@ -name: Update Keyfactor Integrations Catalog Entry -on: - workflow_call: - secrets: - token: - description: 'Secret token from caller workflow to access SDK repo' - required: true - -jobs: - update-catalog-entry: - runs-on: ubuntu-latest - - steps: - - name: Checkout project repo - uses: keyfactor/checkout@v4 - - - name: Checkout catalog repo - uses: keyfactor/checkout@v4 - with: - token: ${{ secrets.token }} - path: './catalog-temp/' - repository: 'Keyfactor/integrations-catalog' # Change back to integrations-catalog after testing - - - uses: Keyfactor/jinja2-action@v1.2.0-multiple-data-files - with: - template: ./catalog-temp/_integration.md.tpl - output_file: ${{ format('./catalog-temp/_integrations/{0}.md', github.event.repository.name) }} - data_file: integration-manifest.json - variables: | - repository= ${{ format('https://github.com/{0}', github.repository) }} - env: - GITHUB_TOKEN: ${{ secrets.token }} - - - uses: Keyfactor/add-and-commit@v9.1.4 - with: - author_name: 'Keyfactor' - author_email: 'keyfactor@keyfactor.github.io' - branch: 'main' - message: ${{ format('Added the manifest for {0}', github.event.repository.name) }} - add: ${{ format('_integrations/{0}.md --force', github.event.repository.name) }} - cwd: './catalog-temp/' +# ============================================================================ +# PROPOSED replacement for: Keyfactor/actions/.github/workflows/update-catalog.yml +# +# Changes from current version: +# 1. Adds validation gate (repo name filter, manifest field checks, visibility) +# 2. Replaces direct-to-main commit with PR-based flow +# 3. Handles private repos as "Coming Soon" entries (link_github=false) +# 4. Labels PRs by type: catalog-add, catalog-update, coming-soon +# ============================================================================ + +name: Update Keyfactor Integrations Catalog Entry +on: + workflow_call: + secrets: + token: + description: 'Secret token from caller workflow to access catalog repo' + required: true + +jobs: + update-catalog-entry: + runs-on: ubuntu-latest + + steps: + - name: Checkout project repo + uses: keyfactor/checkout@v4 + + - name: Checkout catalog repo + uses: keyfactor/checkout@v4 + with: + token: ${{ secrets.token }} + path: './catalog-temp/' + repository: 'Keyfactor/integrations-catalog' + + # ---------------------------------------------------------------- + # STEP 1: Validation Gate + # ---------------------------------------------------------------- + - name: Validate entry + id: validate + env: + GH_TOKEN: ${{ secrets.token }} + REPO_NAME: ${{ github.event.repository.name }} + REPO_FULL: ${{ github.repository }} + run: | + echo "### Validating: $REPO_NAME" >> "$GITHUB_STEP_SUMMARY" + + # --- Failsafe: reject -dev, -test, -staging, -poc repo names --- + # NOTE: This is a failsafe only. Developers are responsible for setting + # update_catalog=false in their manifest for non-production repos. + if echo "$REPO_NAME" | grep -qE -- '-(dev|test|staging|poc)$'; then + echo "::error::Repository name '$REPO_NAME' matches a non-production pattern (-dev, -test, -staging, -poc). Set update_catalog=false in your integration-manifest.json for non-production repos." + echo "rejected=true" >> "$GITHUB_OUTPUT" + echo "reject_reason=Repo name matches non-production pattern. Developers: set \`update_catalog=false\` in your manifest." >> "$GITHUB_OUTPUT" + exit 1 + fi + + # --- Validate required manifest fields --- + if [ ! -f "integration-manifest.json" ]; then + echo "::error::integration-manifest.json not found in repository root." + exit 1 + fi + + NAME=$(jq -r '.name // empty' integration-manifest.json) + TYPE=$(jq -r '.integration_type // empty' integration-manifest.json) + DESC=$(jq -r '.description // empty' integration-manifest.json) + # NOTE: don't use jq's // "true" default — that operator triggers on + # both null AND false, which would silently flip link_github: false + # to "true". Use an explicit null check instead. + LINK_GITHUB=$(jq -r 'if .link_github == null then "true" else .link_github end' integration-manifest.json | tr '[:upper:]' '[:lower:]') + + ERRORS="" + if [ -z "$NAME" ]; then + ERRORS="$ERRORS\n- Missing required field: \`name\`" + fi + if [ -z "$TYPE" ]; then + ERRORS="$ERRORS\n- Missing required field: \`integration_type\`" + fi + if [ -z "$DESC" ]; then + ERRORS="$ERRORS\n- Missing required field: \`description\`" + fi + + # Validate integration_type against allowed values + ALLOWED_TYPES="orchestrator windows-orchestrator iot-orchestrator ca-gateway anyca-plugin caplugin dns-plugin pam approval-handler orchestrator-registration metadata registration-handler alert-handler api-client terraform" + if [ -n "$TYPE" ]; then + if ! echo "$ALLOWED_TYPES" | grep -qw "$TYPE"; then + ERRORS="$ERRORS\n- Invalid \`integration_type\`: \`$TYPE\`. Allowed: $ALLOWED_TYPES" + fi + fi + + if [ -n "$ERRORS" ]; then + printf "::error::Manifest validation failed:%b\n" "$ERRORS" + exit 1 + fi + + # --- Check repo visibility --- + IS_PRIVATE=$(gh api "repos/$REPO_FULL" --jq '.private') + + if [ "$IS_PRIVATE" = "true" ] && [ "$LINK_GITHUB" = "true" ]; then + echo "::error::Repository '$REPO_FULL' is private but link_github=true. This would create a broken link on the public catalog. Set link_github=false in your manifest to create a 'Coming Soon' entry, or make the repo public first." + exit 1 + fi + + # Determine entry type for labeling + if [ "$IS_PRIVATE" = "true" ] && [ "$LINK_GITHUB" = "false" ]; then + echo "entry_type=coming-soon" >> "$GITHUB_OUTPUT" + echo "Entry type: Coming Soon (private repo)" >> "$GITHUB_STEP_SUMMARY" + elif [ -f "./catalog-temp/_integrations/$REPO_NAME.md" ]; then + echo "entry_type=catalog-update" >> "$GITHUB_OUTPUT" + echo "Entry type: Update (existing entry)" >> "$GITHUB_STEP_SUMMARY" + else + echo "entry_type=catalog-add" >> "$GITHUB_OUTPUT" + echo "Entry type: Add (new entry)" >> "$GITHUB_STEP_SUMMARY" + fi + + echo "link_github=$LINK_GITHUB" >> "$GITHUB_OUTPUT" + echo "Validation passed" >> "$GITHUB_STEP_SUMMARY" + + # ---------------------------------------------------------------- + # STEP 2: Render Template + # ---------------------------------------------------------------- + - uses: Keyfactor/jinja2-action@v1.2.0-multiple-data-files + with: + template: ./catalog-temp/_integration.md.tpl + output_file: ${{ format('./catalog-temp/_integrations/{0}.md', github.event.repository.name) }} + data_file: integration-manifest.json + variables: | + repository= ${{ format('https://github.com/{0}', github.repository) }} + env: + GITHUB_TOKEN: ${{ secrets.token }} + + # ---------------------------------------------------------------- + # STEP 3: Create PR (instead of direct commit) + # ---------------------------------------------------------------- + - name: Create or update PR + env: + GH_TOKEN: ${{ secrets.token }} + REPO_NAME: ${{ github.event.repository.name }} + ENTRY_TYPE: ${{ steps.validate.outputs.entry_type }} + working-directory: './catalog-temp/' + run: | + BRANCH="catalog-update/$REPO_NAME" + + git config user.name "Keyfactor" + git config user.email "keyfactor@keyfactor.github.io" + + # Check if branch already exists on remote + if git ls-remote --heads origin "$BRANCH" | grep -q "$BRANCH"; then + git fetch origin "$BRANCH" + git checkout "$BRANCH" + git reset --hard origin/main + else + git checkout -b "$BRANCH" + fi + + # Stage the rendered file + git add "_integrations/$REPO_NAME.md" --force + + # Check if there are actual changes + if git diff --cached --quiet; then + echo "No changes detected — catalog entry is already up to date." + echo "### No changes" >> "$GITHUB_STEP_SUMMARY" + echo "Catalog entry for \`$REPO_NAME\` is already current." >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + + # Determine commit message + case "$ENTRY_TYPE" in + catalog-add) + COMMIT_MSG="Add catalog entry for $REPO_NAME" + PR_TITLE="Add integration: $REPO_NAME" + ;; + catalog-update) + COMMIT_MSG="Update catalog entry for $REPO_NAME" + PR_TITLE="Update integration: $REPO_NAME" + ;; + coming-soon) + COMMIT_MSG="Add Coming Soon catalog entry for $REPO_NAME" + PR_TITLE="Coming Soon: $REPO_NAME" + ;; + esac + + git commit -m "$COMMIT_MSG" + git push origin "$BRANCH" --force + + # Ensure the three labels this workflow uses exist (idempotent — gh + # label create exits 1 if the label is already present, which we + # swallow). Without this, gh pr create --label hard-fails when the + # label is missing in the catalog repo. + gh label create catalog-add --color "2ECC71" --description "New integration entry added to the catalog" 2>/dev/null || true + gh label create catalog-update --color "1A73E8" --description "Existing integration entry updated" 2>/dev/null || true + gh label create coming-soon --color "F39C12" --description "Private repo published as a Coming Soon entry" 2>/dev/null || true + + # Build PR body + PR_BODY=$(cat </dev/null || true) + + if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then + echo "PR #$EXISTING_PR already exists for branch $BRANCH — updated with force push." + echo "### Updated existing PR #$EXISTING_PR" >> "$GITHUB_STEP_SUMMARY" + else + gh pr create \ + --title "$PR_TITLE" \ + --body "$PR_BODY" \ + --label "$ENTRY_TYPE" \ + --base main \ + --head "$BRANCH" + + echo "### Created PR" >> "$GITHUB_STEP_SUMMARY" + echo "PR created for \`$REPO_NAME\` with label \`$ENTRY_TYPE\`" >> "$GITHUB_STEP_SUMMARY" + fi