Skip to content

Commit 0f7e29b

Browse files
committed
feat: add PowerShell script to synchronize .env secrets to GitHub Actions repository secrets.
1 parent ed11a9e commit 0f7e29b

File tree

1 file changed

+35
-23
lines changed

1 file changed

+35
-23
lines changed

scripts/sync_secrets.ps1

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,66 +43,78 @@ if (-not $ghCommand) {
4343
exit 1
4444
}
4545

46-
function gh { & $ghCommand $args }
46+
# Force UTF-8 encoding for pipes to ensure secrets aren't corrupted
47+
$OutputEncoding = [System.Text.Encoding]::UTF8
4748

48-
# Check if logged in
49-
gh auth status
50-
if ($LASTEXITCODE -ne 0) {
51-
Write-Error "Error: Not logged into GitHub CLI. Run 'gh auth login' first."
52-
exit 1
53-
}
49+
# Fix: PowerShell functions don't automatically forward stdin.
50+
# Using $input | ... handles the pipeline gracefully.
51+
function gh { $input | & $ghCommand @args }
5452

5553
Write-Host "Reading secrets from $EnvFile..." -ForegroundColor Cyan
5654

5755
$content = Get-Content $EnvFile -Raw
5856
$lines = $content -split "`r?`n"
5957
$currentKey = $null
6058
$currentValue = @()
59+
$isQuoted = $false
6160

6261
$secrets = @{}
6362

6463
foreach ($line in $lines) {
65-
# Match a new key-value pair. A key must be at the start of the line,
66-
# all uppercase/numbers/underscores, and followed immediately by '='.
67-
if ($line -match "^(?<key>[A-Z0-9_]+)=(?<val>.*)") {
64+
# Match a new key-value pair. Handles optional 'export ' prefix.
65+
if ($line -match '^\s*(?:export\s+)?(?<key>[A-Z0-9_]+)=(?<val>.*)') {
6866
if ($currentKey) {
6967
# Save previous secret
7068
$secrets[$currentKey] = ($currentValue -join "`n").Trim()
7169
}
7270
$currentKey = $Matches.key
7371
$val = $Matches.val
7472

75-
# Check if the value is a single-line quoted value
73+
# Check for quoted multiline start or single-line quoted value
7674
if ($val -match '^"(?<inner>.*)"$') {
77-
$secrets[$currentKey] = $Matches.inner.Replace('\"', '"')
78-
$currentKey = $null # Mark as done immediately
75+
# Single line quoted: strip quotes and handle escapes
76+
$secrets[$currentKey] = $Matches.inner.Replace('\"', '"').Replace('\n', "`n")
77+
$currentKey = $null
7978
$currentValue = @()
79+
} elseif ($val -match '^"(?<inner>.*)$') {
80+
# Multiline quoted start
81+
$currentValue = @($Matches.inner)
82+
$isQuoted = $true
8083
} else {
84+
# Unquoted value
8185
$currentValue = @($val)
86+
$isQuoted = $false
8287
}
8388
} elseif ($currentKey) {
84-
# Continue current secret
85-
$currentValue += $line
89+
if ($isQuoted -and $line -match '^(?<inner>.*)"$') {
90+
# End of multiline quoted value
91+
$currentValue += $Matches.inner
92+
$secrets[$currentKey] = ($currentValue -join "`n").Replace('\"', '"').Replace('\n', "`n").Trim()
93+
$currentKey = $null
94+
$currentValue = @()
95+
$isQuoted = $false
96+
} else {
97+
# Continue current secret
98+
$currentValue += $line
99+
}
86100
}
87101
}
88-
# Final secret
102+
# Final secret if not already closed
89103
if ($currentKey) {
90104
$secrets[$currentKey] = ($currentValue -join "`n").Trim()
91105
}
92106

93-
# Correctly handle multiline quoted values and sync
107+
# Sync secrets
94108
foreach ($key in $secrets.Keys) {
95109
$value = $secrets[$key]
96110

97-
# Final cleanup for multiline quoted strings.
98-
# Use (?s) to allow . to match newlines.
99-
if ($value -match '(?s)^"(?<inner>.*)"$') {
100-
$value = $Matches.inner.Replace('\"', '"')
101-
}
111+
# Validation preview (User requested full view)
112+
$preview = $value.Trim()
102113

103114
Write-Host "Syncing secret: $key... ($( $value.Length ) bytes)" -ForegroundColor Yellow
115+
Write-Host "Value:`n$preview" -ForegroundColor Gray
104116

105-
# Use --body - to read from stdin
117+
# Use --body - to read from stdin. The 'gh' function handles the pipe.
106118
$value | gh secret set $key --body -
107119

108120
if ($LASTEXITCODE -eq 0) {

0 commit comments

Comments
 (0)