Daily Docker Image Cleanup #134
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "Daily Docker Image Cleanup" | |
| on: | |
| schedule: | |
| # Run daily at 2:00 AM UTC | |
| - cron: "0 2 * * *" | |
| workflow_dispatch: | |
| jobs: | |
| cleanup-images: | |
| name: Clean up old Docker images | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Delete old Docker images | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const repo = context.repo.repo; | |
| const owner = context.repo.owner; | |
| try { | |
| // Get all package versions for the repository | |
| const packages = await github.rest.packages.getAllPackageVersionsForPackageOwnedByOrg({ | |
| package_type: 'container', | |
| package_name: repo, | |
| org: owner, | |
| per_page: 100 | |
| }); | |
| console.log(`Found ${packages.data.length} package versions to evaluate`); | |
| let deletedCount = 0; | |
| for (const pkg of packages.data) { | |
| const tags = pkg.metadata?.container?.tags || []; | |
| const createdAt = new Date(pkg.created_at); | |
| const threeMonthsAgo = new Date(); | |
| threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3); | |
| // Delete if no tags (untagged packages should be removed) | |
| if (tags.length === 0) { | |
| console.log(`Deleting untagged package ${pkg.id} (created: ${createdAt.toISOString()})`); | |
| try { | |
| await github.rest.packages.deletePackageVersionForOrg({ | |
| package_type: 'container', | |
| package_name: repo, | |
| org: owner, | |
| package_version_id: pkg.id | |
| }); | |
| console.log(`Deleted untagged package ${pkg.id}`); | |
| deletedCount++; | |
| } catch (deleteError) { | |
| console.log(`Failed to delete untagged package ${pkg.id}:`, deleteError.message); | |
| } | |
| continue; | |
| } | |
| // Check if any tag matches our keep patterns | |
| const hasKeepTags = tags.some(tag => | |
| tag === 'latest' || | |
| tag.startsWith('pr-') || | |
| tag.startsWith('sha-') | |
| ); | |
| // Keep the package if it has keep tags AND is less than 3 months old | |
| const shouldKeep = hasKeepTags && createdAt > threeMonthsAgo; | |
| if (shouldKeep) { | |
| console.log(`Keeping package ${pkg.id} with tags: ${tags.join(', ')} (created: ${createdAt.toISOString()})`); | |
| continue; | |
| } | |
| if (hasKeepTags && createdAt <= threeMonthsAgo) { | |
| console.log(`Deleting old package ${pkg.id} with tags: ${tags.join(', ')} (created: ${createdAt.toISOString()}, older than 3 months)`); | |
| } else { | |
| console.log(`Deleting package ${pkg.id} with non-keep tags: ${tags.join(', ')}`); | |
| } | |
| // Delete the package version | |
| try { | |
| await github.rest.packages.deletePackageVersionForOrg({ | |
| package_type: 'container', | |
| package_name: repo, | |
| org: owner, | |
| package_version_id: pkg.id | |
| }); | |
| console.log(`Deleted package ${pkg.id} with tags: ${tags.join(', ')}`); | |
| deletedCount++; | |
| } catch (deleteError) { | |
| console.log(`Failed to delete package ${pkg.id}:`, deleteError.message); | |
| } | |
| } | |
| console.log(`Cleanup completed. Deleted ${deletedCount} package versions.`); | |
| } catch (error) { | |
| // Handle org vs user owned repos | |
| if (error.status === 404) { | |
| console.log('Trying user-owned package cleanup...'); | |
| try { | |
| const packages = await github.rest.packages.getAllPackageVersionsForPackageOwnedByUser({ | |
| package_type: 'container', | |
| package_name: repo, | |
| username: owner, | |
| per_page: 100 | |
| }); | |
| console.log(`Found ${packages.data.length} package versions to evaluate`); | |
| let deletedCount = 0; | |
| for (const pkg of packages.data) { | |
| const tags = pkg.metadata?.container?.tags || []; | |
| const createdAt = new Date(pkg.created_at); | |
| const threeMonthsAgo = new Date(); | |
| threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3); | |
| // Delete if no tags (untagged packages should be removed) | |
| if (tags.length === 0) { | |
| console.log(`Deleting untagged package ${pkg.id} (created: ${createdAt.toISOString()})`); | |
| try { | |
| await github.rest.packages.deletePackageVersionForUser({ | |
| package_type: 'container', | |
| package_name: repo, | |
| username: owner, | |
| package_version_id: pkg.id | |
| }); | |
| console.log(`Deleted untagged package ${pkg.id}`); | |
| deletedCount++; | |
| } catch (deleteError) { | |
| console.log(`Failed to delete untagged package ${pkg.id}:`, deleteError.message); | |
| } | |
| continue; | |
| } | |
| // Check if any tag matches our keep patterns | |
| const hasKeepTags = tags.some(tag => | |
| tag === 'latest' || | |
| tag.startsWith('pr-') || | |
| tag.startsWith('sha-') | |
| ); | |
| // Keep the package if it has keep tags AND is less than 3 months old | |
| const shouldKeep = hasKeepTags && createdAt > threeMonthsAgo; | |
| if (shouldKeep) { | |
| console.log(`Keeping package ${pkg.id} with tags: ${tags.join(', ')} (created: ${createdAt.toISOString()})`); | |
| continue; | |
| } | |
| if (hasKeepTags && createdAt <= threeMonthsAgo) { | |
| console.log(`Deleting old package ${pkg.id} with tags: ${tags.join(', ')} (created: ${createdAt.toISOString()}, older than 3 months)`); | |
| } else { | |
| console.log(`Deleting package ${pkg.id} with non-keep tags: ${tags.join(', ')}`); | |
| } | |
| // Delete the package version | |
| try { | |
| await github.rest.packages.deletePackageVersionForUser({ | |
| package_type: 'container', | |
| package_name: repo, | |
| username: owner, | |
| package_version_id: pkg.id | |
| }); | |
| console.log(`Deleted package ${pkg.id} with tags: ${tags.join(', ')}`); | |
| deletedCount++; | |
| } catch (deleteError) { | |
| console.log(`Failed to delete package ${pkg.id}:`, deleteError.message); | |
| } | |
| } | |
| console.log(`Cleanup completed. Deleted ${deletedCount} package versions.`); | |
| } catch (userError) { | |
| console.log('Error during user-owned package cleanup:', userError.message); | |
| throw userError; | |
| } | |
| } else { | |
| console.log('Error during org-owned package cleanup:', error.message); | |
| throw error; | |
| } | |
| } |