chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
merge_group:
|
||||
workflow_dispatch:
|
||||
|
||||
# Cancel superseded PR runs; let push/merge_group runs complete.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Default to no permissions; grant minimally at the job level.
|
||||
permissions:
|
||||
actions: none
|
||||
attestations: none
|
||||
checks: none
|
||||
contents: none
|
||||
deployments: none
|
||||
discussions: none
|
||||
id-token: none
|
||||
issues: none
|
||||
models: none
|
||||
packages: none
|
||||
pages: none
|
||||
pull-requests: none
|
||||
repository-projects: none
|
||||
security-events: none
|
||||
statuses: none
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0
|
||||
|
||||
- name: Lint markdown
|
||||
run: mise run lint:md
|
||||
|
||||
- name: Validate manifests and skills
|
||||
run: mise run lint:manifests
|
||||
|
||||
- name: Security scan (gitleaks)
|
||||
run: mise run security
|
||||
@@ -0,0 +1,64 @@
|
||||
name: CodeQL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
schedule:
|
||||
# Weekly on Wednesday at 14:23 UTC (arbitrary off-peak time)
|
||||
- cron: "23 14 * * 3"
|
||||
workflow_dispatch: {}
|
||||
|
||||
# Default to no permissions; grant minimally at the job level.
|
||||
permissions:
|
||||
actions: none
|
||||
attestations: none
|
||||
checks: none
|
||||
contents: none
|
||||
deployments: none
|
||||
discussions: none
|
||||
id-token: none
|
||||
issues: none
|
||||
models: none
|
||||
packages: none
|
||||
pages: none
|
||||
pull-requests: none
|
||||
repository-projects: none
|
||||
security-events: none
|
||||
statuses: none
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
security-events: write # upload SARIF to code scanning
|
||||
packages: read # fetch internal CodeQL packs (no-op for public repos)
|
||||
actions: read
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Scans workflow YAML for GitHub Actions security pitfalls
|
||||
# (script injection, untrusted checkout patterns, over-broad tokens).
|
||||
- language: actions
|
||||
build-mode: none
|
||||
# Scans Python helpers (e.g. tools/validate.py).
|
||||
- language: python
|
||||
build-mode: none
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@5e7a52feb2a3dfb87f88be2af33b9e2275f48de6 # v4.32.2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@5e7a52feb2a3dfb87f88be2af33b9e2275f48de6 # v4.32.2
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Dependency Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# Default to no permissions; grant minimally at the job level.
|
||||
permissions:
|
||||
actions: none
|
||||
attestations: none
|
||||
checks: none
|
||||
contents: none
|
||||
deployments: none
|
||||
discussions: none
|
||||
id-token: none
|
||||
issues: none
|
||||
models: none
|
||||
packages: none
|
||||
pages: none
|
||||
pull-requests: none
|
||||
repository-projects: none
|
||||
security-events: none
|
||||
statuses: none
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
|
||||
with:
|
||||
# Shared Amazon OSPO dependency-review config used across awslabs/aws repos.
|
||||
config-file: amazon-ospo/dependency-review-config/default/dependency-review-config.yml@8e4c9fdde54d2b7c6a3a28b97eddd26c4cd90a66 # main
|
||||
@@ -0,0 +1,124 @@
|
||||
---
|
||||
# Prevents unintentional merges beyond what branch rulesets can express.
|
||||
#
|
||||
# Two independent gates:
|
||||
# 1. Label gate — a PR labelled with `do-not-merge` (or `vars.DO_NOT_MERGE_LABEL`)
|
||||
# is blocked until the label is removed.
|
||||
# 2. Global gate — the `HALT_MERGES` repo variable blocks all merges when set:
|
||||
# HALT_MERGES=0 → gate disabled (default)
|
||||
# HALT_MERGES=<N> → only PR #N is allowed to merge
|
||||
# HALT_MERGES=-1 → all merges blocked
|
||||
#
|
||||
# Both gates are cheap escape hatches for incident response; they are not a
|
||||
# replacement for branch protection rules.
|
||||
name: Merge Prevention
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
- edited
|
||||
- labeled
|
||||
- unlabeled
|
||||
merge_group:
|
||||
types:
|
||||
- checks_requested
|
||||
|
||||
# Cancel superseded PR runs; never cancel merge_group runs (they gate a queued merge).
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Default to no permissions; grant minimally at the job level.
|
||||
permissions:
|
||||
actions: none
|
||||
attestations: none
|
||||
checks: none
|
||||
contents: none
|
||||
deployments: none
|
||||
discussions: none
|
||||
id-token: none
|
||||
issues: none
|
||||
models: none
|
||||
packages: none
|
||||
pages: none
|
||||
pull-requests: none
|
||||
repository-projects: none
|
||||
security-events: none
|
||||
statuses: none
|
||||
|
||||
env:
|
||||
DO_NOT_MERGE_LABEL: ${{ vars.DO_NOT_MERGE_LABEL || 'do-not-merge' }}
|
||||
HALT_MERGES: ${{ vars.HALT_MERGES || '0' }}
|
||||
|
||||
jobs:
|
||||
get-pr-info:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
outputs:
|
||||
pr_number: ${{ steps.get-pr.outputs.pr-number }}
|
||||
pr_labels: ${{ steps.get-pr.outputs.pr-labels }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
PR_LABELS_JSON: ${{ toJson(github.event.pull_request.labels.*.name) }}
|
||||
steps:
|
||||
- name: Get PR info
|
||||
id: get-pr
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "merge_group" ]; then
|
||||
PR_NUMBER=$(echo "${{ github.ref }}" | grep -oP '(?<=/pr-)\d+' || echo "")
|
||||
PR_LABELS=$(gh api "repos/${{ github.repository }}/pulls/$PR_NUMBER" | jq -c '[.labels[].name] // []')
|
||||
else
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
PR_LABELS=$(echo "$PR_LABELS_JSON" | jq -c '.')
|
||||
fi
|
||||
|
||||
echo "pr-number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
|
||||
echo "pr-labels=$PR_LABELS" >> "$GITHUB_OUTPUT"
|
||||
|
||||
check-halt-merges:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
needs: get-pr-info
|
||||
if: always()
|
||||
steps:
|
||||
- name: Enforce HALT_MERGES gate
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.get-pr-info.outputs.pr_number }}
|
||||
run: |
|
||||
# Default to 0 (allow all) if not set
|
||||
if [ -z "$HALT_MERGES" ]; then
|
||||
HALT_MERGES=0
|
||||
fi
|
||||
|
||||
if [ "$HALT_MERGES" = "0" ]; then
|
||||
echo "::debug::All merges allowed (HALT_MERGES=0)"
|
||||
exit 0
|
||||
elif [ "$HALT_MERGES" = "$PR_NUMBER" ]; then
|
||||
echo "::debug::PR #$PR_NUMBER is explicitly allowed"
|
||||
exit 0
|
||||
else
|
||||
if [ "$HALT_MERGES" -lt 0 ] 2>/dev/null; then
|
||||
echo "::error::All merges are blocked (HALT_MERGES=$HALT_MERGES)"
|
||||
else
|
||||
echo "::warning::Only PR #$HALT_MERGES is allowed to merge"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
check-do-not-merge-label:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
needs: get-pr-info
|
||||
if: always()
|
||||
steps:
|
||||
- name: Block when PR has the "${{ env.DO_NOT_MERGE_LABEL }}" label
|
||||
if: contains(needs.get-pr-info.outputs.pr_labels, env.DO_NOT_MERGE_LABEL)
|
||||
run: |
|
||||
echo "::error::The label \"${{ env.DO_NOT_MERGE_LABEL }}\" is used to prevent merging."
|
||||
exit 1
|
||||
@@ -0,0 +1,52 @@
|
||||
name: PR Lint
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, synchronize, reopened]
|
||||
branches: [main]
|
||||
|
||||
# Key on PR number because pull_request_target's github.ref is the base branch.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# Default to no permissions; grant minimally at the job level.
|
||||
permissions:
|
||||
actions: none
|
||||
attestations: none
|
||||
checks: none
|
||||
contents: none
|
||||
deployments: none
|
||||
discussions: none
|
||||
id-token: none
|
||||
issues: none
|
||||
models: none
|
||||
packages: none
|
||||
pages: none
|
||||
pull-requests: none
|
||||
repository-projects: none
|
||||
security-events: none
|
||||
statuses: none
|
||||
|
||||
jobs:
|
||||
pr-title:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
pull-requests: read
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
types: |
|
||||
feat
|
||||
fix
|
||||
docs
|
||||
style
|
||||
refactor
|
||||
perf
|
||||
test
|
||||
chore
|
||||
ci
|
||||
requireScope: false
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Scorecard Analysis
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
schedule:
|
||||
# Weekly on Mondays at 11:00 UTC (03:00 Pacific)
|
||||
- cron: "0 11 * * 1"
|
||||
workflow_dispatch: {}
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
name: Scorecard analysis
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write # upload SARIF to code scanning
|
||||
id-token: write # publish results to OSSF
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run analysis
|
||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
||||
with:
|
||||
results_file: scorecard-results.sarif
|
||||
results_format: sarif
|
||||
# Scorecard team runs a weekly scan of public GitHub repos
|
||||
# (https://github.com/ossf/scorecard#public-data). Setting
|
||||
# `publish_results: true` lets the OSSF reuse our workflow output
|
||||
# instead of scanning us independently.
|
||||
publish_results: true
|
||||
|
||||
- name: Upload SARIF artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: SARIF file
|
||||
path: scorecard-results.sarif
|
||||
retention-days: 14
|
||||
|
||||
- name: Upload to code scanning
|
||||
uses: github/codeql-action/upload-sarif@5e7a52feb2a3dfb87f88be2af33b9e2275f48de6 # v4.32.2
|
||||
with:
|
||||
sarif_file: scorecard-results.sarif
|
||||
@@ -0,0 +1,111 @@
|
||||
name: Skill Frontmatter Check
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize]
|
||||
branches: [main]
|
||||
paths:
|
||||
- "**/SKILL.md"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
check-frontmatter:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Check SKILL.md frontmatter via API
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
|
||||
with:
|
||||
retries: 3
|
||||
script: |
|
||||
const DISALLOWED_FIELDS = new Set(['stages']);
|
||||
const DISALLOWED_PREFIXES = ['owner_'];
|
||||
|
||||
const files = await github.paginate(
|
||||
github.rest.pulls.listFiles,
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number,
|
||||
per_page: 100
|
||||
}
|
||||
);
|
||||
|
||||
const skillFiles = files
|
||||
.filter(f => f.filename.endsWith('SKILL.md') && f.status !== 'removed');
|
||||
|
||||
if (skillFiles.length === 0) {
|
||||
core.info('No SKILL.md files changed.');
|
||||
return;
|
||||
}
|
||||
|
||||
const violations = [];
|
||||
|
||||
for (const file of skillFiles) {
|
||||
const { data } = await github.rest.repos.getContent({
|
||||
owner: context.payload.pull_request.head.repo.owner.login,
|
||||
repo: context.payload.pull_request.head.repo.name,
|
||||
path: file.filename,
|
||||
ref: context.payload.pull_request.head.sha
|
||||
});
|
||||
|
||||
const content = Buffer.from(data.content, 'base64').toString('utf-8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
if (lines[0] !== '---') continue;
|
||||
|
||||
const disallowed = [];
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
if (lines[i] === '---') break;
|
||||
const match = lines[i].match(/^([a-zA-Z_][a-zA-Z0-9_-]*):/);
|
||||
if (match && (DISALLOWED_FIELDS.has(match[1]) || DISALLOWED_PREFIXES.some(p => match[1].startsWith(p)))) {
|
||||
disallowed.push(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (disallowed.length > 0) {
|
||||
violations.push({ path: file.filename, fields: disallowed });
|
||||
}
|
||||
}
|
||||
|
||||
if (violations.length === 0) {
|
||||
core.info('All SKILL.md files pass frontmatter check.');
|
||||
return;
|
||||
}
|
||||
|
||||
const list = violations
|
||||
.map(v => `- \`${v.path}\`: ${v.fields.map(f => '`' + f + '`').join(', ')}`)
|
||||
.join('\n');
|
||||
|
||||
const body = [
|
||||
'⚠️ **PR closed automatically.**',
|
||||
'',
|
||||
'SKILL.md frontmatter contains fields that are not permitted in this public repository:',
|
||||
'',
|
||||
list,
|
||||
'',
|
||||
'Please remove these fields and reopen the PR.'
|
||||
].join('\n');
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
body
|
||||
});
|
||||
|
||||
await github.rest.pulls.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number,
|
||||
state: 'closed'
|
||||
});
|
||||
|
||||
core.setFailed('SKILL.md files contained disallowed frontmatter fields.');
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Stale
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
# Default to no permissions; grant minimally at the job level.
|
||||
permissions:
|
||||
actions: none
|
||||
attestations: none
|
||||
checks: none
|
||||
contents: none
|
||||
deployments: none
|
||||
discussions: none
|
||||
id-token: none
|
||||
issues: none
|
||||
models: none
|
||||
packages: none
|
||||
pages: none
|
||||
pull-requests: none
|
||||
repository-projects: none
|
||||
security-events: none
|
||||
statuses: none
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
|
||||
with:
|
||||
stale-pr-message: "This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs."
|
||||
stale-issue-message: "This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs."
|
||||
days-before-pr-stale: 14
|
||||
days-before-pr-close: 7
|
||||
days-before-issue-stale: 60
|
||||
days-before-issue-close: 14
|
||||
stale-pr-label: stale
|
||||
stale-issue-label: stale
|
||||
Reference in New Issue
Block a user