From 26aa1b69ac482d29ba226d08988a1c902e6141bb Mon Sep 17 00:00:00 2001 From: Dominic Rubas <1042243+rubas@users.noreply.github.com> Date: Fri, 16 Oct 2026 10:41:58 +0200 Subject: [PATCH 1/3] ci: move the audit issue report into a report-failure action security.yml keeps the same title, labels, and issue text. --- .github/actions/report-failure/action.yml | 80 +++++++++++++++++++++++ .github/workflows/security.yml | 66 ++++--------------- 2 files changed, 91 insertions(+), 55 deletions(-) create mode 100644 .github/actions/report-failure/action.yml diff --git a/.github/actions/report-failure/action.yml b/.github/actions/report-failure/action.yml new file mode 100644 index 0000000..3998e33 --- /dev/null +++ b/.github/actions/report-failure/action.yml @@ -0,0 +1,80 @@ +name: "Report workflow failure" +description: "Open or comment on an issue when a workflow fails, and close it on the next green run." + +inputs: + result: + description: "'failure' opens or comments on the issue, 'success' closes it" + required: true + title: + description: "Issue title; it identifies the issue across runs" + required: true + subject: + description: "What failed, in lower case, for the issue text (e.g. 'security audit')" + required: true + labels: + description: "JSON array of {name, color, description} for a new issue; missing labels are created" + required: false + default: "[]" + +runs: + using: "composite" + steps: + - name: Open or close the issue + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + RESULT: ${{ inputs.result }} + TITLE: ${{ inputs.title }} + SUBJECT: ${{ inputs.subject }} + LABELS: ${{ inputs.labels }} + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const { RESULT: result, TITLE: title, SUBJECT: subject } = process.env; + const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + if (result === 'failure') { + const labels = JSON.parse(process.env.LABELS); + for (const label of labels) { + try { + await github.rest.issues.getLabel({ owner, repo, name: label.name }); + } catch (error) { + if (error.status !== 404) throw error; + await github.rest.issues.createLabel({ owner, repo, ...label }); + } + } + const date = new Date().toISOString().split('T')[0]; + const body = `The ${subject} failed on ${date}. See the [workflow run](${runUrl}).`; + const { data: open } = await github.rest.issues.listForRepo({ + owner, repo, state: 'open', labels: labels.map(label => label.name).join(','), per_page: 100, + }); + const existing = open.find(i => i.title === title); + if (existing) { + await github.rest.issues.createComment({ + owner, repo, + issue_number: existing.number, body, + }); + } else { + await github.rest.issues.create({ + owner, repo, + title, body, labels: labels.map(label => label.name), + }); + } + } else if (result === 'success') { + const passed = subject.charAt(0).toUpperCase() + subject.slice(1); + const { data: open } = await github.rest.issues.listForRepo({ + owner, repo, state: 'open', per_page: 100, + }); + for (const issue of open.filter(i => i.title === title)) { + await github.rest.issues.createComment({ + owner, repo, + issue_number: issue.number, + body: `${passed} passed. Closing. See the [workflow run](${runUrl}).`, + }); + await github.rest.issues.update({ + owner, repo, + issue_number: issue.number, state: 'closed', + }); + } + } else { + throw new Error(`result must be 'failure' or 'success', got '${result}'`); + } diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 562ae66..bb4da8c 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -66,63 +66,19 @@ jobs: - name: Report audit failure if: failure() - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + uses: $/.github/actions/report-failure with: - script: | - const owner = context.repo.owner; - const repo = context.repo.repo; - const title = 'Security Audit Failed'; - const labels = [ - { name: 'security', color: 'd73a4a', description: 'Security-relevant' }, - { name: 'automated', color: 'ededed', description: 'Created by automation' }, - ]; - for (const label of labels) { - try { - await github.rest.issues.getLabel({ owner, repo, name: label.name }); - } catch (error) { - if (error.status !== 404) throw error; - await github.rest.issues.createLabel({ owner, repo, ...label }); - } - } - const date = new Date().toISOString().split('T')[0]; - const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; - const body = `The security audit failed on ${date}. See the [workflow run](${runUrl}).`; - const { data: open } = await github.rest.issues.listForRepo({ - owner, repo, state: 'open', labels: labels.map(label => label.name).join(','), per_page: 100, - }); - const existing = open.find(i => i.title === title); - if (existing) { - await github.rest.issues.createComment({ - owner, repo, - issue_number: existing.number, body, - }); - } else { - await github.rest.issues.create({ - owner, repo, - title, body, labels: labels.map(label => label.name), - }); - } + result: failure + title: Security Audit Failed + subject: security audit + labels: >- + [{"name": "security", "color": "d73a4a", "description": "Security-relevant"}, + {"name": "automated", "color": "ededed", "description": "Created by automation"}] - name: Resolve audit issue on success if: success() - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + uses: $/.github/actions/report-failure with: - script: | - const owner = context.repo.owner; - const repo = context.repo.repo; - const title = 'Security Audit Failed'; - const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; - const { data: open } = await github.rest.issues.listForRepo({ - owner, repo, state: 'open', per_page: 100, - }); - for (const issue of open.filter(i => i.title === title)) { - await github.rest.issues.createComment({ - owner, repo, - issue_number: issue.number, - body: `Security audit passed. Closing. See the [workflow run](${runUrl}).`, - }); - await github.rest.issues.update({ - owner, repo, - issue_number: issue.number, state: 'closed', - }); - } + result: success + title: Security Audit Failed + subject: security audit From 2a94d117fff1fa026322377969f966b1895881f3 Mon Sep 17 00:00:00 2001 From: Dominic Rubas <1042243+rubas@users.noreply.github.com> Date: Fri, 16 Oct 2026 10:41:58 +0200 Subject: [PATCH 2/3] ci: open an issue when the weekly integration run fails The issue closes on the next green run, like the security audit issue. --- .github/workflows/integration.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 501d21c..8d47806 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -29,6 +29,7 @@ jobs: timeout-minutes: 25 permissions: contents: read # checkout the repository + issues: write # open an issue when the integration run fails concurrency: group: ${{ github.workflow }}-${{ github.ref }}-integration cancel-in-progress: true @@ -48,3 +49,22 @@ jobs: - name: Run integration tests run: task test:integration + + - name: Report integration failure + if: failure() + uses: $/.github/actions/report-failure + with: + result: failure + title: Integration Tests Failed + subject: integration run + labels: >- + [{"name": "integration", "color": "fbca04", "description": "Integration test run"}, + {"name": "automated", "color": "ededed", "description": "Created by automation"}] + + - name: Resolve integration issue on success + if: success() + uses: $/.github/actions/report-failure + with: + result: success + title: Integration Tests Failed + subject: integration run From 9a597633f988e57af3ba4e079101516a5c3063e6 Mon Sep 17 00:00:00 2001 From: Dominic Rubas <1042243+rubas@users.noreply.github.com> Date: Fri, 16 Oct 2026 10:41:58 +0200 Subject: [PATCH 3/3] ci: cache the VAD model fixture and key the cache on fixtures.ex jfk.wav no longer exists; the PCM is committed under test/support/. --- .github/actions/setup/action.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 2cc3f61..158e73c 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -88,15 +88,15 @@ runs: restore-keys: | ${{ runner.os }}-native-${{ inputs.rust-toolchain }}- - - name: Cache integration fixtures (model + audio) + - name: Cache integration fixtures (models) if: ${{ inputs.cache-fixtures == 'true' }} uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | test/fixtures/ggml-tiny.en.bin test/fixtures/ggml-tiny.bin - test/fixtures/jfk.wav - key: ${{ runner.os }}-fixtures-v1 + test/fixtures/ggml-silero-v5.1.2.bin + key: ${{ runner.os }}-fixtures-${{ hashFiles('test/support/fixtures.ex') }} - name: Install Hex/Rebar + fetch deps shell: bash