Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ updates:
schedule:
interval: yearly
open-pull-requests-limit: 0
cooldown:
default-days: 7
10 changes: 7 additions & 3 deletions .github/workflows/weekly-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
cache: ''

- name: Setup pnpm
uses: pnpm/action-setup@58e6119fe4f3092a76a7771efb55e04d25b6b26f # v5
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5

- name: Install dependencies
shell: bash
Expand Down Expand Up @@ -69,6 +69,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
Expand All @@ -77,7 +78,7 @@ jobs:
cache: ''

- name: Setup pnpm
uses: pnpm/action-setup@58e6119fe4f3092a76a7771efb55e04d25b6b26f # v5
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5

- name: Install dependencies
shell: bash
Expand Down Expand Up @@ -134,8 +135,11 @@ jobs:
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
shell: bash
env:
GH_TOKEN: ${{ github.token }}
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
run: git push origin "$BRANCH_NAME"
run: |
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git"
git push origin "$BRANCH_NAME"

- name: Create Pull Request
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
Expand Down
3 changes: 3 additions & 0 deletions .github/zizmor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
rules:
secrets-outside-env:
disable: true
121 changes: 41 additions & 80 deletions src/releases/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,27 +103,6 @@ export const SOCKET_BTM_REPO = {

const logger = getDefaultLogger()

// In-memory TTL cache for GitHub API responses.
// Prevents redundant API calls during parallel asset downloads and
// repeated builds (e.g., pre-commit hooks) within the same process.
const API_CACHE_TTL_MS = 5 * 60_000
const _apiCache = new Map<string, { data: unknown; expiresAt: number }>()

function getCachedApiResponse<T>(key: string): T | undefined {
const entry = _apiCache.get(key)
if (entry && entry.expiresAt > Date.now()) {
return entry.data as T
}
if (entry) {
_apiCache.delete(key)
}
return undefined
}

function setCachedApiResponse(key: string, data: unknown): void {
_apiCache.set(key, { data, expiresAt: Date.now() + API_CACHE_TTL_MS })
}

let _fs: typeof import('node:fs') | undefined
let _path: typeof import('node:path') | undefined

Expand Down Expand Up @@ -383,42 +362,32 @@ export async function getLatestRelease(
// Create matcher function if pattern provided.
const isMatch = assetPattern ? createAssetMatcher(assetPattern) : undefined

const cacheKey = `releases:${owner}/${repo}`

return await pRetry(
async () => {
// Check in-memory cache first to avoid redundant API calls.
let releases = getCachedApiResponse<
Array<{
tag_name: string
published_at: string
assets: Array<{ name: string }>
}>
>(cacheKey)

if (!releases) {
// Fetch recent releases (100 should cover all tool releases).
const response = await httpRequest(
`https://api.github.com/repos/${owner}/${repo}/releases?per_page=100`,
{
headers: getAuthHeaders(),
},
)

if (!response.ok) {
throw new Error(`Failed to fetch releases: ${response.status}`)
}
// Fetch recent releases (100 should cover all tool releases).
const response = await httpRequest(
`https://api.github.com/repos/${owner}/${repo}/releases?per_page=100`,
{
headers: getAuthHeaders(),
},
)

try {
releases = JSON.parse(response.body.toString('utf8'))
} catch (cause) {
throw new Error(
`Failed to parse GitHub releases response from https://api.github.com/repos/${owner}/${repo}/releases`,
{ cause },
)
}
if (!response.ok) {
throw new Error(`Failed to fetch releases: ${response.status}`)
}

setCachedApiResponse(cacheKey, releases)
let releases: Array<{
tag_name: string
published_at: string
assets: Array<{ name: string }>
}>
try {
releases = JSON.parse(response.body.toString('utf8'))
} catch (cause) {
throw new Error(
`Failed to parse GitHub releases response from https://api.github.com/repos/${owner}/${repo}/releases`,
{ cause },
)
}

// Filter releases matching the tool prefix.
Expand Down Expand Up @@ -514,37 +483,29 @@ export async function getReleaseAssetUrl(
? (input: string) => input === assetPattern
: createAssetMatcher(assetPattern as AssetPattern)

const cacheKey = `release:${owner}/${repo}:${tag}`

return await pRetry(
async () => {
// Check in-memory cache first to avoid redundant API calls.
let release = getCachedApiResponse<{
assets: Array<{ name: string; browser_download_url: string }>
}>(cacheKey)

if (!release) {
const response = await httpRequest(
`https://api.github.com/repos/${owner}/${repo}/releases/tags/${tag}`,
{
headers: getAuthHeaders(),
},
)

if (!response.ok) {
throw new Error(`Failed to fetch release ${tag}: ${response.status}`)
}
const response = await httpRequest(
`https://api.github.com/repos/${owner}/${repo}/releases/tags/${tag}`,
{
headers: getAuthHeaders(),
},
)

try {
release = JSON.parse(response.body.toString('utf8'))
} catch (cause) {
throw new Error(
`Failed to parse GitHub release response for tag ${tag}`,
{ cause },
)
}
if (!response.ok) {
throw new Error(`Failed to fetch release ${tag}: ${response.status}`)
}

setCachedApiResponse(cacheKey, release)
let release: {
assets: Array<{ name: string; browser_download_url: string }>
}
try {
release = JSON.parse(response.body.toString('utf8'))
} catch (cause) {
throw new Error(
`Failed to parse GitHub release response for tag ${tag}`,
{ cause },
)
}

// Find the matching asset.
Expand Down
Loading