diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 85b3a12..137a3c8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,3 +8,5 @@ updates: schedule: interval: yearly open-pull-requests-limit: 0 + cooldown: + default-days: 7 diff --git a/.github/workflows/weekly-update.yml b/.github/workflows/weekly-update.yml index d3f052b..60c6d6e 100644 --- a/.github/workflows/weekly-update.yml +++ b/.github/workflows/weekly-update.yml @@ -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 @@ -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 @@ -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 @@ -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' diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000..39d1b18 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,3 @@ +rules: + secrets-outside-env: + disable: true diff --git a/src/releases/github.ts b/src/releases/github.ts index f4aa212..19672e6 100644 --- a/src/releases/github.ts +++ b/src/releases/github.ts @@ -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() - -function getCachedApiResponse(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 @@ -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. @@ -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.