diff --git a/.github/workflows/workshop-playwright-qa.lock.yml b/.github/workflows/workshop-playwright-qa.lock.yml
index 99231804..c453bd21 100644
--- a/.github/workflows/workshop-playwright-qa.lock.yml
+++ b/.github/workflows/workshop-playwright-qa.lock.yml
@@ -1,4 +1,4 @@
-# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"1dc5bf35ba06eb283084a91122aae015241e5bdaef25d4ace4019671d1d3f2df","body_hash":"86513b24e16f0979ffb328c51172355c0c65fcefee687b48fc55d1db9c66a106","compiler_version":"v0.82.13","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.70"}}
+# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"86657a0e9726f2ea203023f0fe9526768f2259602ac48236b19f641b3324e37e","body_hash":"0da169cf49f1146846984102711c0fc5f82b01e8e7c003d1edeb4e0e892ae187","compiler_version":"v0.82.13","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.70"}}
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"a5d8b7df9d4f9137bedb3d074d0a0b005f1e1996","version":"v0.82.13"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35","digest":"sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35","digest":"sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35","digest":"sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35","digest":"sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}]}
# This file was automatically generated by gh-aw (v0.82.13). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
#
@@ -54,7 +54,7 @@
name: "Workshop Playwright QA"
on:
schedule:
- - cron: "10 7 * * *" # Friendly format: daily (scattered)
+ - cron: "40 19 * * *" # Friendly format: daily (scattered)
workflow_dispatch:
inputs:
aw_context:
@@ -247,21 +247,23 @@ jobs:
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_30eec136205a39fa_EOF'
+ cat << 'GH_AW_PROMPT_7e8d519f6c7e392b_EOF'
- GH_AW_PROMPT_30eec136205a39fa_EOF
+ GH_AW_PROMPT_7e8d519f6c7e392b_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/playwright_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_30eec136205a39fa_EOF'
+ cat << 'GH_AW_PROMPT_7e8d519f6c7e392b_EOF'
- Tools: add_comment(max:5), create_issue(max:10), missing_tool, missing_data, noop
+ Tools: add_comment(max:5), create_issue(max:10), upload_asset(max:30), missing_tool, missing_data, noop
+
+ upload_asset: provide a file path; returns a URL; assets are published after the workflow completes (safeoutputs).
- GH_AW_PROMPT_30eec136205a39fa_EOF
+ GH_AW_PROMPT_7e8d519f6c7e392b_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_30eec136205a39fa_EOF'
+ cat << 'GH_AW_PROMPT_7e8d519f6c7e392b_EOF'
The following GitHub context information is available for this workflow:
{{#if github.actor}}
@@ -290,12 +292,12 @@ jobs:
{{/if}}
- GH_AW_PROMPT_30eec136205a39fa_EOF
+ GH_AW_PROMPT_7e8d519f6c7e392b_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_30eec136205a39fa_EOF'
+ cat << 'GH_AW_PROMPT_7e8d519f6c7e392b_EOF'
{{#runtime-import .github/workflows/workshop-playwright-qa.md}}
- GH_AW_PROMPT_30eec136205a39fa_EOF
+ GH_AW_PROMPT_7e8d519f6c7e392b_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
@@ -386,9 +388,9 @@ jobs:
queue: max
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
- GH_AW_ASSETS_ALLOWED_EXTS: ""
- GH_AW_ASSETS_BRANCH: ""
- GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_ASSETS_ALLOWED_EXTS: ".png"
+ GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
+ GH_AW_ASSETS_MAX_SIZE_KB: 10240
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
GH_AW_WORKFLOW_ID_SANITIZED: workshopplaywrightqa
@@ -517,20 +519,24 @@ jobs:
- name: Download container images
run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.35@sha256:2202f63e8650b2b8b0d38033b44a05387b2b71ad3e690c4d23a34786f5462aed ghcr.io/github/gh-aw-firewall/api-proxy:0.27.35@sha256:755b79d0dfda82bd6b43a208d68666721e504110c5d342a4eeb199802644ff04 ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.35@sha256:fe83cd274636efa9de3f456e2b078fae137328b9bb6ee4986ae510acaef0cec5 ghcr.io/github/gh-aw-firewall/squid:0.27.35@sha256:f69282ec7b1326ba53891c399cf5b10475c0d3ccf4e1519b33d234a5427b57d3 ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32 ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3
- name: Generate Safe Outputs Config
+ env:
+ GITHUB_WORKFLOW: ${{ github.workflow }}
run: |
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_ec1bdfdf65052bbd_EOF'
- {"add_comment":{"max":5},"create_issue":{"deduplicate_by_title":true,"expires":24,"labels":["bug","accessibility"],"max":10,"title_prefix":"[workshop-playwright-qa] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
- GH_AW_SAFE_OUTPUTS_CONFIG_ec1bdfdf65052bbd_EOF
+ mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs/assets"
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_e173516f7d8c408c_EOF'
+ {"add_comment":{"max":5},"create_issue":{"deduplicate_by_title":true,"expires":24,"labels":["bug","accessibility"],"max":10,"title_prefix":"[workshop-playwright-qa] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"upload_asset":{"allowed-exts":[".png"],"branch":"assets/${GITHUB_WORKFLOW}","max":30,"max-size":10240}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_e173516f7d8c408c_EOF
- name: Generate Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
{
"description_suffixes": {
"add_comment": " CONSTRAINTS: Maximum 5 comment(s) can be added. Supports reply_to_id for discussion threading.",
- "create_issue": " CONSTRAINTS: Maximum 10 issue(s) can be created. Title will be prefixed with \"[workshop-playwright-qa] \". Labels [\"bug\" \"accessibility\"] will be automatically added."
+ "create_issue": " CONSTRAINTS: Maximum 10 issue(s) can be created. Title will be prefixed with \"[workshop-playwright-qa] \". Labels [\"bug\" \"accessibility\"] will be automatically added.",
+ "upload_asset": " CONSTRAINTS: Maximum 30 asset(s) can be uploaded. Maximum file size: 10240KB. Allowed file extensions: [.png]."
},
"repo_params": {},
"dynamic_tools": []
@@ -668,6 +674,15 @@ jobs:
"maxLength": 1024
}
}
+ },
+ "upload_asset": {
+ "defaultMax": 10,
+ "fields": {
+ "path": {
+ "required": true,
+ "type": "string"
+ }
+ }
}
}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
@@ -680,6 +695,9 @@ jobs:
- name: Start MCP Gateway
id: start-mcp-gateway
env:
+ GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }}
+ GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }}
+ GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }}
GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }}
GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }}
@@ -834,6 +852,9 @@ jobs:
COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
COPILOT_GITHUB_TOKEN: ${{ github.token }}
COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_ASSETS_ALLOWED_EXTS: ".png"
+ GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
+ GH_AW_ASSETS_MAX_SIZE_KB: 10240
GH_AW_LLM_PROVIDER: github
GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}
GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }}
@@ -980,6 +1001,15 @@ jobs:
if [ ! -f /tmp/gh-aw/agent_output.json ]; then
echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
fi
+ # Upload safe-outputs assets for upload_assets job
+ - name: Upload Safe Outputs Assets
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: safe-outputs-assets
+ path: ${{ runner.temp }}/gh-aw/safeoutputs/assets/
+ retention-days: 1
+ if-no-files-found: ignore
- name: Upload agent artifacts
if: always()
continue-on-error: true
@@ -1012,6 +1042,7 @@ jobs:
- agent
- detection
- safe_outputs
+ - upload_assets
if: >
always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
needs.activation.outputs.oauth_token_check_failed == 'true' || needs.activation.outputs.stale_lock_file_failed == 'true' ||
@@ -1600,7 +1631,7 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,127.0.0.1,::1,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":5},\"create_issue\":{\"deduplicate_by_title\":true,\"expires\":24,\"labels\":[\"bug\",\"accessibility\"],\"max\":10,\"title_prefix\":\"[workshop-playwright-qa] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":5},\"create_issue\":{\"deduplicate_by_title\":true,\"expires\":24,\"labels\":[\"bug\",\"accessibility\"],\"max\":10,\"title_prefix\":\"[workshop-playwright-qa] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{},\"upload_asset\":{\"allowed-exts\":[\".png\"],\"branch\":\"assets/${{ github.workflow }}\",\"max\":30,\"max-size\":10240}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
@@ -1617,3 +1648,90 @@ jobs:
/tmp/gh-aw/safe-output-items.jsonl
/tmp/gh-aw/temporary-id-map.json
if-no-files-found: ignore
+
+ upload_assets:
+ needs:
+ - activation
+ - agent
+ if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'upload_asset')
+ runs-on: ubuntu-slim
+ permissions:
+ contents: write
+ timeout-minutes: 10
+ env:
+ GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
+ outputs:
+ branch_name: ${{ steps.upload_assets.outputs.branch_name }}
+ published_count: ${{ steps.upload_assets.outputs.published_count }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@a5d8b7df9d4f9137bedb3d074d0a0b005f1e1996 # v0.82.13
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Workshop Playwright QA"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/workshop-playwright-qa.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.70"
+ GH_AW_INFO_AWF_VERSION: "v0.27.35"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Checkout repository
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ with:
+ persist-credentials: false
+ fetch-depth: 0
+ - name: Configure Git credentials
+ env:
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh"
+ - name: Download assets
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: safe-outputs-assets
+ path: /tmp/gh-aw/safeoutputs/assets/
+ - name: List downloaded asset files
+ continue-on-error: true
+ run: |
+ echo "Downloaded asset files:"
+ find /tmp/gh-aw/safeoutputs/assets/ -maxdepth 1 -ls
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Push assets
+ id: upload_assets
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_ASSETS_DIR: "/tmp/gh-aw/safeoutputs/assets"
+ GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
+ GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_ASSETS_ALLOWED_EXTS: ".png"
+ GH_AW_WORKFLOW_NAME: "Workshop Playwright QA"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/workshop-playwright-qa.md"
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_ENGINE_VERSION: "1.0.70"
+ GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/upload_assets.cjs');
+ await main();
diff --git a/.github/workflows/workshop-playwright-qa.md b/.github/workflows/workshop-playwright-qa.md
index 8f7d035b..cd2d8160 100644
--- a/.github/workflows/workshop-playwright-qa.md
+++ b/.github/workflows/workshop-playwright-qa.md
@@ -38,6 +38,9 @@ safe-outputs:
expires: 1d
add-comment:
max: 5
+ upload-asset:
+ allowed-exts: [.png]
+ max: 30
timeout-minutes: 30
steps:
- name: Build workshop docs and start static server
@@ -177,11 +180,16 @@ For every failed check, record a finding with:
observed (include element selectors or text snippets where useful)
- `screenshot_path` — path where a screenshot of the failure was saved
(use `/tmp/gh-aw/screenshots/--.png`)
+- `screenshot_asset_url` — GitHub asset URL returned by `upload_asset`
+ after publishing the screenshot
For each finding, take a targeted screenshot:
- Use Playwright to capture a screenshot of the offending element when
possible, or a full-page screenshot if the element is not isolatable.
- Save the screenshot to the `screenshot_path` recorded in the finding.
+- Immediately call the `upload_asset` safe-output tool with the absolute
+ `screenshot_path`, then store the returned asset URL in
+ `screenshot_asset_url`.
If the same visual defect appears on all three viewports, record one
finding with `viewport: "all"` and a single representative screenshot
@@ -227,10 +235,8 @@ Group findings by `page_id`. For each page that has findings:
### Screenshots
-
+ 
+ 
```
4. Call `create-issue` (or `add-comment` if the issue already exists)
@@ -248,5 +254,7 @@ Group findings by `page_id`. For each page that has findings:
a description specific enough for a human to reproduce it.
- Screenshots must be taken before moving to the next finding so the
browser state is captured at the moment of failure.
-- Never call write tools other than `create-issue`, `add-comment`, and
- `noop`.
+- Every issue or comment with findings must embed the uploaded screenshot
+ assets in Markdown, not local `/tmp/...` paths.
+- Never call write tools other than `create-issue`, `add-comment`,
+ `upload_asset`, and `noop`.
diff --git a/scripts/build-docs.js b/scripts/build-docs.js
index 218ae575..b11a4d40 100644
--- a/scripts/build-docs.js
+++ b/scripts/build-docs.js
@@ -7,6 +7,25 @@ const { marked } = require('marked');
const { default: GithubSlugger } = require('github-slugger');
const markedAlert = require('marked-alert');
+function flattenTokenText(tokenOrTokens) {
+ if (Array.isArray(tokenOrTokens)) {
+ return tokenOrTokens.map(flattenTokenText).join('');
+ }
+ if (!tokenOrTokens) return '';
+ return tokenOrTokens.tokens
+ ? flattenTokenText(tokenOrTokens.tokens)
+ : (tokenOrTokens.text || tokenOrTokens.raw || '');
+}
+
+function escapeHtml(value) {
+ return String(value)
+ .replaceAll('&', '&')
+ .replaceAll('"', '"')
+ .replaceAll('<', '<')
+ .replaceAll('>', '>')
+ .replaceAll('\'', ''');
+}
+
// Plugin: clickable heading anchors with GitHub-compatible IDs
const slugger = new GithubSlugger();
marked.use({
@@ -19,9 +38,7 @@ marked.use({
heading({ tokens, depth }) {
const text = this.parser.parseInline(tokens);
// Extract plain text from tokens (avoids regex-based HTML stripping)
- const raw = tokens.map(function flattenTokenText(t) {
- return t.tokens ? t.tokens.map(flattenTokenText).join('') : (t.text || t.raw || '');
- }).join('').trim();
+ const raw = flattenTokenText(tokens).trim();
// slugger.slug() always returns a URL-safe [a-z0-9-] string, safe for attribute interpolation
const id = slugger.slug(raw);
// text is the HTML output of parseInline(), which escapes user content
@@ -37,9 +54,13 @@ marked.use({
? item.tokens[0].tokens.slice(1)
: item.tokens.slice(1);
const text = this.parser.parseInline(inlineTokens);
+ const accessibleName = flattenTokenText(inlineTokens).replace(/\s+/g, ' ').trim();
const baseAttrs = 'class="task-list-item-checkbox" disabled="" type="checkbox"';
const attrs = item.checked ? `${baseAttrs} checked=""` : baseAttrs;
- return ` ${text}\n`;
+ const labelAttr = accessibleName
+ ? ` aria-label="${escapeHtml(accessibleName)}"`
+ : '';
+ return ` ${text}\n`;
}
return false; // use default rendering for non-task items
},
@@ -150,12 +171,14 @@ const htmlContent = files.map((f, index) => {
// regex safely finds the first heading regardless of leading comment lines.
const headingMatch = markdown.match(headingRegex);
const title = pageTitleByFile.get(f);
+ const titleHtml = escapeHtml(title);
const sectionId = sectionIdsByFile.get(f);
+ const sectionTitleId = `${sectionId}-title`;
// Intentionally remove only the first heading because it is promoted to .
const markdownWithoutTitle = headingMatch ? markdown.replace(headingRegex, '').trimStart() : markdown;
const content = marked(renderWorkshopNavigation(markdownWithoutTitle, f));
const detailsOpenAttr = index === 0 ? ' open' : '';
- return `\n${title}
\n${content}\n `;
+ return `\n${titleHtml}
\n${titleHtml}
\n${content}\n `;
}).join('\n\n');
const menuGroups = [
@@ -429,19 +452,16 @@ body,
scroll-margin-top: 72px;
}
.markdown-body > details > summary {
- cursor: default;
- list-style: none;
+ display: none;
}
-.markdown-body > details > summary > h4 {
+.markdown-body > details > .workshop-page-title {
+ margin: 0;
font-size: 32px;
line-height: 1.25;
}
-.markdown-body > details > summary::-webkit-details-marker {
- display: none;
-}
@media (max-width: 543px) {
- .markdown-body > details > summary > h4 {
+ .markdown-body > details > .workshop-page-title {
font-size: 28px;
}
}