### Install dependencies and build project Source: https://github.com/calibreapp/image-actions/blob/main/CONTRIBUTING.md Install project dependencies and build the project using npm commands. ```bash npm install ``` ```bash npm run build ``` ```bash npm run watch ``` -------------------------------- ### Image Compression Job Configuration Source: https://github.com/calibreapp/image-actions/blob/main/README.md Sets up the build job to run on ubuntu-latest. It includes conditional logic to ensure the action runs only on the main repository and for pull requests originating from the same repository. ```yaml jobs: build: name: calibreapp/image-actions runs-on: ubuntu-latest # Only run on main repo on and PRs that match the main repo. if: | github.repository == 'example/example_repo' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) steps: - name: Checkout Branch uses: actions/checkout@v3 - name: Compress Images id: calibre uses: calibreapp/image-actions@main with: # For non-Pull Requests, run in compressOnly mode and we'll PR after. compressOnly: ${{ github.event_name != 'pull_request' }} ``` -------------------------------- ### Build Docker image Source: https://github.com/calibreapp/image-actions/blob/main/CONTRIBUTING.md Confirm a successful Docker build for the image-actions project. ```bash docker build -t calibreapp/image-actions:dev . ``` -------------------------------- ### Fork PR Handling via Push to Main (No PAT) Source: https://context7.com/calibreapp/image-actions/llms.txt This alternative approach for handling fork PRs avoids the need for a PAT. It utilizes a catchup job triggered on a push to the main branch, compressing images from fork PRs and opening a new PR if necessary. ```yaml on: push: branches: [main] paths: ['**.jpg', '**.jpeg', '**.png', '**.webp', '**.avif'] jobs: compress-on-merge: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Compress Images from Fork PRs id: calibre uses: calibreapp/image-actions@main with: compressOnly: 'true' - name: Open Compression PR if Needed if: steps.calibre.outputs.markdown != '' uses: peter-evans/create-pull-request@v4 with: title: Compressed Images branch-suffix: timestamp commit-message: Compress Images body: ${{ steps.calibre.outputs.markdown }} ``` -------------------------------- ### Basic GitHub Actions Workflow for Image Compression Source: https://github.com/calibreapp/image-actions/blob/main/README.md Set up this workflow to automatically compress images when JPG, JPEG, PNG, WebP, or AVIF files are added or changed in a pull request. It also supports manual runs via workflow_dispatch. ```yaml name: Compress Images on: pull_request: # Run Image Actions when JPG, JPEG, PNG, WebP or AVIF files are added or changed. # See https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#onpushpull_requestpaths for reference. paths: - '**.jpg' - '**.jpeg' - '**.png' - '**.webp' - '**.avif' workflow_dispatch: jobs: build: # Only run on Pull Requests within the same repository, and not from forks. if: github.event.pull_request.head.repo.full_name == github.repository name: calibreapp/image-actions permissions: write-all runs-on: ubuntu-latest steps: - name: Checkout Repo uses: actions/checkout@v4 - name: Compress PR Images uses: calibreapp/image-actions@main ``` -------------------------------- ### Run project tests Source: https://github.com/calibreapp/image-actions/blob/main/CONTRIBUTING.md Verify that the project checks are still passing after making changes. ```bash npm run test ``` -------------------------------- ### Combined Workflow: Scheduled, On-Demand, and PR Compression Source: https://context7.com/calibreapp/image-actions/llms.txt This comprehensive workflow combines PR compression with push-to-main catchup, manual dispatch, and a weekly schedule. It uses conditional logic to determine whether to commit directly (for PR events) or open a new PR (for all other events), ensuring images are compressed across different triggers. ```yaml name: Compress Images on: pull_request: paths: ['**.jpg', '**.jpeg', '**.png', '**.webp', '**.avif'] push: branches: [main] paths: ['**.jpg', '**.jpeg', '**.png', '**.webp', '**.avif'] workflow_dispatch: schedule: - cron: '00 23 * * 0' # 11 PM every Sunday jobs: build: name: calibreapp/image-actions runs-on: ubuntu-latest if: | github.repository == 'myorg/myrepo' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) steps: - name: Checkout Branch uses: actions/checkout@v4 - name: Compress Images id: calibre uses: calibreapp/image-actions@main with: # On PR events: commit + comment. On all others: compress only. compressOnly: ${{ github.event_name != 'pull_request' }} - name: Create Pull Request if: | github.event_name != 'pull_request' && steps.calibre.outputs.markdown != '' uses: peter-evans/create-pull-request@v4 with: title: Auto Compress Images branch-suffix: timestamp commit-message: Compress Images body: ${{ steps.calibre.outputs.markdown }} ``` -------------------------------- ### Compress PR Images and Output Markdown Source: https://context7.com/calibreapp/image-actions/llms.txt This snippet demonstrates compressing images in a standard Pull Request scenario and making the markdown summary available as an output. The `markdown` output can be used by subsequent steps, such as echoing the summary. ```yaml - name: Compress PR Images id: image-step uses: calibreapp/image-actions@main - name: Print Markdown Summary run: echo "${{ steps.image-step.outputs.markdown }}" ``` -------------------------------- ### Compress Images on Demand or Schedule Source: https://github.com/calibreapp/image-actions/blob/main/README.md This workflow triggers on demand or on a schedule to compress images and opens a pull request if optimizations are found. It requires the `peter-evans/create-pull-request` action. ```yaml name: Compress Images on: workflow_dispatch: schedule: - cron: '00 23 * * 0' jobs: build: name: calibreapp/image-actions runs-on: ubuntu-latest steps: - name: Checkout Repo uses: actions/checkout@v3 - name: Compress Images id: calibre uses: calibreapp/image-actions@main with: compressOnly: true - name: Create New Pull Request If Needed if: steps.calibre.outputs.markdown != '' uses: peter-evans/create-pull-request@v4 with: title: Compressed Images Nightly branch-suffix: timestamp commit-message: Compressed Images body: ${{ steps.calibre.outputs.markdown }} ``` -------------------------------- ### Control Image Quality Settings in Image Actions Source: https://github.com/calibreapp/image-actions/blob/main/README.md Customize the compression quality for different image formats by specifying these arguments in your workflow. Ensure values are strings as shown. ```yaml with: jpegQuality: '85' jpegProgressive: false pngQuality: '80' webpQuality: '85' avifQuality: '75' ``` -------------------------------- ### Create Pull Request for Compressed Images Source: https://github.com/calibreapp/image-actions/blob/main/README.md This step is executed only when the event is not a pull request and the image compression step produced output. It uses the 'create-pull-request' action to open a new PR with the compressed images. ```yaml - name: Create Pull Request # If it's not a Pull Request then commit any changes as a new PR. if: | github.event_name != 'pull_request' && steps.calibre.outputs.markdown != '' uses: peter-evans/create-pull-request@v4 with: title: Auto Compress Images branch-suffix: timestamp commit-message: Compress Images body: ${{ steps.calibre.outputs.markdown }} ``` -------------------------------- ### Skip Commit and Summary with compressOnly Option Source: https://github.com/calibreapp/image-actions/blob/main/README.md Set `compressOnly` to `true` to prevent the action from committing optimized images or posting a summary comment. This is useful if you need to handle these steps manually, especially for forks. ```yaml with: compressOnly: true ``` -------------------------------- ### Action Inputs — Quality Configuration Source: https://context7.com/calibreapp/image-actions/llms.txt Configure compression quality levels per format. Values are integers 1–100 passed as strings. `jpegProgressive` enables interlaced (progressive) JPEG encoding. These settings map directly to sharp's output options. ```yaml - name: Compress PR Images uses: calibreapp/image-actions@main with: jpegQuality: '80' # default: 85 (sharp JPEG quality) jpegProgressive: 'true' # default: false (interlaced scan) pngQuality: '75' # default: 80 (sharp PNG quality) webpQuality: '80' # default: 85 (sharp WebP quality) avifQuality: '65' # default: 75 (sharp AVIF quality) # Config used at runtime (logged by getConfig()): # { # "jpeg": { "quality": 80, "progressive": true, "chromaSubsampling": "4:4:4" }, # "png": { "quality": 75, "compressionLevel": 9 }, # "webp": { "quality": 80, "smartSubsample": true }, # "avif": { "quality": 65 } # } ``` -------------------------------- ### Action Input — `minPctChange` Source: https://context7.com/calibreapp/image-actions/llms.txt The minimum percentage size reduction required before a compressed image is written to disk and committed. Images not meeting the threshold are reported but not committed. Defaults to '5' (5%). ```yaml - name: Compress PR Images uses: calibreapp/image-actions@main with: minPctChange: '2.5' # commit if at least 2.5% smaller # Internal calculation per image: # const percentChange = ((beforeStats - afterStats) / beforeStats) * 100 # const compressionWasSignificant = percentChange >= MIN_PCT_CHANGE # // true → image written to disk, added to optimisedImages[] # // false → image added to unoptimisedImages[], not committed ``` -------------------------------- ### Core Image Compression Pipeline Source: https://context7.com/calibreapp/image-actions/llms.txt Discovers images, compresses them using sharp, calculates byte size changes, and writes files only if significant savings are achieved. Returns a sorted list of processed images, capped at 500 by byte savings. ```typescript // Simplified internal flow of processImages() const config = await getConfig() // reads Action inputs const imagePaths = await discoverImages() // PR diff or full repo glob for (const imgPath of imagePaths) { const beforeStats = (await stat(imgPath)).size const { data, info } = await sharp(imgPath) .toFormat('jpeg', { quality: 85, progressive: false, chromaSubsampling: '4:4:4' }) .toBuffer({ resolveWithObject: true }) const percentChange = ((beforeStats - info.size) / beforeStats) * 100 if (percentChange >= 5 /* minPctChange */) { await writeFile(imgPath, data) // overwrite original with optimised version optimisedImages.push({ name, path, beforeStats, afterStats: info.size, percentChange, compressionWasSignificant: true }) } else { unoptimisedImages.push({ ..., compressionWasSignificant: false }) } } // Result sorted by largest byte savings first, limited to 500: return { optimisedImages: sortedOptimisedImages.slice(0, 500), unoptimisedImages, metrics: { bytesSaved: 46080, percentChange: -18.4 } } ``` -------------------------------- ### Configure Image Compression Workflow Source: https://github.com/calibreapp/image-actions/blob/main/README.md Defines the triggers for the image compression workflow, including pull requests, pushes to main, manual dispatch, and scheduled runs. It specifies which file types will trigger the action. ```yaml on: pull_request: paths: - '**.jpg' - '**.jpeg' - '**.png' - '**.webp' - '**.avif' push: branches: - main paths: - '**.jpg' - '**.jpeg' - '**.png' - '**.webp' - '**.avif' workflow_dispatch: schedule: - cron: '00 23 * * 0' ``` -------------------------------- ### Create a new Git branch Source: https://github.com/calibreapp/image-actions/blob/main/CONTRIBUTING.md Before making changes, create a new branch for your work using Git. ```bash git checkout -b my-branch-name ``` -------------------------------- ### Compress Images Only (No Commit/PR) Source: https://context7.com/calibreapp/image-actions/llms.txt Use `compressOnly: 'true'` to compress images without committing changes or posting a PR comment. The markdown summary is exported as an output variable for downstream steps. This mode is essential for scheduled runs, `workflow_dispatch`, and handling fork PRs. ```yaml - name: Compress Images id: calibre uses: calibreapp/image-actions@main with: compressOnly: 'true' - name: Create Pull Request if Images Were Optimised if: steps.calibre.outputs.markdown != '' uses: peter-evans/create-pull-request@v4 with: title: Compressed Images Nightly branch-suffix: timestamp commit-message: Compressed Images body: ${{ steps.calibre.outputs.markdown }} ``` -------------------------------- ### Basic Pull Request Compression Workflow Source: https://context7.com/calibreapp/image-actions/llms.txt This is the minimal configuration to automatically compress images added or changed in any Pull Request. It checks out the repo, detects changed image files, compresses them, commits them back, and posts a summary comment. ```yaml # .github/workflows/compress-images.yml name: Compress Images on: pull_request: paths: - '**.jpg' - '**.jpeg' - '**.png' - '**.webp' - '**.avif' workflow_dispatch: jobs: build: # Only run on PRs within the same repo (not forks) to allow commits if: github.event.pull_request.head.repo.full_name == github.repository name: calibreapp/image-actions permissions: write-all runs-on: ubuntu-latest steps: - name: Checkout Repo uses: actions/checkout@v4 - name: Compress PR Images uses: calibreapp/image-actions@main # Expected: optimised images are committed to the PR branch and a comment is # posted on the PR showing a table of before/after sizes and % improvements. ``` -------------------------------- ### Set Minimum Percentage Change for Compression Source: https://github.com/calibreapp/image-actions/blob/main/README.md Adjust the `minPctChange` option to control the minimum size reduction required before an optimized image is committed. This helps prevent unnecessary re-compressions. Accepts a numerical value as a string. ```yaml with: minPctChange: '2.5' ``` -------------------------------- ### Compress Images on Push to Main Branch Source: https://github.com/calibreapp/image-actions/blob/main/README.md This workflow compresses images on pushes to the main branch and creates a pull request if any images are modified. It's useful for handling images from forked repositories. ```yaml name: Compress Images on Push to main branch on: push: branches: - main paths: - '**.jpg' - '**.jpeg' - '**.png' - '**.webp' - '**.avif' jobs: build: name: calibreapp/image-actions runs-on: ubuntu-latest steps: - name: Checkout Repo uses: actions/checkout@v3 - name: Compress Images id: calibre uses: calibreapp/image-actions@main with: compressOnly: true - name: Create New Pull Request If Needed if: steps.calibre.outputs.markdown != '' uses: peter-evans/create-pull-request@v4 with: title: Compressed Images branch-suffix: timestamp commit-message: Compressed Images body: ${{ steps.calibre.outputs.markdown }} ``` -------------------------------- ### Compress PR Images with GITHUB_TOKEN Source: https://github.com/calibreapp/image-actions/blob/main/README.md This snippet shows how to compress images within a Pull Request using the default `GITHUB_TOKEN`. Note that the default token has limitations with forked repositories. ```yaml - name: Compress PR Images uses: calibreapp/image-actions@main with: # `GITHUB_TOKEN` is automatically generated by GitHub and scoped only to the repository that is currently running the action. By default, the action can’t update Pull Requests initiated from forked repositories. # See https://docs.github.com/en/actions/reference/authentication-in-a-workflow and https://help.github.com/en/articles/virtual-environments-for-github-actions#token-permissions GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` -------------------------------- ### Image Discovery: PR vs. Repository Glob Source: https://context7.com/calibreapp/image-actions/llms.txt Uses GitHub REST API for PR changes or falls back to globbing the entire workspace. Filters by supported extensions and ignores removed files or specified paths. ```typescript // getChangedImages.ts — PR-scoped discovery (returns null on failure/no PR) // Supported extensions: jpeg, jpg, png, webp, avif const { data: files } = await api.rest.pulls.listFiles({ owner: 'myorg', repo: 'myrepo', pull_number: 42 }) // files filtered to: // ext in ['jpeg','jpg','png','webp','avif'] // status !== 'removed' // !minimatch(filename, ignorePath) for each ignorePath ``` ```typescript // getRepositoryImages.ts — full-repo fallback const imagePaths = await glob( `${repoDir}/**/*.{jpeg,jpg,png,webp,avif}`, { ignore: resolvedIgnorePaths, nodir: true, follow: false, dot: true } ) // Returns absolute paths; processImages() converts them back to relative // paths for committing via the GitHub Git Trees API. ``` -------------------------------- ### Ignore Specific Paths in Image Compression Source: https://github.com/calibreapp/image-actions/blob/main/README.md Use the `ignorePaths` argument with minimatch glob patterns to exclude certain directories or files from compression. Separate multiple patterns with commas. ```yaml with: ignorePaths: 'node_modules/**,build' ``` -------------------------------- ### Fork PR Handling with Personal Access Token Source: https://context7.com/calibreapp/image-actions/llms.txt This option addresses the limitation of `GITHUB_TOKEN` not being able to write to fork branches by using a Personal Access Token (PAT). It's crucial to understand the security implications of exposing a PAT to fork-sourced workflow runs. ```yaml - name: Compress PR Images uses: calibreapp/image-actions@main with: GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} # WARNING: PAT tokens have broader permissions than GITHUB_TOKEN. # Only use if you understand the security implications of exposing # a PAT to fork-sourced workflow runs. ``` -------------------------------- ### Action Input — `ignorePaths` Source: https://context7.com/calibreapp/image-actions/llms.txt Exclude paths from processing using comma-separated minimatch glob patterns. Defaults to `node_modules/**`. Applies to both PR-diff image list and full-repository scan fallback. ```yaml - name: Compress PR Images uses: calibreapp/image-actions@main with: # Ignore test fixtures, build output, and a specific vendor directory ignorePaths: 'node_modules/**,__tests__/example-images/**,public/vendor/**' # Internally the action runs: # minimatch(filename, 'node_modules/**') // true → skip # minimatch(filename, '__tests__/example-images/**') // true → skip # minimatch(filename, 'public/vendor/**') // true → skip # Any image path not matching these patterns is processed normally. ``` -------------------------------- ### Commit Optimized Images via Git Tree API Source: https://context7.com/calibreapp/image-actions/llms.txt Commits optimized images without a git push by creating blobs, a new tree, a commit, and updating the PR branch ref. Throttled to respect GitHub API rate limits. ```typescript // Throttle: max 10 GitHub API requests per 10 seconds const throttle = pThrottle({ limit: 10, interval: 10000 }) // 1. Create blobs for all optimised images for (const image of optimisedImages) { const content = await readFile(image.path, { encoding: 'base64' }) const blob = await api.git.createBlob({ owner, repo, content, encoding: 'base64' }) treeBlobs.push({ path: image.name, type: 'blob', mode: '100644', sha: blob.data.sha }) } // 2. Build a new Git tree on top of the current HEAD tree const newTree = await api.git.createTree({ owner, repo, base_tree: headTreeSha, tree: treeBlobs }) // 3. Create a commit const commit = await api.git.createCommit({ owner, repo, message: 'Optimised images with calibre/image-actions', tree: newTree.data.sha, parents: [mostRecentCommitSHA] }) // 4. Update the PR branch ref to point at the new commit await api.git.updateRef({ owner, repo, ref: `heads/${headRef}`, sha: commit.data.sha }) // → commit.data is returned and its .sha is used to generate visual diff URLs ``` -------------------------------- ### Generate PR Comment Markdown Source: https://context7.com/calibreapp/image-actions/llms.txt Renders EJS templates for PR comments, conditionally including diff links based on commit SHA. Outputs markdown via `core.setOutput`. ```typescript // Diff URL format (used when commitSha is present): // ///pull//commits/?short_path=#diff- const fileId = crypto.createHash('md5').update('assets/hero.jpg').digest('hex') // → 'a1b2c3d4e5f6...' const url = `/myorg/myrepo/pull/42/commits/abc1234?short_path=a1b2c3#diff-a1b2c3d4e5f6...` // Template variables passed to EJS: { overallPercentageSaved: '18.4', // string, already .toFixed(1) overallBytesSaved: '102.3 KB', // humanised via Intl.NumberFormat optimisedImages: [ // max 25 displayed in PR comment { name: 'assets/hero.jpg', formattedBeforeStats: '250.0 KB', formattedAfterStats: '204.1 KB', formattedPercentChange: '-18.4%', diffUrl: '/myorg/myrepo/pull/42/commits/abc1234?... } ], unoptimisedImages: [...], showSummary: false, // true when >25 images optimised totalOptimisedCount: 3 } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.