Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cda95c2
fix(updater): Add token validation and git credential configuration
vaind Oct 9, 2025
70aad9b
fix: Escape template expression in error message
vaind Oct 9, 2025
06e4c9b
fix(updater): Remove token syntax echo from validation error message
vaind Oct 9, 2025
a3b716f
fix: Improve token validation to detect malformed tokens
vaind Oct 9, 2025
71c67c5
refactor: Use PowerShell for token validation and git config
vaind Oct 9, 2025
7b2af37
feat: Add token scope validation
vaind Oct 9, 2025
bd87297
fix: Reintroduce token validity and access checks in the validation p…
vaind Oct 9, 2025
e496fa7
fix(updater): Remove token syntax echo from validation error message
vaind Oct 9, 2025
40bfeac
feat: Enhance whitespace detection in token validation
vaind Oct 9, 2025
9e13f1c
fix: Remove debug output for token preview in error handling
vaind Oct 9, 2025
9333c40
feat: Add explicit check for SSH keys in token validation
vaind Oct 9, 2025
62a7a9d
feat: Add SSH key support as alternative to token authentication
vaind Oct 9, 2025
6835015
fix: Allow both api-token and ssh-key together
vaind Oct 9, 2025
009fee2
refactor: Split authentication validation into separate steps
vaind Oct 9, 2025
da1a5fd
refactor: Remove manual git credential configuration
vaind Oct 9, 2025
335d3dd
docs: Add changelog entry and update v3 breaking changes
vaind Oct 9, 2025
8e9eef8
docs: Remove commented-out api-token option from changelog
vaind Oct 9, 2025
02e7c4f
fix: Fallback to github.token when api-token is empty
vaind Oct 9, 2025
54163cb
fix: Update updater version to use latest stable release
vaind Oct 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand All @@ -63,6 +70,21 @@
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
with:
path: scripts/update-sentry-native-ndk.sh
name: Native SDK
ssh-key: ${{ secrets.CI_DEPLOY_KEY }}
```

To update your existing Danger workflows:

```yaml
Expand Down
135 changes: 126 additions & 9 deletions updater/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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, but can be used together with ssh-key for GitHub API operations.'
required: false
default: ''
ssh-key:
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:
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
Expand Down Expand Up @@ -117,6 +122,116 @@ runs:
}
Write-Output "✓ Post-update script path '${{ inputs.post-update-script }}' is valid"

- name: Validate authentication inputs
shell: pwsh
env:
GH_TOKEN: ${{ inputs.api-token || github.token }}
SSH_KEY: ${{ inputs.ssh-key }}
run: |
$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 ($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 || github.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
}

# 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
}

# 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 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
}
}

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"

# What we need to accomplish:
# * update to the latest tag
# * create a PR
Expand All @@ -137,7 +252,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

Expand All @@ -150,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
Expand Down Expand Up @@ -194,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)
Expand All @@ -221,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 }}' `
Expand Down Expand Up @@ -276,7 +392,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

Expand All @@ -287,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
Expand All @@ -297,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 `
Expand Down
Loading