-
Notifications
You must be signed in to change notification settings - Fork 15
(MOT-4268) feat(harness): add live quickstart canary #619
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c5e137f
c64b13a
65fa6e5
147743f
0973b4f
6fb011c
7373697
5531437
83c3a41
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,40 +8,135 @@ on: | |||||||||||||||||||||||||||||||||||||||||||||||||||
| channel: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: Installer channel to validate | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: choice | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| options: [main, next] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: main | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| options: [latest, next] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: latest | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| contents: read | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| concurrency: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| group: harness-quickstart-${{ inputs.channel || 'main' }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| group: harness-quickstart-${{ inputs.channel || github.event_name }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| cancel-in-progress: true | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| validate: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: harness quickstart / latest registry (${{ inputs.channel || 'main' }}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: harness quickstart / published registry (${{ matrix.channel }}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| strategy: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fail-fast: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| matrix: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel: >- | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ${{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| github.event_name == 'schedule' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| && fromJSON('["latest","next"]') | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| || fromJSON(format('["{0}"]', inputs.channel)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| timeout-minutes: 25 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Covers registry timeouts plus terminal rendering and finalization. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| timeout-minutes: 35 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Notify Slack that validation started | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: slack_start | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue-on-error: true | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| SLACK_CHANNEL: worker-releases | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| III_CHANNEL: ${{ matrix.channel }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| text="⏳ *Harness quickstart* started · \`${III_CHANNEL}\` · <${RUN_URL}|view run>" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| payload=$(jq -n \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --arg channel "$SLACK_CHANNEL" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --arg text "$text" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| '{channel: $channel, text: $text}') | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| response=$(curl -sS --retry 3 --retry-all-errors \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -X POST https://slack.com/api/chat.postMessage \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -H "Content-Type: application/json; charset=utf-8" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -d "$payload") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! jq -e '.ok == true' <<<"$response" >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| error=$(jq -r '.error // "invalid_response"' <<<"$response" 2>/dev/null || printf 'invalid_response') | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::warning::Slack start notification failed: $error" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| message_ts=$(jq -er '.ts' <<<"$response") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel_id=$(jq -er '.channel' <<<"$response") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "ts=$message_ts" >>"$GITHUB_OUTPUT" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "channel_id=$channel_id" >>"$GITHUB_OUTPUT" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| - uses: actions/checkout@v5 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Install VHS for terminal recording | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue-on-error: true | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| VHS_VERSION: "0.11.0" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| TTYD_VERSION: "1.7.7" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| sudo apt-get update | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| sudo apt-get install -y --no-install-recommends ffmpeg | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| curl -fsSL --retry 3 \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "https://github.com/charmbracelet/vhs/releases/download/v${VHS_VERSION}/vhs_${VHS_VERSION}_Linux_x86_64.tar.gz" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -o /tmp/vhs.tar.gz | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| tar -xzf /tmp/vhs.tar.gz -C /tmp | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| sudo install -m 755 "/tmp/vhs_${VHS_VERSION}_Linux_x86_64/vhs" /usr/local/bin/vhs | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| curl -fsSL --retry 3 \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}/ttyd.x86_64" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -o /tmp/ttyd | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| sudo install -m 755 /tmp/ttyd /usr/local/bin/ttyd | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| vhs --version | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ttyd --version | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ffprobe -v quiet -version | sed -n '1p' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| vhs validate harness/tests/quickstart/quickstart.tape | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Run quickstart validator | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: quickstart | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| HARNESS_QUICKSTART_ARTIFACTS_DIR: ${{ github.workspace }}/target/harness-quickstart | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| III_CHANNEL: ${{ inputs.channel || 'main' }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| HARNESS_QUICKSTART_TRACE: "1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| III_CHANNEL: ${{ matrix.channel }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: harness/tests/quickstart/run-ci.sh | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # VHS records the real validator operations. The tape waits for a | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # unique shell prompt instead of matching transient terminal output. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Pass/fail is read from result.json because VHS cannot propagate the | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # typed command's exit code. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if command -v vhs >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if vhs harness/tests/quickstart/quickstart.tape; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! ffprobe -v error \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -select_streams v:0 \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -show_entries stream=codec_type \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -of csv=p=0 \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target/harness-quickstart/quickstart.mp4 | | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| grep -qx video; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::warning::VHS produced an invalid terminal recording" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| rm -f target/harness-quickstart/quickstart.mp4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::warning::VHS recording failed" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| rm -f target/harness-quickstart/quickstart.mp4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ ! -f target/harness-quickstart/result.json ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::warning::No validator result was produced; rerunning without recording" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| harness/tests/quickstart/run-ci.sh | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::warning::VHS is unavailable; running without recording" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| harness/tests/quickstart/run-ci.sh | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| jq -e '.status == "passed"' target/harness-quickstart/result.json >/dev/null || { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| reason=$(jq -r '.failure_reason // empty' target/harness-quickstart/result.json 2>/dev/null || true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::error::quickstart validator failed${reason:+: $reason}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Show quickstart logs | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: always() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| shopt -s nullglob | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -f target/harness-quickstart/EVIDENCE.md ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::group::EVIDENCE.md" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| cat target/harness-quickstart/EVIDENCE.md | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::endgroup::" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| logs=(target/harness-quickstart/logs/*.log) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ((${#logs[@]} == 0)); then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "No quickstart logs were produced." | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -57,48 +152,185 @@ jobs: | |||||||||||||||||||||||||||||||||||||||||||||||||||
| if: always() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -f target/harness-quickstart/EVIDENCE.md ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| cat target/harness-quickstart/EVIDENCE.md | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif [[ -f target/harness-quickstart/result.json ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -f target/harness-quickstart/result.json ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "### Harness quickstart" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| jq -r ' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "| Status | CLI | Duration |", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "| --- | --- | ---: |", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "| \(.status) | \(.cli_version) | \(.elapsed_ms) ms |", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| (if .failure_reason != "" then "", "Failure: \(.failure_reason)" else empty end) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "| Status | Channel | CLI | GLM canary | Duration |", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "| --- | --- | --- | --- | ---: |", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "| \(.status) | \(.channel) | \(.cli_version) | \(.message_check) | \(.elapsed_ms) ms |", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| (if .failure_reason != "" then "", "**Failure:** \(.failure_reason)" else empty end) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ' target/harness-quickstart/result.json | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "### Harness quickstart" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "No quickstart result was produced." | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -f target/harness-quickstart/quickstart.mp4 ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Terminal recording: \`quickstart.mp4\` in the \`harness-quickstart-${{ matrix.channel }}\` artifact." | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
158
to
+172
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Separately, moving 🐛 Proposed fix- (if .failure_reason != "" then "", "**Failure:** \(.failure_reason)" else empty end)
+ (if (.failure_reason // "") != "" then "", "**Failure:** \(.failure_reason)" else empty end)📝 Committable suggestion
Suggested change
🧰 Tools🪛 zizmor (1.28.0)[warning] 171-171: code injection via template expansion (template-injection): may expand into attacker-controllable code (template-injection) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| } >> "$GITHUB_STEP_SUMMARY" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Upload quickstart result | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Upload quickstart artifacts | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: always() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: actions/upload-artifact@v6 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: harness-quickstart-result | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| path: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target/harness-quickstart/result.json | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target/harness-quickstart/EVIDENCE.md | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target/harness-quickstart/timings.tsv | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target/harness-quickstart/cli-version.txt | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target/harness-quickstart/console-send.json | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target/harness-quickstart/console-status.json | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target/harness-quickstart/config.yaml | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target/harness-quickstart/iii.lock | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: harness-quickstart-${{ matrix.channel }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| path: target/harness-quickstart/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| retention-days: 7 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if-no-files-found: ignore | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Upload quickstart logs | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: always() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: actions/upload-artifact@v6 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: harness-quickstart-logs | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| path: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target/harness-quickstart/logs/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| target/harness-quickstart/console.html | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| retention-days: 7 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if-no-files-found: ignore | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Finalize Slack notification | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: ${{ always() && steps.slack_start.outputs.ts != '' && steps.slack_start.outputs.channel_id != '' }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue-on-error: true | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| SLACK_CHANNEL_ID: ${{ steps.slack_start.outputs.channel_id }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| SLACK_MESSAGE_TS: ${{ steps.slack_start.outputs.ts }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| III_CHANNEL: ${{ matrix.channel }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| QUICKSTART_OUTCOME: ${{ steps.quickstart.outcome }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| JOB_STATUS: ${{ job.status }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| COMMIT_URL: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| COMMIT_SHA: ${{ github.sha }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -uo pipefail | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| slack_call() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| local method=$1 payload=$2 response error | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! response=$(curl -sS --retry 3 --retry-all-errors \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -X POST "https://slack.com/api/${method}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -H "Content-Type: application/json; charset=utf-8" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -d "$payload"); then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::warning::Slack ${method} request failed" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! jq -e '.ok == true' <<<"$response" >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| error=$(jq -r '.error // "invalid_response"' <<<"$response" 2>/dev/null || printf 'invalid_response') | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::warning::Slack ${method} failed: $error" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| result_file=target/harness-quickstart/result.json | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -f "$result_file" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| result_status=$(jq -r '.status // "failed"' "$result_file") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| cli_version=$(jq -r '.cli_version // "unknown"' "$result_file") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| duration=$(jq -r '((.elapsed_ms // 0) / 1000 | floor | tostring) + "s"' "$result_file") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| failure_reason=$(jq -r '.failure_reason // ""' "$result_file") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| message_check=$(jq -r '.message_check // "unknown"' "$result_file") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| result_status=failed | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| cli_version=unknown | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| duration=unknown | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| message_check=unknown | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| failure_reason="No result.json was produced (validator outcome: ${QUICKSTART_OUTCOME})." | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| short_sha=${COMMIT_SHA:0:7} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ "$JOB_STATUS" == "success" && "$result_status" == "passed" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| icon=✅ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| label=succeeded | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| detail=$(printf "*Status:* succeeded\n*Installer channel:* \`%s\`\n*CLI:* \`%s\`\n*GLM canary:* \`%s\`\n*Duration:* %s\n*Commit:* <%s|\`%s\`>\n<%s|Open GitHub Actions run>" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "$III_CHANNEL" "$cli_version" "$message_check" "$duration" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "$COMMIT_URL" "$short_sha" "$RUN_URL") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| icon=❌ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| label=failed | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -z "$failure_reason" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| failure_reason="Workflow status: ${JOB_STATUS} (validator outcome: ${QUICKSTART_OUTCOME})." | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| detail=$(printf "*Status:* failed\n*Installer channel:* \`%s\`\n*CLI:* \`%s\`\n*GLM canary:* \`%s\`\n*Duration:* %s\n*Failure:* %s\n*Commit:* <%s|\`%s\`>\n<%s|Open GitHub Actions run>" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "$III_CHANNEL" "$cli_version" "$message_check" "$duration" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "${failure_reason:-unknown failure}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "$COMMIT_URL" "$short_sha" "$RUN_URL") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| root_text="${icon} *Harness quickstart* ${label} · \`${III_CHANNEL}\` · GLM ${message_check} · ${duration} · <${RUN_URL}|view run>" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| update_payload=$( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # shellcheck disable=SC2016 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| jq -n \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --arg channel "$SLACK_CHANNEL_ID" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --arg ts "$SLACK_MESSAGE_TS" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --arg text "$root_text" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| '{channel: $channel, ts: $ts, text: $text}' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| thread_payload=$( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # shellcheck disable=SC2016 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| jq -n \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --arg channel "$SLACK_CHANNEL_ID" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --arg ts "$SLACK_MESSAGE_TS" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --arg text "$detail" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| '{channel: $channel, thread_ts: $ts, text: $text}' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| failed=0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| slack_call chat.update "$update_payload" || failed=1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| slack_call chat.postMessage "$thread_payload" || failed=1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit "$failed" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Upload terminal recording to Slack thread | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: ${{ always() && steps.slack_start.outputs.ts != '' && steps.slack_start.outputs.channel_id != '' }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue-on-error: true | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| SLACK_CHANNEL_ID: ${{ steps.slack_start.outputs.channel_id }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| SLACK_MESSAGE_TS: ${{ steps.slack_start.outputs.ts }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -uo pipefail | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| recording=target/harness-quickstart/quickstart.mp4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ ! -s "$recording" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "No terminal recording was produced; skipping the Slack upload." | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| filename=$(basename "$recording") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| length=$(wc -c <"$recording" | tr -d '[:space:]') | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Slack external upload flow: reserve a URL, send the bytes, then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # attach the file to the run's thread. Needs the files:write scope. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ticket=$(curl -sS --retry 3 --retry-all-errors \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -X POST https://slack.com/api/files.getUploadURLExternal \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --data-urlencode "filename=${filename}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --data-urlencode "length=${length}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! jq -e '.ok == true' <<<"$ticket" >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| error=$(jq -r '.error // "invalid_response"' <<<"$ticket" 2>/dev/null || printf 'invalid_response') | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::warning::Slack files.getUploadURLExternal failed: $error (is the files:write scope granted?)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| upload_url=$(jq -er '.upload_url' <<<"$ticket") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| file_id=$(jq -er '.file_id' <<<"$ticket") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! curl -sS --fail --retry 3 --retry-all-errors \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -X POST "$upload_url" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -H "Content-Type: application/octet-stream" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --data-binary @"$recording" >/dev/null; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::warning::Slack recording upload failed" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| complete_payload=$(jq -n \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --arg channel "$SLACK_CHANNEL_ID" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --arg ts "$SLACK_MESSAGE_TS" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --arg id "$file_id" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| --arg title "Harness quickstart terminal recording.mp4" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| '{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel_id: $channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| thread_ts: $ts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| initial_comment: "🎬 Terminal recording of this run", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| files: [{id: $id, title: $title}] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }') | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| response=$(curl -sS --retry 3 --retry-all-errors \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -X POST https://slack.com/api/files.completeUploadExternal \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -H "Content-Type: application/json; charset=utf-8" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| -d "$complete_payload") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! jq -e '.ok == true' <<<"$response" >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| error=$(jq -r '.error // "invalid_response"' <<<"$response" 2>/dev/null || printf 'invalid_response') | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::warning::Slack files.completeUploadExternal failed: $error" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Uploaded ${filename} (${length} bytes) to the Slack thread." | ||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Does Slack chat.postMessage still support channel name instead of channel ID?💡 Result:
Yes, the Slack chat.postMessage API method still supports using a channel name instead of a channel ID [1][2]. According to the official Slack API documentation, you can pass either the channel name (e.g.,
#general) or the encoded channel ID to the channel parameter [1]. However, while this is supported, it is generally discouraged in production environments. Channel names can change over time, whereas channel IDs remain permanent identifiers [3]. Furthermore, most other Slack API methods that require a channel identifier do not support names and specifically require the channel ID [3]. Using the channel ID is considered the best practice to ensure reliability and consistency across your integration [3].Citations:
🌐 Web query:
Slack chat.postMessage channel parameter "channel" accept bare channel name without # Slack API💡 Result:
Yes, the Slack API chat.postMessage channel parameter accepts a bare channel name (without the # prefix) [1][2][3][4]. According to official Slack API documentation, the channel argument can be an encoded channel ID (e.g., C123456) or the channel's name [1][2]. When passing the name, you do not need to include the hash (#) symbol [1][2]. While the API allows using channel names, it is generally recommended to use the channel's unique ID instead [5]. Channel names can be renamed, whereas channel IDs are persistent, ensuring your application remains reliable if a channel's name is changed [5][6]. You can retrieve the ID associated with a channel name by using the conversations.list API method [1][5][7].
Citations:
🌐 Web query:
Slack API channel names chat.postMessage channel parameter no # examples💡 Result:
The Slack API
chat.postMessagemethod'schannelparameter accepts either an encoded channel ID (e.g.,C12345678) or a channel name [1][2]. When using a channel name, you do not need to include the hash (#) prefix [2]. You can pass the name as a plain string (e.g.,"general") [2]. While the API documentation has historically permitted channel names [2], using the channel ID is strongly recommended because channel names are not globally unique across workspaces and can be changed by users, whereas the channel ID is immutable [1]. To obtain the correct channel ID, you can use theconversations.listAPI method [1][3].Citations:
🌐 Web query:
Slack API channel_not_name channel_param_is_not_valid chat.postMessage channel name💡 Result:
The channel_not_found error in the Slack API's chat.postMessage method indicates that the Slack platform cannot locate the specified channel or that your application does not have the necessary permission to post into it [1][2][3]. Common causes and solutions include: 1. App Membership: For private channels, your Slack app or bot must be explicitly added to the channel as a member [2][4]. You can invite your bot by typing /invite
@YourBotNamein the channel [3][4]. 2. Incorrect Identifier: Ensure the value passed for the channel argument is correct [5]. While Slack often supports both channel names (e.g.,#general) and channel IDs (e.g., C024BE91L), using the unique channel ID is more reliable and recommended to avoid ambiguity [6][5][4]. 3. Permission Scopes: Verify that your application has the required scopes for the requested action, such as chat:write or chat:write.public (for public channels where the bot is not a member) [7][4]. 4. Token Context: Ensure you are using the correct token associated with the intended workspace. Errors can occur if an OAuth token is used for a workspace where the app was not installed, or if there is a mismatch between the token and the channel access [8][5]. 5. Formatting: If making manual API requests, ensure parameters are correctly encoded in the request body (e.g., application/x-www-form-urlencoded) rather than incorrectly placed in headers or improperly formatted [8][9][10].Citations:
🌐 Web query:
Slack chat.postMessage channel parameter "channel" accepts channel name or encoded channel ID💡 Result:
The channel parameter in the Slack chat.postMessage method accepts either a Slack-encoded channel ID (e.g., C123456) or a channel name (e.g.,
#general) [1][2][3]. While it is possible to use a channel name, it is generally recommended to use the channel ID [4]. Channel names are mutable and can be changed by workspace members, whereas channel IDs are persistent and unique identifiers [4]. Furthermore, most other Slack API methods that require a channel reference only accept the channel ID, making the use of IDs a more consistent practice across your integration [4]. If you use a channel name, the API will resolve it to the corresponding channel, but relying on IDs ensures greater stability for your application [1][4].Citations:
🌐 Web query:
curl --retry-all-errors retry POST request duplicate submission non-idempotent documentation💡 Result:
Using the --retry-all-errors option in curl is considered a "sledgehammer" approach to retrying transfers and should be used with extreme caution, particularly with non-idempotent HTTP methods like POST [1][2]. Key Considerations: 1. Risk of Duplicate Submissions: When you use --retry-all-errors, curl will attempt to retry the request for any error it encounters [3][4]. If the initial request was a POST or another non-idempotent method that partially reached the server—or was processed before a connection error occurred—the retry will trigger a duplicate submission on the server side [1][2][5]. 2. Scope of Option: This option must be used in conjunction with --retry [1][4]. By default, curl only retries on certain connection errors and transient HTTP 5xx server errors [5]. Enabling --retry-all-errors forces retries on nearly all failures, including many 4xx client errors that would otherwise be considered final [1][5]. 3. Data Handling: The documentation warns against using this option with redirected input or output (e.g., using pipes | or >), as curl cannot automatically "reset" these streams before a retry, which can lead to receiving or sending duplicate data [1]. 4. Best Practices: * Avoid --retry-all-errors for any request that is not explicitly safe to repeat [2][5]. * Prioritize idempotent methods (like GET or HEAD) when configuring automatic retry logic [2][5]. * For POST/PUT requests, handle retries at the application level where you can verify the state of the previous submission before attempting another [2][5]. * If you must use it, ensure the command is part of a controlled, well-understood environment rather than a default configuration [1][2]. Official documentation emphasizes that you are generally better off handling specific, expected failure scenarios within a script rather than relying on this broad, indiscriminate retry mechanism [1].
Citations:
Use the Slack channel ID and limit retries for non-idempotent Slack posts.
chat.postMessageaccepts channel names, but names are mutable and can fail withchannel_not_found;channel_idis preferred for reliability. Also,--retry-all-errorswill retry the POST submission more broadly than--retry 3, which risks duplicate Slack posts if the first request was processed before a failure was reported.🤖 Prompt for AI Agents