Merge pull request #44 from Snowflake-Labs/feature/aem-publish-pipeline #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Publish Skill to AEM | ||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| skill_name: | ||
| description: 'Skill folder name (e.g. good-morning)' | ||
| required: true | ||
| type: string | ||
| commit_sha: | ||
| description: 'Commit SHA to checkout' | ||
| required: true | ||
| type: string | ||
| pr_number: | ||
| description: 'PR number for commenting (optional, omit for merge runs)' | ||
| required: false | ||
| type: number | ||
| secrets: | ||
| AEM_USERNAME: | ||
| required: true | ||
| AEM_PASSWORD: | ||
| required: true | ||
| AEM_AUTHOR_URL: | ||
| required: true | ||
| env: | ||
| CF_BASE_PATH: /content/dam/snowflake-site/en/content-fragments/webinars/product-demo/manage-data-pipelines | ||
| CF_DEST_BASE: /content/dam/snowflake-site/en/content-fragments/skills-library | ||
| jobs: | ||
| publish_skill: | ||
| runs-on: ubuntu-latest | ||
| env: | ||
| AEM_URL: ${{ secrets.AEM_AUTHOR_URL }} | ||
| AEM_USERNAME: ${{ secrets.AEM_USERNAME }} | ||
| AEM_PASSWORD: ${{ secrets.AEM_PASSWORD }} | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ inputs.commit_sha }} | ||
| sparse-checkout: | | ||
| skills/${{ inputs.skill_name }} | ||
| scripts/aem-skills | ||
| sparse-checkout-cone-mode: true | ||
| - name: Setup Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.11' | ||
| - name: Install dependencies | ||
| run: pip install pyyaml | ||
| - name: Find SKILL.md | ||
| id: find_md | ||
| run: | | ||
| md_file="skills/${{ inputs.skill_name }}/SKILL.md" | ||
| if [ ! -f "$md_file" ]; then | ||
| echo "❌ SKILL.md not found at $md_file" | ||
| exit 1 | ||
| fi | ||
| echo "md_file=$md_file" >> $GITHUB_OUTPUT | ||
| echo "📄 Found: $md_file" | ||
| - name: Parse SKILL.md | ||
| run: | | ||
| python3 scripts/aem-skills/parse_skill.py \ | ||
| "${{ steps.find_md.outputs.md_file }}" \ | ||
| --output-json parsed_skill.json | ||
| echo "📄 Parsed skill:" | ||
| cat parsed_skill.json | ||
| - name: Prepare AEM payload | ||
| run: | | ||
| python3 scripts/aem-skills/prepare_skill_payload.py \ | ||
| parsed_skill.json \ | ||
| --output-json skill_payload.json | ||
| echo "📦 Payload prepared" | ||
| - name: Ensure skills-library folder exists | ||
| run: | | ||
| check=$(curl -s -o /dev/null -w "%{http_code}" \ | ||
| "${AEM_URL}${CF_DEST_BASE}.json" \ | ||
| -u "${AEM_USERNAME}:${AEM_PASSWORD}") | ||
| echo "skills-library folder check: $check" | ||
| if [ "$check" != "200" ]; then | ||
| echo "📁 Creating skills-library folder" | ||
| curl -s -X POST \ | ||
| "${AEM_URL}/api/assets/snowflake-site/en/content-fragments/*" \ | ||
| -u "${AEM_USERNAME}:${AEM_PASSWORD}" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"class":"assetFolder","properties":{"name":"skills-library","title":"skills-library"}}' | ||
| sleep 2 | ||
| fi | ||
| - name: Create or verify CF exists | ||
| id: cf_path | ||
| run: | | ||
| cf_path="${CF_DEST_BASE}/${{ inputs.skill_name }}" | ||
| echo "cf_path=$cf_path" >> $GITHUB_OUTPUT | ||
| check=$(curl -s -o /dev/null -w "%{http_code}" \ | ||
| "${AEM_URL}${cf_path}.json" \ | ||
| -u "${AEM_USERNAME}:${AEM_PASSWORD}") | ||
| echo "CF existence check: HTTP $check" | ||
| if [ "$check" = "200" ]; then | ||
| echo "✅ CF already exists, will update" | ||
| else | ||
| echo "📋 Creating new CF by copying base..." | ||
| response=$(curl -s -w "\n%{http_code}" -X POST \ | ||
| "${AEM_URL}${CF_BASE_PATH}" \ | ||
| -u "${AEM_USERNAME}:${AEM_PASSWORD}" \ | ||
| -H "Content-Type: application/x-www-form-urlencoded" \ | ||
| -d ":operation=copy" \ | ||
| -d ":dest=${cf_path}" \ | ||
| -d ":replace=true") | ||
| http_code=$(echo "$response" | tail -n1) | ||
| echo "Copy HTTP: $http_code" | ||
| if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then | ||
| echo "❌ Failed to create CF" | ||
| exit 1 | ||
| fi | ||
| echo "✅ CF created" | ||
| sleep 3 | ||
| fi | ||
| - name: Update CF with skill content | ||
| run: | | ||
| cf_path="${{ steps.cf_path.outputs.cf_path }}" | ||
| jq -j '.cf_payload' skill_payload.json > cf_payload.txt | ||
| csrf_token=$(curl -s -u "${AEM_USERNAME}:${AEM_PASSWORD}" \ | ||
| "${AEM_URL}/libs/granite/csrf/token.json" | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])") | ||
| echo "📝 Updating CF: $cf_path" | ||
| response=$(curl -s -w "\n%{http_code}" -X POST \ | ||
| "${AEM_URL}${cf_path}/jcr:content" \ | ||
| -u "${AEM_USERNAME}:${AEM_PASSWORD}" \ | ||
| -H "Content-Type: application/x-www-form-urlencoded" \ | ||
| -H "CSRF-Token: $csrf_token" \ | ||
| --data-binary @cf_payload.txt) | ||
| http_code=$(echo "$response" | tail -n1) | ||
| echo "Update HTTP: $http_code" | ||
| if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then | ||
| echo "❌ Failed to update CF" | ||
| echo "$response" | sed '$d' | ||
| exit 1 | ||
| fi | ||
| echo "✅ CF updated" | ||
| - name: Replicate CF to publish | ||
| run: | | ||
| cf_path="${{ steps.cf_path.outputs.cf_path }}" | ||
| echo "📢 Replicating CF: $cf_path" | ||
| curl -s -X POST \ | ||
| "${AEM_URL}/bin/replicate.json" \ | ||
| -u "${AEM_USERNAME}:${AEM_PASSWORD}" \ | ||
| -H "Content-Type: application/x-www-form-urlencoded" \ | ||
| -d "cmd=Activate" \ | ||
| -d "path=${cf_path}" | ||
| echo "✅ CF replicated" | ||
| - name: Comment PR with staging link | ||
| if: ${{ inputs.pr_number != 0 }} | ||
| uses: actions/github-script@v7 | ||
| env: | ||
| AEM_AUTHOR_URL: ${{ secrets.AEM_AUTHOR_URL }} | ||
| with: | ||
| script: | | ||
| const skillName = '${{ inputs.skill_name }}'; | ||
| const cfPath = '/content/dam/snowflake-site/en/content-fragments/skills-library/' + skillName; | ||
| const authorUrl = process.env.AEM_AUTHOR_URL; | ||
| const cfLink = `${authorUrl}/ui#/aem/editor.html${cfPath}`; | ||
| const body = `## ✅ Skill Staged to AEM | ||
| **Skill:** \`${skillName}\` | ||
| 👉 **AEM Staging CF:** [Open in AEM Author](${cfLink}) | ||
| > Content Fragment has been created/updated and replicated to the staging publish instance. | ||
| --- | ||
| *Generated by GitHub Actions*`; | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: ${{ inputs.pr_number }}, | ||
| body | ||
| }); | ||