From bc94492293797d97b7859b905d7e7aed70b0357c Mon Sep 17 00:00:00 2001 From: frodehus Date: Sat, 19 Sep 2026 14:44:47 +0200 Subject: [PATCH 1/2] Hold the Certum signing jobs to one login at a time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v1.8.0 release failed with "SimplySign rejected the credentials 3 times; check the account name, the otpauth URI and the clock" — and every one of those was fine. In the same run, minutes apart, the CLI's Windows job opened a session against the same account and signed. Four jobs across three workflows sign with one Certum SimplySign account, and two of them — "Windows app" and the CLI matrix's windows leg — both hang off `needs: check`, so they start together and log in together. A TOTP code is single-use, so whichever login lands second is told "invalid user name or token". Connect-SimplySign.ps1 sees only an unexpected window, dismisses it, pastes a fresh code and eventually gives up, blaming the credentials. It is a race, and it has been there since signing was introduced in #165. It had simply been winning: at v1.7.0 the two logins overlapped by three seconds, at v1.8.0 they overlapped entirely. A shared `certum-simplysign` concurrency group now holds every signing job in the repository to one login at a time, releases and the manual signing check alike, with cancel-in-progress false so the loser queues rather than dies. The group cannot be narrowed to the windows leg: job-level concurrency may use only the github, inputs and vars contexts, not matrix, so the CLI's linux and macOS legs queue on it too. They are minutes of waiting against an account lockout. The thrown message now names a concurrent signing job as the first thing to suspect, since the old one sent this investigation after three things that were all correct. Co-authored-by: Claude Opus 5 --- .github/workflows/release-audit.yml | 8 ++++++++ .github/workflows/release.yml | 16 ++++++++++++++++ .github/workflows/signing-check.yml | 3 ++- scripts/Connect-SimplySign.ps1 | 2 +- 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-audit.yml b/.github/workflows/release-audit.yml index 8db6c4aa..68f3fe41 100644 --- a/.github/workflows/release-audit.yml +++ b/.github/workflows/release-audit.yml @@ -132,6 +132,14 @@ jobs: if: github.event_name == 'push' needs: check runs-on: ${{ matrix.os }} + # One Certum SimplySign account serves every signing job, and a TOTP code is single-use, so + # two logins that overlap make the loser read "invalid user name or token" — which the script + # can only see as a rejected credential. This group holds every signing job in the repository, + # across workflows, to one login at a time. Not `matrix`-scoped: job-level concurrency may use + # only the github, inputs and vars contexts, so the CLI's other legs queue here too. + concurrency: + group: certum-simplysign + cancel-in-progress: false strategy: fail-fast: false matrix: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6919bafe..8ec3a5ce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -427,6 +427,14 @@ jobs: if: github.event_name == 'push' needs: check runs-on: windows-latest + # One Certum SimplySign account serves every signing job, and a TOTP code is single-use, so + # two logins that overlap make the loser read "invalid user name or token" — which the script + # can only see as a rejected credential. This group holds every signing job in the repository, + # across workflows, to one login at a time. Not `matrix`-scoped: job-level concurrency may use + # only the github, inputs and vars contexts, so the CLI's other legs queue here too. + concurrency: + group: certum-simplysign + cancel-in-progress: false env: TAG: ${{ github.ref_name }} outputs: @@ -522,6 +530,14 @@ jobs: if: github.event_name == 'push' needs: check runs-on: ${{ matrix.os }} + # One Certum SimplySign account serves every signing job, and a TOTP code is single-use, so + # two logins that overlap make the loser read "invalid user name or token" — which the script + # can only see as a rejected credential. This group holds every signing job in the repository, + # across workflows, to one login at a time. Not `matrix`-scoped: job-level concurrency may use + # only the github, inputs and vars contexts, so the CLI's other legs queue here too. + concurrency: + group: certum-simplysign + cancel-in-progress: false strategy: fail-fast: false matrix: diff --git a/.github/workflows/signing-check.yml b/.github/workflows/signing-check.yml index 2b2e908f..903274e2 100644 --- a/.github/workflows/signing-check.yml +++ b/.github/workflows/signing-check.yml @@ -8,8 +8,9 @@ name: Signing check on: workflow_dispatch: +# Shared with the release workflows' signing jobs: one Certum account, one login at a time. concurrency: - group: "${{ github.workflow }}" + group: certum-simplysign cancel-in-progress: false jobs: diff --git a/scripts/Connect-SimplySign.ps1 b/scripts/Connect-SimplySign.ps1 index b456e4f1..74e6560f 100644 --- a/scripts/Connect-SimplySign.ps1 +++ b/scripts/Connect-SimplySign.ps1 @@ -248,7 +248,7 @@ for ($elapsed = 0; $elapsed -lt 180; $elapsed += 5) { exit 0 } if (Get-AppWindows | Where-Object { $_ -ne $login }) { - if ($retries -ge 3) { throw "SimplySign rejected the credentials $retries times; check the account name, the otpauth URI and the clock." } + if ($retries -ge 3) { throw "SimplySign refused the login $retries times. The likeliest cause is another signing job holding the account: a TOTP code is single-use, so whichever login lands second reads 'invalid user name or token'. Check that no other run is signing, then the account name, the otpauth URI and the clock." } $retries++ if (Dismiss-Modals $login) { Start-Sleep -Milliseconds 500 From 37992c91d39c0bc71f25abd11d8151010a9c931c Mon Sep 17 00:00:00 2001 From: frodehus Date: Sat, 19 Sep 2026 14:54:02 +0200 Subject: [PATCH 2/2] Release notes: an apostrophe was ending the shell string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Microsoft's" sits inside a single-quoted bash string, so the apostrophe closed it and left the rest of the sentence bare. The first unquoted ")" then ended the step: line 48: syntax error near unexpected token `)' v1.8.0 is the first release to reach that line — it landed in #170 at 14:01 on 2026-09-15, and the last release before it ran at 11:20 the same morning. Both release workflows carry the sentence, so elevate-audit would have failed the same way on its next tag. Neither notes step runs outside a release, which is why a syntax error sat in main for four days. Checking it is cheap: every `run:` block in the repository that is not pwsh now passes `bash -n` (91 of them), and that is worth a job of its own if this happens again. Co-authored-by: Claude Opus 5 --- .github/workflows/release-audit.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-audit.yml b/.github/workflows/release-audit.yml index 68f3fe41..e4ab0599 100644 --- a/.github/workflows/release-audit.yml +++ b/.github/workflows/release-audit.yml @@ -316,7 +316,7 @@ jobs: echo ' brew trust frodehus/elevate' echo ' brew install frodehus/elevate/elevate-audit' echo - echo 'Windows: `winget install Reothor.Elevate.Audit` (the manifest goes to winget-pkgs with each release and is published once Microsoft's checks pass, usually within a day or two), or download `elevate-audit-'"$VERSION"'-win-x64.zip` (or `-win-arm64.zip`) below and put `elevate-audit.exe` on your PATH.' + echo 'Windows: `winget install Reothor.Elevate.Audit` (the manifest goes to winget-pkgs with each release and is published once Microsoft'"'"'s checks pass, usually within a day or two), or download `elevate-audit-'"$VERSION"'-win-x64.zip` (or `-win-arm64.zip`) below and put `elevate-audit.exe` on your PATH.' echo echo 'Or download the archive for your platform below and unpack it anywhere on your PATH. Verify with `sha256sum -c elevate-audit-'"$VERSION"'-checksums.txt`. See [docs/audit.md](https://github.com/'"${{ github.repository }}"'/blob/main/docs/audit.md).' echo diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8ec3a5ce..fe61e64a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -775,7 +775,7 @@ jobs: echo echo ' brew install frodehus/elevate/elevate-cli' echo - echo 'Windows: `Elevate-'"$VERSION"'-x64.msi` (or `-arm64.msi`) installs `elevate.exe` in a `cli` folder under the app and adds that folder to your PATH. Standalone: `winget install Reothor.Elevate.CLI` (the manifest goes to winget-pkgs with each release and is published once Microsoft's checks pass, usually within a day or two), or download `elevate-cli-'"$VERSION"'-win-x64.zip` (or `-win-arm64.zip`) below and put `elevate.exe` on your PATH.' + echo 'Windows: `Elevate-'"$VERSION"'-x64.msi` (or `-arm64.msi`) installs `elevate.exe` in a `cli` folder under the app and adds that folder to your PATH. Standalone: `winget install Reothor.Elevate.CLI` (the manifest goes to winget-pkgs with each release and is published once Microsoft'"'"'s checks pass, usually within a day or two), or download `elevate-cli-'"$VERSION"'-win-x64.zip` (or `-win-arm64.zip`) below and put `elevate.exe` on your PATH.' echo echo 'Or download the archive for your platform below and unpack it anywhere on your PATH. Verify with `sha256sum -c elevate-cli-'"$VERSION"'-checksums.txt`. See [cli/README.md](https://github.com/'"${{ github.repository }}"'/blob/main/cli/README.md).' if [ "$MAC_SIGNED" != "1" ]; then