Skip to content

Fix gallery proposal issue handoff #29

Fix gallery proposal issue handoff

Fix gallery proposal issue handoff #29

name: Audit gallery content
on:
pull_request:
branches: [main]
schedule:
- cron: '17 6 * * 1'
workflow_dispatch:
permissions:
contents: read
pull-requests: read
concurrency:
group: gallery-content-audit
cancel-in-progress: false
jobs:
validate:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Test gallery audit
run: npm run test:gallery-audit
- name: Test configured discovery sources
run: npm run gallery:audit:fixtures
- name: Build static gallery
run: npm run build
audit:
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
pull-requests: read
copilot-requests: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
with:
fetch-depth: 0
persist-credentials: false
- name: Verify main approval ruleset
env:
GH_TOKEN: ${{ github.token }}
run: |
protected=false
for ruleset_id in $(gh api "repos/$GITHUB_REPOSITORY/rulesets?includes_parents=true" --jq '.[].id'); do
if gh api "repos/$GITHUB_REPOSITORY/rulesets/$ruleset_id" | jq -e '
.target == "branch" and
.enforcement == "active" and
any(.conditions.ref_name.include[]?; . == "refs/heads/main" or . == "~DEFAULT_BRANCH") and
any(.rules[]?;
.type == "pull_request" and
.parameters.required_approving_review_count >= 1 and
.parameters.dismiss_stale_reviews_on_push == true and
.parameters.require_last_push_approval == true and
.parameters.required_review_thread_resolution == true
)
' > /dev/null; then
protected=true
break
fi
done
test "$protected" = "true" || {
echo "main must require an approving review, stale-review dismissal, approval after the latest push, and resolved review threads."
exit 1
}
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Test gallery audit
run: npm run test:gallery-audit
- name: Run deterministic audit and discovery
env:
GITHUB_TOKEN: ${{ github.token }}
run: npm run gallery:audit
- name: Install GitHub Copilot CLI
run: npm install --global @github/copilot@1.0.85
- name: Install and verify curator skills
shell: bash
env:
AGENT_KIT_SKILL_URL: https://raw.githubusercontent.com/AzureCosmosDB/CosmosDB-Agent-Kit/a7a7c2c23214edd36a06d13b674ed392b1fefae8/skills/cosmosdb-best-practices/SKILL.md
AGENT_KIT_SKILL_SHA256: c46a4a6ab5d2f46ba4ae06c5c178a8acf70578c7816815bcca47bcda5d3760d6
run: |
curl --fail --silent --show-error --location --proto '=https' --tlsv1.2 \
"$AGENT_KIT_SKILL_URL" --output "$RUNNER_TEMP/cosmosdb-best-practices.SKILL.md"
echo "$AGENT_KIT_SKILL_SHA256 $RUNNER_TEMP/cosmosdb-best-practices.SKILL.md" | sha256sum --check --strict
copilot skill add --project "$RUNNER_TEMP/cosmosdb-best-practices.SKILL.md"
copilot skill list --json > "$RUNNER_TEMP/copilot-skills.json"
node -e "const fs=require('fs');const value=fs.readFileSync(process.env.RUNNER_TEMP + '/copilot-skills.json','utf8');for(const name of ['cosmosdb-best-practices','humanizer'])if(!value.includes(name))throw new Error('Required Copilot skill unavailable: '+name)"
- name: Classify and promote high-confidence updates
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
node scripts/gallery-audit/index.mjs --classify-only --promote
- name: Validate proposed catalog
if: hashFiles('output/gallery-content-review/promotion-result.json') != ''
run: |
npm run test:gallery-audit
npm run build
- name: Detect actionable recommendations
id: actionable
shell: bash
run: |
if [ ! -f output/gallery-content-review/promotion-result.json ]; then
echo "available=false" >> "$GITHUB_OUTPUT"
exit 0
fi
count=$(node -p "const r=require('./output/gallery-content-review/promotion-result.json'); (r.additions?.length ?? 0) + (r.updates?.length ?? 0) + (r.retirements?.length ?? 0)")
test "$count" -gt 0 || { echo "available=false" >> "$GITHUB_OUTPUT"; exit 0; }
git diff --quiet -- static/templates.json static/retired-templates.json && {
echo "Actionable recommendations did not produce a catalog diff."
exit 1
}
echo "available=true" >> "$GITHUB_OUTPUT"
- name: Upload proposal files
if: steps.actionable.outputs.available == 'true'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: gallery-content-proposal-${{ github.run_id }}
path: output/gallery-content-review/promotion-summary.md
retention-days: 30
if-no-files-found: error
- name: Add summary
if: always()
run: |
test ! -f "output/gallery-content-review/audit-summary.md" || cat "output/gallery-content-review/audit-summary.md" >> "$GITHUB_STEP_SUMMARY"
test ! -f "output/gallery-content-review/promotion-summary.md" || cat "output/gallery-content-review/promotion-summary.md" >> "$GITHUB_STEP_SUMMARY"
- name: Upload review bundle
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: gallery-content-review-${{ github.run_id }}
path: output/gallery-content-review/
retention-days: 30
if-no-files-found: error