From cda95c29fd5a63763654785ed64022805dc27e98 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 12:46:47 +0200 Subject: [PATCH 01/19] fix(updater): Add token validation and git credential configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses GitHub Actions checkout authentication issues by: - Adding early token validation with clear error messages - Configuring git credentials explicitly to prevent "terminal prompts disabled" errors This helps prevent and diagnose common token issues like: - Expired tokens - Missing expiration dates - Insufficient scopes - Incorrect secret references Related to actions/checkout#664 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- updater/action.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/updater/action.yml b/updater/action.yml index f153507..b951ea4 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -117,6 +117,30 @@ runs: } Write-Output "✓ Post-update script path '${{ inputs.post-update-script }}' is valid" + - name: Validate GitHub token + shell: bash + env: + GH_TOKEN: ${{ inputs.api-token }} + run: | + if ! gh api repos/${{ github.repository }} --silent; then + echo "::error::GitHub token validation failed. Please verify:" + echo " 1. Token has not expired" + echo " 2. Token has an expiration date set" + echo " 3. Token has 'repo' and 'workflow' scopes" + echo " 4. Token syntax is correct: \${{ secrets.TOKEN_NAME }}" + exit 1 + fi + echo "✓ GitHub token is valid and has access to this repository" + + - name: Configure git credentials + shell: bash + env: + GH_TOKEN: ${{ inputs.api-token }} + run: | + git config --global credential.https://github.com.username git + git config --global credential.https://github.com.password "$GH_TOKEN" + echo "✓ Git credentials configured for GitHub operations" + # What we need to accomplish: # * update to the latest tag # * create a PR From 70aad9baf2e0d26e0782fb31d4a44a6a3a6a8111 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 12:49:58 +0200 Subject: [PATCH 02/19] fix: Escape template expression in error message --- updater/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updater/action.yml b/updater/action.yml index b951ea4..48d0596 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -127,7 +127,7 @@ runs: echo " 1. Token has not expired" echo " 2. Token has an expiration date set" echo " 3. Token has 'repo' and 'workflow' scopes" - echo " 4. Token syntax is correct: \${{ secrets.TOKEN_NAME }}" + echo " 4. Token syntax is correct: "'${{ secrets.TOKEN_NAME }}'" exit 1 fi echo "✓ GitHub token is valid and has access to this repository" From 06e4c9b5d343bd79d77fed33b30906214ca767f6 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 12:50:15 +0200 Subject: [PATCH 03/19] fix(updater): Remove token syntax echo from validation error message --- updater/action.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/updater/action.yml b/updater/action.yml index 48d0596..b08cff9 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -127,7 +127,6 @@ runs: echo " 1. Token has not expired" echo " 2. Token has an expiration date set" echo " 3. Token has 'repo' and 'workflow' scopes" - echo " 4. Token syntax is correct: "'${{ secrets.TOKEN_NAME }}'" exit 1 fi echo "✓ GitHub token is valid and has access to this repository" From a3b716ff33e27ce4153b3ffb1c3bda2b2a245954 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:08:24 +0200 Subject: [PATCH 04/19] fix: Improve token validation to detect malformed tokens --- updater/action.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/updater/action.yml b/updater/action.yml index b08cff9..cceaa8f 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -122,11 +122,23 @@ runs: env: GH_TOKEN: ${{ inputs.api-token }} run: | - if ! gh api repos/${{ github.repository }} --silent; then + if [ -z "$GH_TOKEN" ]; then + echo "::error::GitHub token is empty. Please verify the token is passed correctly." + exit 1 + fi + + if echo "$GH_TOKEN" | grep -q '[[:space:]]'; then + echo "::error::GitHub token contains whitespace characters (newlines, spaces, etc). This suggests the token secret may be malformed." + exit 1 + fi + + if ! gh api repos/${{ github.repository }} --silent 2>&1; then echo "::error::GitHub token validation failed. Please verify:" - echo " 1. Token has not expired" - echo " 2. Token has an expiration date set" - echo " 3. Token has 'repo' and 'workflow' scopes" + echo " 1. Token is not empty or malformed" + echo " 2. Token has not expired" + echo " 3. Token has an expiration date set" + echo " 4. Token has 'repo' and 'workflow' scopes" + echo " 5. Token syntax is correct: "'${{ secrets.TOKEN_NAME }}'" exit 1 fi echo "✓ GitHub token is valid and has access to this repository" From 71c67c5ebb7bb1f41721595020a907589e92c0f8 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:10:35 +0200 Subject: [PATCH 05/19] refactor: Use PowerShell for token validation and git config --- updater/action.yml | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/updater/action.yml b/updater/action.yml index cceaa8f..36532b8 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -118,39 +118,40 @@ runs: Write-Output "✓ Post-update script path '${{ inputs.post-update-script }}' is valid" - name: Validate GitHub token - shell: bash + shell: pwsh env: GH_TOKEN: ${{ inputs.api-token }} run: | - if [ -z "$GH_TOKEN" ]; then - echo "::error::GitHub token is empty. Please verify the token is passed correctly." + if ([string]::IsNullOrEmpty($env:GH_TOKEN)) { + Write-Output "::error::GitHub token is empty. Please verify the token is passed correctly." exit 1 - fi + } - if echo "$GH_TOKEN" | grep -q '[[:space:]]'; then - echo "::error::GitHub token contains whitespace characters (newlines, spaces, etc). This suggests the token secret may be malformed." + if ($env:GH_TOKEN -match '\s') { + Write-Output "::error::GitHub token contains whitespace characters (newlines, spaces, etc). This suggests the token secret may be malformed." exit 1 - fi + } - if ! gh api repos/${{ github.repository }} --silent 2>&1; then - echo "::error::GitHub token validation failed. Please verify:" - echo " 1. Token is not empty or malformed" - echo " 2. Token has not expired" - echo " 3. Token has an expiration date set" - echo " 4. Token has 'repo' and 'workflow' scopes" - echo " 5. Token syntax is correct: "'${{ secrets.TOKEN_NAME }}'" + gh api repos/${{ github.repository }} --silent 2>&1 | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Output "::error::GitHub token validation failed. Please verify:" + Write-Output " 1. Token is not empty or malformed" + Write-Output " 2. Token has not expired" + Write-Output " 3. Token has an expiration date set" + Write-Output " 4. Token has 'repo' and 'workflow' scopes" + Write-Output " 5. Token syntax is correct: "'${{ secrets.TOKEN_NAME }}'" exit 1 - fi - echo "✓ GitHub token is valid and has access to this repository" + } + Write-Output "✓ GitHub token is valid and has access to this repository" - name: Configure git credentials - shell: bash + shell: pwsh env: GH_TOKEN: ${{ inputs.api-token }} run: | git config --global credential.https://github.com.username git - git config --global credential.https://github.com.password "$GH_TOKEN" - echo "✓ Git credentials configured for GitHub operations" + git config --global credential.https://github.com.password $env:GH_TOKEN + Write-Output "✓ Git credentials configured for GitHub operations" # What we need to accomplish: # * update to the latest tag From 7b2af371523fe473ac7ce5e94363ed4a2b4a2de4 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:12:39 +0200 Subject: [PATCH 06/19] feat: Add token scope validation Checks token scopes using x-oauth-scopes header: - Reports scopes for classic PATs - Warns if repo/public_repo scope missing - Provides guidance for fine-grained PATs Based on https://github.com/orgs/community/discussions/25259 --- updater/action.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/updater/action.yml b/updater/action.yml index 36532b8..a89ad66 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -132,6 +132,7 @@ runs: exit 1 } + # Check token validity and access gh api repos/${{ github.repository }} --silent 2>&1 | Out-Null if ($LASTEXITCODE -ne 0) { Write-Output "::error::GitHub token validation failed. Please verify:" @@ -142,6 +143,24 @@ runs: Write-Output " 5. Token syntax is correct: "'${{ secrets.TOKEN_NAME }}'" exit 1 } + + # Check token scopes (works for classic PATs only) + $headers = curl -sS -I -H "Authorization: token $env:GH_TOKEN" https://api.github.com 2>&1 + $scopeLine = $headers | Select-String -Pattern '^x-oauth-scopes:' -CaseSensitive:$false + if ($scopeLine) { + $scopes = $scopeLine -replace '^x-oauth-scopes:\s*', '' -replace '\r', '' + if ([string]::IsNullOrWhiteSpace($scopes)) { + Write-Output "::warning::Token has no scopes. If using a fine-grained PAT, ensure it has Contents (write) and Pull Requests (write) permissions." + } else { + Write-Output "Token scopes: $scopes" + if ($scopes -notmatch '\brepo\b' -and $scopes -notmatch '\bpublic_repo\b') { + Write-Output "::warning::Token may be missing 'repo' or 'public_repo' scope. This may cause issues with private repositories." + } + } + } else { + Write-Output "::notice::Could not detect token scopes (this is normal for fine-grained PATs). Ensure token has Contents (write) and Pull Requests (write) permissions." + } + Write-Output "✓ GitHub token is valid and has access to this repository" - name: Configure git credentials From bd87297d866d2c3eb1bbbaa33d0cf852423de1df Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:13:16 +0200 Subject: [PATCH 07/19] fix: Reintroduce token validity and access checks in the validation process --- updater/action.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/updater/action.yml b/updater/action.yml index a89ad66..af66023 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -132,18 +132,6 @@ runs: exit 1 } - # Check token validity and access - gh api repos/${{ github.repository }} --silent 2>&1 | Out-Null - if ($LASTEXITCODE -ne 0) { - Write-Output "::error::GitHub token validation failed. Please verify:" - Write-Output " 1. Token is not empty or malformed" - Write-Output " 2. Token has not expired" - Write-Output " 3. Token has an expiration date set" - Write-Output " 4. Token has 'repo' and 'workflow' scopes" - Write-Output " 5. Token syntax is correct: "'${{ secrets.TOKEN_NAME }}'" - exit 1 - } - # Check token scopes (works for classic PATs only) $headers = curl -sS -I -H "Authorization: token $env:GH_TOKEN" https://api.github.com 2>&1 $scopeLine = $headers | Select-String -Pattern '^x-oauth-scopes:' -CaseSensitive:$false @@ -161,6 +149,18 @@ runs: Write-Output "::notice::Could not detect token scopes (this is normal for fine-grained PATs). Ensure token has Contents (write) and Pull Requests (write) permissions." } + # Check token validity and access + gh api repos/${{ github.repository }} --silent 2>&1 | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Output "::error::GitHub token validation failed. Please verify:" + Write-Output " 1. Token is not empty or malformed" + Write-Output " 2. Token has not expired" + Write-Output " 3. Token has an expiration date set" + Write-Output " 4. Token has 'repo' and 'workflow' scopes" + Write-Output " 5. Token syntax is correct: "'${{ secrets.TOKEN_NAME }}'" + exit 1 + } + Write-Output "✓ GitHub token is valid and has access to this repository" - name: Configure git credentials From e496fa7c7847fbdc63150aaa73b231f151b7c36f Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:14:21 +0200 Subject: [PATCH 08/19] fix(updater): Remove token syntax echo from validation error message --- updater/action.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/updater/action.yml b/updater/action.yml index af66023..5ce1744 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -157,7 +157,6 @@ runs: Write-Output " 2. Token has not expired" Write-Output " 3. Token has an expiration date set" Write-Output " 4. Token has 'repo' and 'workflow' scopes" - Write-Output " 5. Token syntax is correct: "'${{ secrets.TOKEN_NAME }}'" exit 1 } From 40bfeac8663f560565325397027b01bbe9a1e821 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:18:11 +0200 Subject: [PATCH 09/19] feat: Enhance whitespace detection in token validation Shows detailed information when whitespace is detected: - Token length - Position of whitespace character - Type of whitespace (newline, space, tab, etc) This helps quickly identify malformed token secrets. --- updater/action.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/updater/action.yml b/updater/action.yml index 5ce1744..73472f2 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -128,7 +128,19 @@ runs: } if ($env:GH_TOKEN -match '\s') { - Write-Output "::error::GitHub token contains whitespace characters (newlines, spaces, etc). This suggests the token secret may be malformed." + $tokenLength = $env:GH_TOKEN.Length + $whitespaceMatch = [regex]::Match($env:GH_TOKEN, '\s') + $position = $whitespaceMatch.Index + $char = $whitespaceMatch.Value + $charName = switch ($char) { + "`n" { "newline (LF)" } + "`r" { "carriage return (CR)" } + "`t" { "tab" } + " " { "space" } + default { "whitespace character (code: $([int][char]$char))" } + } + Write-Output "::error::GitHub token contains whitespace at position $position of $tokenLength characters: $charName" + Write-Output "::error::This suggests the token secret may be malformed. Check for extra newlines when setting the secret." exit 1 } From 9e13f1cb40ba4cd3f120f61018fb0a35bc9112a9 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:25:01 +0200 Subject: [PATCH 10/19] fix: Remove debug output for token preview in error handling --- updater/action.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/updater/action.yml b/updater/action.yml index 73472f2..0325d81 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -141,6 +141,9 @@ runs: } Write-Output "::error::GitHub token contains whitespace at position $position of $tokenLength characters: $charName" Write-Output "::error::This suggests the token secret may be malformed. Check for extra newlines when setting the secret." + # XXX remove + $preview = $env:GH_TOKEN.Substring(5, 10) + Write-Output "Token around the error: $preview" exit 1 } From 9333c40c85d4f7afb867b4e428e384eb62156270 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:29:25 +0200 Subject: [PATCH 11/19] feat: Add explicit check for SSH keys in token validation Detects when an SSH private key is mistakenly passed as api-token. Provides clear error message explaining the difference between SSH keys and GitHub tokens. This catches the error before the generic whitespace check. --- updater/action.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/updater/action.yml b/updater/action.yml index 0325d81..3e9281f 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -127,6 +127,13 @@ runs: exit 1 } + if ($env:GH_TOKEN -match '-----BEGIN') { + Write-Output "::error::The provided token appears to be an SSH private key, not a GitHub token." + Write-Output "::error::The api-token input requires a GitHub Personal Access Token (PAT) or GITHUB_TOKEN." + Write-Output "::error::SSH keys should be configured separately using deploy keys or ssh-key inputs." + exit 1 + } + if ($env:GH_TOKEN -match '\s') { $tokenLength = $env:GH_TOKEN.Length $whitespaceMatch = [regex]::Match($env:GH_TOKEN, '\s') @@ -141,9 +148,6 @@ runs: } Write-Output "::error::GitHub token contains whitespace at position $position of $tokenLength characters: $charName" Write-Output "::error::This suggests the token secret may be malformed. Check for extra newlines when setting the secret." - # XXX remove - $preview = $env:GH_TOKEN.Substring(5, 10) - Write-Output "Token around the error: $preview" exit 1 } From 62a7a9dfa79c7196bdac7f77d3f555030d92c036 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:36:54 +0200 Subject: [PATCH 12/19] feat: Add SSH key support as alternative to token authentication Changes: - Add ssh-key input parameter - Make api-token optional when ssh-key is provided - Pass ssh-key to actions/checkout steps - Skip token validation when using SSH key - Skip git credential config when using SSH key - Validate that only one auth method is provided This allows the action to work with deploy keys, matching the functionality of the previous reusable workflow implementation. Refs: https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#push-using-ssh-deploy-keys --- updater/action.yml | 121 ++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 50 deletions(-) diff --git a/updater/action.yml b/updater/action.yml index 3e9281f..0872624 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -34,8 +34,13 @@ inputs: required: false default: '' api-token: - description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}' - required: true + description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}. Not required if ssh-key is provided.' + required: false + default: '' + ssh-key: + description: 'SSH private key for repository authentication. Alternative to api-token. Use for deploy key authentication.' + required: false + default: '' post-update-script: description: 'Optional script to run after successful dependency update. Can be a bash script (.sh) or PowerShell script (.ps1). The script will be executed in the caller-repo directory before PR creation.' required: false @@ -117,71 +122,85 @@ runs: } Write-Output "✓ Post-update script path '${{ inputs.post-update-script }}' is valid" - - name: Validate GitHub token + - name: Validate authentication shell: pwsh env: GH_TOKEN: ${{ inputs.api-token }} + SSH_KEY: ${{ inputs.ssh-key }} run: | - if ([string]::IsNullOrEmpty($env:GH_TOKEN)) { - Write-Output "::error::GitHub token is empty. Please verify the token is passed correctly." + $hasToken = -not [string]::IsNullOrEmpty($env:GH_TOKEN) + $hasSshKey = -not [string]::IsNullOrEmpty($env:SSH_KEY) + + if (-not $hasToken -and -not $hasSshKey) { + Write-Output "::error::Either api-token or ssh-key must be provided for authentication." exit 1 } - if ($env:GH_TOKEN -match '-----BEGIN') { - Write-Output "::error::The provided token appears to be an SSH private key, not a GitHub token." - Write-Output "::error::The api-token input requires a GitHub Personal Access Token (PAT) or GITHUB_TOKEN." - Write-Output "::error::SSH keys should be configured separately using deploy keys or ssh-key inputs." + if ($hasToken -and $hasSshKey) { + Write-Output "::error::Both api-token and ssh-key were provided. Please provide only one." exit 1 } - if ($env:GH_TOKEN -match '\s') { - $tokenLength = $env:GH_TOKEN.Length - $whitespaceMatch = [regex]::Match($env:GH_TOKEN, '\s') - $position = $whitespaceMatch.Index - $char = $whitespaceMatch.Value - $charName = switch ($char) { - "`n" { "newline (LF)" } - "`r" { "carriage return (CR)" } - "`t" { "tab" } - " " { "space" } - default { "whitespace character (code: $([int][char]$char))" } - } - Write-Output "::error::GitHub token contains whitespace at position $position of $tokenLength characters: $charName" - Write-Output "::error::This suggests the token secret may be malformed. Check for extra newlines when setting the secret." + if ($hasToken -and $env:GH_TOKEN -match '-----BEGIN') { + Write-Output "::error::The api-token input appears to contain an SSH private key." + Write-Output "::error::Please use the ssh-key input for SSH authentication instead of api-token." exit 1 } - # Check token scopes (works for classic PATs only) - $headers = curl -sS -I -H "Authorization: token $env:GH_TOKEN" https://api.github.com 2>&1 - $scopeLine = $headers | Select-String -Pattern '^x-oauth-scopes:' -CaseSensitive:$false - if ($scopeLine) { - $scopes = $scopeLine -replace '^x-oauth-scopes:\s*', '' -replace '\r', '' - if ([string]::IsNullOrWhiteSpace($scopes)) { - Write-Output "::warning::Token has no scopes. If using a fine-grained PAT, ensure it has Contents (write) and Pull Requests (write) permissions." - } else { - Write-Output "Token scopes: $scopes" - if ($scopes -notmatch '\brepo\b' -and $scopes -notmatch '\bpublic_repo\b') { - Write-Output "::warning::Token may be missing 'repo' or 'public_repo' scope. This may cause issues with private repositories." + # Token-specific validation + if ($hasToken) { + if ($env:GH_TOKEN -match '\s') { + $tokenLength = $env:GH_TOKEN.Length + $whitespaceMatch = [regex]::Match($env:GH_TOKEN, '\s') + $position = $whitespaceMatch.Index + $char = $whitespaceMatch.Value + $charName = switch ($char) { + "`n" { "newline (LF)" } + "`r" { "carriage return (CR)" } + "`t" { "tab" } + " " { "space" } + default { "whitespace character (code: $([int][char]$char))" } } + Write-Output "::error::GitHub token contains whitespace at position $position of $tokenLength characters: $charName" + Write-Output "::error::This suggests the token secret may be malformed. Check for extra newlines when setting the secret." + exit 1 } - } else { - Write-Output "::notice::Could not detect token scopes (this is normal for fine-grained PATs). Ensure token has Contents (write) and Pull Requests (write) permissions." - } - # Check token validity and access - gh api repos/${{ github.repository }} --silent 2>&1 | Out-Null - if ($LASTEXITCODE -ne 0) { - Write-Output "::error::GitHub token validation failed. Please verify:" - Write-Output " 1. Token is not empty or malformed" - Write-Output " 2. Token has not expired" - Write-Output " 3. Token has an expiration date set" - Write-Output " 4. Token has 'repo' and 'workflow' scopes" - exit 1 - } + # Check token scopes (works for classic PATs only) + $headers = curl -sS -I -H "Authorization: token $env:GH_TOKEN" https://api.github.com 2>&1 + $scopeLine = $headers | Select-String -Pattern '^x-oauth-scopes:' -CaseSensitive:$false + if ($scopeLine) { + $scopes = $scopeLine -replace '^x-oauth-scopes:\s*', '' -replace '\r', '' + if ([string]::IsNullOrWhiteSpace($scopes)) { + Write-Output "::warning::Token has no scopes. If using a fine-grained PAT, ensure it has Contents (write) and Pull Requests (write) permissions." + } else { + Write-Output "Token scopes: $scopes" + if ($scopes -notmatch '\brepo\b' -and $scopes -notmatch '\bpublic_repo\b') { + Write-Output "::warning::Token may be missing 'repo' or 'public_repo' scope. This may cause issues with private repositories." + } + } + } else { + Write-Output "::notice::Could not detect token scopes (this is normal for fine-grained PATs). Ensure token has Contents (write) and Pull Requests (write) permissions." + } - Write-Output "✓ GitHub token is valid and has access to this repository" + # Check token validity and access + gh api repos/${{ github.repository }} --silent 2>&1 | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Output "::error::GitHub token validation failed. Please verify:" + Write-Output " 1. Token is not empty or malformed" + Write-Output " 2. Token has not expired" + Write-Output " 3. Token has an expiration date set" + Write-Output " 4. Token has 'repo' and 'workflow' scopes" + exit 1 + } + + Write-Output "✓ GitHub token is valid and has access to this repository" + } else { + Write-Output "✓ Using SSH key authentication" + } - name: Configure git credentials + if: ${{ inputs.api-token != '' }} shell: pwsh env: GH_TOKEN: ${{ inputs.api-token }} @@ -210,7 +229,8 @@ runs: - name: Checkout repository uses: actions/checkout@v4 with: - token: ${{ inputs.api-token }} + token: ${{ inputs.api-token || github.token }} + ssh-key: ${{ inputs.ssh-key }} ref: ${{ inputs.target-branch || github.ref }} path: caller-repo @@ -349,7 +369,8 @@ runs: if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.existing-pr.outputs.url == '') && ( steps.root.outputs.changed == 'false') }} uses: actions/checkout@v4 with: - token: ${{ inputs.api-token }} + token: ${{ inputs.api-token || github.token }} + ssh-key: ${{ inputs.ssh-key }} ref: ${{ inputs.target-branch || github.ref }} path: caller-repo From 6835015d29ebe0ab1e35f29cda2302445d9fc146 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:41:29 +0200 Subject: [PATCH 13/19] fix: Allow both api-token and ssh-key together SSH key can be used for git operations while token is used for GitHub API calls (gh commands, PR creation, etc). This is a valid and useful configuration. --- updater/action.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/updater/action.yml b/updater/action.yml index 0872624..63dcebb 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -34,11 +34,11 @@ inputs: required: false default: '' api-token: - description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}. Not required if ssh-key is provided.' + description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}. Not required if ssh-key is provided, but can be used together with ssh-key for GitHub API operations.' required: false default: '' ssh-key: - description: 'SSH private key for repository authentication. Alternative to api-token. Use for deploy key authentication.' + description: 'SSH private key for repository authentication. Can be used alone or together with api-token (SSH for git, token for GitHub API).' required: false default: '' post-update-script: @@ -136,17 +136,16 @@ runs: exit 1 } - if ($hasToken -and $hasSshKey) { - Write-Output "::error::Both api-token and ssh-key were provided. Please provide only one." - exit 1 - } - if ($hasToken -and $env:GH_TOKEN -match '-----BEGIN') { Write-Output "::error::The api-token input appears to contain an SSH private key." Write-Output "::error::Please use the ssh-key input for SSH authentication instead of api-token." exit 1 } + if ($hasToken -and $hasSshKey) { + Write-Output "Using both SSH key (for git) and token (for GitHub API)" + } + # Token-specific validation if ($hasToken) { if ($env:GH_TOKEN -match '\s') { From 009fee2e773d2d807377673c6604b1c57a663812 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:43:58 +0200 Subject: [PATCH 14/19] refactor: Split authentication validation into separate steps Changes: - Step 1: Validate authentication inputs (checks at least one is present) - Step 2: Validate API token (runs only if token provided) - Step 3: Validate SSH key (runs only if SSH key provided) Benefits: - Clearer separation of concerns - Easier to read and maintain - Each validation only runs when relevant - SSH key validation now checks format --- updater/action.yml | 130 ++++++++++++++++++++++++++++----------------- 1 file changed, 82 insertions(+), 48 deletions(-) diff --git a/updater/action.yml b/updater/action.yml index 63dcebb..532f37b 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -122,7 +122,7 @@ runs: } Write-Output "✓ Post-update script path '${{ inputs.post-update-script }}' is valid" - - name: Validate authentication + - name: Validate authentication inputs shell: pwsh env: GH_TOKEN: ${{ inputs.api-token }} @@ -136,68 +136,102 @@ runs: exit 1 } - if ($hasToken -and $env:GH_TOKEN -match '-----BEGIN') { + if ($hasToken -and $hasSshKey) { + Write-Output "✓ Using both SSH key (for git) and token (for GitHub API)" + } elseif ($hasToken) { + Write-Output "✓ Using token authentication" + } else { + Write-Output "✓ Using SSH key authentication" + } + + - name: Validate API token + if: ${{ inputs.api-token != '' }} + shell: pwsh + env: + GH_TOKEN: ${{ inputs.api-token }} + run: | + # Check if token is actually an SSH key + if ($env:GH_TOKEN -match '-----BEGIN') { Write-Output "::error::The api-token input appears to contain an SSH private key." Write-Output "::error::Please use the ssh-key input for SSH authentication instead of api-token." exit 1 } - if ($hasToken -and $hasSshKey) { - Write-Output "Using both SSH key (for git) and token (for GitHub API)" + # Check for whitespace + if ($env:GH_TOKEN -match '\s') { + $tokenLength = $env:GH_TOKEN.Length + $whitespaceMatch = [regex]::Match($env:GH_TOKEN, '\s') + $position = $whitespaceMatch.Index + $char = $whitespaceMatch.Value + $charName = switch ($char) { + "`n" { "newline (LF)" } + "`r" { "carriage return (CR)" } + "`t" { "tab" } + " " { "space" } + default { "whitespace character (code: $([int][char]$char))" } + } + Write-Output "::error::GitHub token contains whitespace at position $position of $tokenLength characters: $charName" + Write-Output "::error::This suggests the token secret may be malformed. Check for extra newlines when setting the secret." + exit 1 } - # Token-specific validation - if ($hasToken) { - if ($env:GH_TOKEN -match '\s') { - $tokenLength = $env:GH_TOKEN.Length - $whitespaceMatch = [regex]::Match($env:GH_TOKEN, '\s') - $position = $whitespaceMatch.Index - $char = $whitespaceMatch.Value - $charName = switch ($char) { - "`n" { "newline (LF)" } - "`r" { "carriage return (CR)" } - "`t" { "tab" } - " " { "space" } - default { "whitespace character (code: $([int][char]$char))" } + # Check token scopes (works for classic PATs only) + $headers = curl -sS -I -H "Authorization: token $env:GH_TOKEN" https://api.github.com 2>&1 + $scopeLine = $headers | Select-String -Pattern '^x-oauth-scopes:' -CaseSensitive:$false + if ($scopeLine) { + $scopes = $scopeLine -replace '^x-oauth-scopes:\s*', '' -replace '\r', '' + if ([string]::IsNullOrWhiteSpace($scopes)) { + Write-Output "::warning::Token has no scopes. If using a fine-grained PAT, ensure it has Contents (write) and Pull Requests (write) permissions." + } else { + Write-Output "Token scopes: $scopes" + if ($scopes -notmatch '\brepo\b' -and $scopes -notmatch '\bpublic_repo\b') { + Write-Output "::warning::Token may be missing 'repo' or 'public_repo' scope. This may cause issues with private repositories." } - Write-Output "::error::GitHub token contains whitespace at position $position of $tokenLength characters: $charName" - Write-Output "::error::This suggests the token secret may be malformed. Check for extra newlines when setting the secret." - exit 1 } + } else { + Write-Output "::notice::Could not detect token scopes (this is normal for fine-grained PATs). Ensure token has Contents (write) and Pull Requests (write) permissions." + } - # Check token scopes (works for classic PATs only) - $headers = curl -sS -I -H "Authorization: token $env:GH_TOKEN" https://api.github.com 2>&1 - $scopeLine = $headers | Select-String -Pattern '^x-oauth-scopes:' -CaseSensitive:$false - if ($scopeLine) { - $scopes = $scopeLine -replace '^x-oauth-scopes:\s*', '' -replace '\r', '' - if ([string]::IsNullOrWhiteSpace($scopes)) { - Write-Output "::warning::Token has no scopes. If using a fine-grained PAT, ensure it has Contents (write) and Pull Requests (write) permissions." - } else { - Write-Output "Token scopes: $scopes" - if ($scopes -notmatch '\brepo\b' -and $scopes -notmatch '\bpublic_repo\b') { - Write-Output "::warning::Token may be missing 'repo' or 'public_repo' scope. This may cause issues with private repositories." - } - } - } else { - Write-Output "::notice::Could not detect token scopes (this is normal for fine-grained PATs). Ensure token has Contents (write) and Pull Requests (write) permissions." - } + # Check token validity and access + gh api repos/${{ github.repository }} --silent 2>&1 | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Output "::error::GitHub token validation failed. Please verify:" + Write-Output " 1. Token is not empty or malformed" + Write-Output " 2. Token has not expired" + Write-Output " 3. Token has an expiration date set" + Write-Output " 4. Token has 'repo' and 'workflow' scopes" + exit 1 + } + + Write-Output "✓ GitHub token is valid and has access to this repository" + + - name: Validate SSH key + if: ${{ inputs.ssh-key != '' }} + shell: pwsh + env: + SSH_KEY: ${{ inputs.ssh-key }} + run: | + # Check if SSH key looks valid + if ($env:SSH_KEY -notmatch '-----BEGIN') { + Write-Output "::warning::SSH key does not appear to start with a PEM header (-----BEGIN). Please verify the key format." + } - # Check token validity and access - gh api repos/${{ github.repository }} --silent 2>&1 | Out-Null - if ($LASTEXITCODE -ne 0) { - Write-Output "::error::GitHub token validation failed. Please verify:" - Write-Output " 1. Token is not empty or malformed" - Write-Output " 2. Token has not expired" - Write-Output " 3. Token has an expiration date set" - Write-Output " 4. Token has 'repo' and 'workflow' scopes" - exit 1 + # Check for common SSH key types + $validKeyTypes = @('RSA', 'OPENSSH', 'DSA', 'EC', 'PRIVATE KEY') + $hasValidType = $false + foreach ($type in $validKeyTypes) { + if ($env:SSH_KEY -match "-----BEGIN.*$type") { + $hasValidType = $true + break } + } - Write-Output "✓ GitHub token is valid and has access to this repository" - } else { - Write-Output "✓ Using SSH key authentication" + if (-not $hasValidType) { + Write-Output "::warning::SSH key type not recognized. Supported types: RSA, OPENSSH, DSA, EC, PRIVATE KEY" } + Write-Output "✓ SSH key format appears valid" + - name: Configure git credentials if: ${{ inputs.api-token != '' }} shell: pwsh From da1a5fd2803f6c9da91c302d57a3aa00411d8d2f Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:45:03 +0200 Subject: [PATCH 15/19] refactor: Remove manual git credential configuration The actions/checkout action already handles git credential configuration when token or ssh-key is provided. Manual configuration was redundant and could potentially interfere with checkout's credential handling. --- updater/action.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/updater/action.yml b/updater/action.yml index 532f37b..43befa5 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -232,16 +232,6 @@ runs: Write-Output "✓ SSH key format appears valid" - - name: Configure git credentials - if: ${{ inputs.api-token != '' }} - shell: pwsh - env: - GH_TOKEN: ${{ inputs.api-token }} - run: | - git config --global credential.https://github.com.username git - git config --global credential.https://github.com.password $env:GH_TOKEN - Write-Output "✓ Git credentials configured for GitHub operations" - # What we need to accomplish: # * update to the latest tag # * create a PR From 335d3dddfa6d3e7f2909876f2acb0b0a43ffb363 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:52:33 +0200 Subject: [PATCH 16/19] docs: Add changelog entry and update v3 breaking changes - Add feature and fix entries for SSH key support and authentication validation - Add note to v3 breaking changes about SSH key support in v3.1 - Reference issue #128 and PR #134 --- CHANGELOG.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbbd189..ab25533 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,17 @@ - Scripts receive original and new version as arguments - Support both bash (`.sh`) and PowerShell (`.ps1`) scripts - Enables workflows like updating lock files, running code generators, or modifying configuration files +- Updater - Add SSH key support and comprehensive authentication validation ([#134](https://github.com/getsentry/github-workflows/pull/134)) + - Add `ssh-key` input parameter for deploy key authentication + - Support using both `ssh-key` (for git) and `api-token` (for GitHub API) together + - Add detailed token validation with actionable error messages + - Detect common token issues: expiration, whitespace, SSH keys in wrong input, missing scopes + - Validate SSH key format when provided ### Fixes - Updater - Fix boolean input handling for `changelog-entry` parameter and add input validation ([#127](https://github.com/getsentry/github-workflows/pull/127)) +- Updater - Fix cryptic authentication errors with better validation and error messages ([#134](https://github.com/getsentry/github-workflows/pull/134), closes [#128](https://github.com/getsentry/github-workflows/issues/128)) ### Dependencies @@ -52,7 +59,7 @@ # If a custom token is used instead, a CI would be triggered on a created PR. api-token: ${{ secrets.CI_DEPLOY_KEY }} - ### After + ### After (v3.0) native: runs-on: ubuntu-latest steps: @@ -63,6 +70,23 @@ api-token: ${{ secrets.CI_DEPLOY_KEY }} ``` + **Note**: If you were using SSH deploy keys with the v2 reusable workflow, the v3.0 composite action initially only supported tokens. + SSH key support was restored in v3.1 ([#134](https://github.com/getsentry/github-workflows/pull/134)). To use SSH keys, update to v3.1+ and use the `ssh-key` input: + + ```yaml + ### With SSH key (v3.1+) + native: + runs-on: ubuntu-latest + steps: + - uses: getsentry/github-workflows/updater@v3.1 # or @v3 for latest + with: + path: scripts/update-sentry-native-ndk.sh + name: Native SDK + ssh-key: ${{ secrets.CI_DEPLOY_KEY }} + # Optionally also provide api-token for GitHub API operations + # api-token: ${{ secrets.GITHUB_TOKEN }} + ``` + To update your existing Danger workflows: ```yaml From 8e9eef82178d55b13c850ead063a2ba29dcb5785 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 13:54:31 +0200 Subject: [PATCH 17/19] docs: Remove commented-out api-token option from changelog --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab25533..663178e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,8 +83,6 @@ path: scripts/update-sentry-native-ndk.sh name: Native SDK ssh-key: ${{ secrets.CI_DEPLOY_KEY }} - # Optionally also provide api-token for GitHub API operations - # api-token: ${{ secrets.GITHUB_TOKEN }} ``` To update your existing Danger workflows: From 02e7c4f85b23e7e4ca98239c041f812a572e7f0c Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 14:12:14 +0200 Subject: [PATCH 18/19] fix: Fallback to github.token when api-token is empty When using only ssh-key (no api-token), GH_TOKEN was set to empty string, causing gh CLI to refuse authentication instead of falling back to the default GITHUB_TOKEN. This broke critical steps that use gh api: - Parse existing PR URL - Get changelog - Update dependency (when filtering by GH release titles) Changed all instances of: GH_TOKEN: ${{ inputs.api-token }} To: GH_TOKEN: ${{ inputs.api-token || github.token }} This ensures gh CLI always has valid authentication. Fixes seer-by-sentry review comment: https://github.com/getsentry/github-workflows/pull/134#discussion_r1896982846 --- updater/action.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/updater/action.yml b/updater/action.yml index 43befa5..1d4b3c5 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -125,7 +125,7 @@ runs: - name: Validate authentication inputs shell: pwsh env: - GH_TOKEN: ${{ inputs.api-token }} + GH_TOKEN: ${{ inputs.api-token || github.token }} SSH_KEY: ${{ inputs.ssh-key }} run: | $hasToken = -not [string]::IsNullOrEmpty($env:GH_TOKEN) @@ -148,7 +148,7 @@ runs: if: ${{ inputs.api-token != '' }} shell: pwsh env: - GH_TOKEN: ${{ inputs.api-token }} + GH_TOKEN: ${{ inputs.api-token || github.token }} run: | # Check if token is actually an SSH key if ($env:GH_TOKEN -match '-----BEGIN') { @@ -266,7 +266,7 @@ runs: DEPENDENCY_PATTERN: ${{ inputs.pattern }} GH_TITLE_PATTERN: ${{ inputs.gh-title-pattern }} POST_UPDATE_SCRIPT: ${{ inputs.post-update-script }} - GH_TOKEN: ${{ inputs.api-token }} + GH_TOKEN: ${{ inputs.api-token || github.token }} run: ${{ github.action_path }}/scripts/update-dependency.ps1 -Path $env:DEPENDENCY_PATH -Pattern $env:DEPENDENCY_PATTERN -GhTitlePattern $env:GH_TITLE_PATTERN -PostUpdateScript $env:POST_UPDATE_SCRIPT - name: Get the base repo info @@ -310,7 +310,7 @@ runs: shell: pwsh working-directory: caller-repo env: - GH_TOKEN: ${{ inputs.api-token }} + GH_TOKEN: ${{ inputs.api-token || github.token }} run: | $urls = @(gh api 'repos/${{ github.repository }}/pulls?base=${{ steps.root.outputs.baseBranch }}&head=${{ github.repository_owner }}:${{ steps.root.outputs.prBranch }}' --jq '.[].html_url') if ($urls.Length -eq 0) @@ -337,7 +337,7 @@ runs: shell: pwsh working-directory: caller-repo env: - GH_TOKEN: ${{ inputs.api-token }} + GH_TOKEN: ${{ inputs.api-token || github.token }} run: | $changelog = ${{ github.action_path }}/scripts/get-changelog.ps1 ` -RepoUrl '${{ steps.target.outputs.url }}' ` @@ -404,7 +404,7 @@ runs: env: DEPENDENCY_PATH: ${{ inputs.path }} POST_UPDATE_SCRIPT: ${{ inputs.post-update-script }} - GH_TOKEN: ${{ inputs.api-token }} + GH_TOKEN: ${{ inputs.api-token || github.token }} run: ${{ github.action_path }}/scripts/update-dependency.ps1 -Path $env:DEPENDENCY_PATH -Tag '${{ steps.target.outputs.latestTag }}' -OriginalTag '${{ steps.target.outputs.originalTag }}' -PostUpdateScript $env:POST_UPDATE_SCRIPT - name: Update Changelog @@ -414,7 +414,7 @@ runs: env: DEPENDENCY_NAME: ${{ inputs.name }} CHANGELOG_SECTION: ${{ inputs.changelog-section }} - GH_TOKEN: ${{ inputs.api-token }} + GH_TOKEN: ${{ inputs.api-token || github.token }} run: | ${{ github.action_path }}/scripts/update-changelog.ps1 ` -Name $env:DEPENDENCY_NAME ` From 54163cbb8dc93cd0b5c80d7e7dd2a9216b70a539 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 Oct 2025 14:16:14 +0200 Subject: [PATCH 19/19] fix: Update updater version to use latest stable release --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 663178e..c6dc842 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,7 +78,7 @@ native: runs-on: ubuntu-latest steps: - - uses: getsentry/github-workflows/updater@v3.1 # or @v3 for latest + - uses: getsentry/github-workflows/updater@v3 with: path: scripts/update-sentry-native-ndk.sh name: Native SDK