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
232 changes: 232 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
name: build

on:
push:
branches: [master, main]
pull_request:
workflow_dispatch:

permissions:
contents: read

env:
SOURCE_ARCHIVE: vendor/source/optipng-0.7.8.tar.gz

jobs:
build:
name: ${{ matrix.id }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- id: darwin-x64
runner: macos-14
cross_arch: x86_64
- id: darwin-arm64
runner: macos-14
- id: linux-x64
runner: ubuntu-latest
- id: linux-x64-musl
runner: ubuntu-latest
musl: true
- id: linux-arm64
runner: ubuntu-24.04-arm
- id: linux-arm64-musl
runner: ubuntu-24.04-arm
musl: true
- id: win32-x64
runner: windows-latest
windows: true
- id: win32-arm64
runner: windows-11-arm
windows: true
msys2_msystem: CLANGARM64
msys2_install: mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-zlib make

steps:
- uses: actions/checkout@v5

- name: Read version
id: version
shell: bash
run: |
v=$(node -p "require('./package.json').version")
echo "version=$v" >> "$GITHUB_OUTPUT"
echo "tag=v$v" >> "$GITHUB_OUTPUT"

# ── macOS ──
- name: Build (macOS)
if: startsWith(matrix.runner, 'macos')
env:
CROSS_ARCH: ${{ matrix.cross_arch }}
run: |
set -euo pipefail
tmp=$(mktemp -d)
tar -xzf "$SOURCE_ARCHIVE" -C "$tmp"
cd "$tmp"/optipng-*
# optipng has a custom (non-autotools) configure script that does
# NOT understand --host=. CFLAGS/LDFLAGS env vars with -arch
# x86_64 are enough — Apple clang produces x64 binaries and
# Rosetta 2 (preinstalled on macos-14) runs configure's test
# programs.
if [ -n "${CROSS_ARCH:-}" ]; then
export CFLAGS="-arch $CROSS_ARCH"
export LDFLAGS="-arch $CROSS_ARCH"
fi
./configure --with-system-zlib
make -j"$(sysctl -n hw.ncpu)"
strip src/optipng/optipng
mkdir -p "$GITHUB_WORKSPACE/vendor"
cp src/optipng/optipng "$GITHUB_WORKSPACE/vendor/"

# ── Linux glibc ──
- name: Build (Linux glibc)
if: startsWith(matrix.runner, 'ubuntu') && !matrix.musl
run: |
set -euo pipefail
sudo apt-get update -qq
sudo apt-get install -qq -y build-essential zlib1g-dev
tmp=$(mktemp -d)
tar -xzf "$SOURCE_ARCHIVE" -C "$tmp"
cd "$tmp"/optipng-*
./configure --with-system-zlib
make -j"$(nproc)"
strip src/optipng/optipng
mkdir -p "$GITHUB_WORKSPACE/vendor"
cp src/optipng/optipng "$GITHUB_WORKSPACE/vendor/"

# ── Linux musl (build + test inside the same alpine container) ──
- name: Build + test (Linux musl via Alpine docker)
if: matrix.musl
run: |
set -euo pipefail
docker run --rm \
-v "$PWD":/work -w /work \
node:24-alpine sh -c '
set -e
apk add --no-cache build-base zlib-dev tar
tmp=$(mktemp -d)
tar -xzf vendor/source/optipng-0.7.8.tar.gz -C "$tmp"
cd "$tmp"/optipng-*
./configure --with-system-zlib
make -j"$(nproc)"
strip src/optipng/optipng
mkdir -p /work/vendor
cp src/optipng/optipng /work/vendor/
cd /work
npm install --ignore-scripts
npx ava --timeout=120s
'

# ── Windows MSYS2 (x64 mingw OR arm64 clang) ──
- name: Set up MSYS2
if: matrix.windows
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msys2_msystem || 'MINGW64' }}
update: true
install: ${{ matrix.msys2_install || 'mingw-w64-x86_64-gcc mingw-w64-x86_64-zlib make' }}

- name: Build (Windows MSYS2)
if: matrix.windows
shell: msys2 {0}
run: |
set -euo pipefail
tmp=$(mktemp -d)
tar -xzf vendor/source/optipng-0.7.8.tar.gz -C "$tmp"
cd "$tmp"/optipng-*
# optipng's custom configure doesn't accept `LDFLAGS=...` as a
# positional arg; pass it through the environment instead.
LDFLAGS="-static -static-libgcc" ./configure --with-system-zlib
make -j"$(nproc)"
strip src/optipng/optipng.exe
mkdir -p "$GITHUB_WORKSPACE/vendor"
cp src/optipng/optipng.exe "$GITHUB_WORKSPACE/vendor/"

- uses: actions/setup-node@v5
if: ${{ !matrix.musl }}
with:
node-version: '24.15.0'

- name: Install npm deps (skipping postinstall — binary already at vendor/)
if: ${{ !matrix.musl }}
shell: bash
run: npm install --ignore-scripts

- name: Test ${{ matrix.id }} binary
if: ${{ !matrix.musl }}
shell: bash
run: |
set -euo pipefail
ls -la vendor/
npx ava --timeout=120s

- name: Tarball
shell: bash
env:
TAG: ${{ steps.version.outputs.tag }}
run: |
set -euo pipefail
mkdir -p artifacts
name="optipng-bin-${TAG}-${{ matrix.id }}.tar.gz"
tar -czf "artifacts/${name}" -C vendor $(ls vendor | grep -E '^optipng(\.exe)?$')
ls -la artifacts/

- uses: actions/upload-artifact@v7
with:
name: bin-${{ matrix.id }}
path: artifacts/*.tar.gz
if-no-files-found: error
retention-days: 30

release:
name: auto-release on version bump
needs: build
if: github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v5

- name: Read version from package.json
id: version
run: |
set -euo pipefail
v=$(node -p "require('./package.json').version")
echo "version=$v" >> "$GITHUB_OUTPUT"
echo "tag=v$v" >> "$GITHUB_OUTPUT"

- name: Check whether tag already exists on origin
id: check_tag
run: |
set -euo pipefail
tag="${{ steps.version.outputs.tag }}"
if git ls-remote --tags origin "refs/tags/$tag" | grep -q .; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "Tag $tag already exists — nothing to release."
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "Tag $tag does not exist — will release."
fi

- if: steps.check_tag.outputs.exists == 'false'
uses: actions/download-artifact@v8
with:
pattern: bin-*
path: artifacts
merge-multiple: true

- if: steps.check_tag.outputs.exists == 'false'
run: ls -la artifacts/

- if: steps.check_tag.outputs.exists == 'false'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.tag }}
target_commitish: ${{ github.sha }}
name: ${{ steps.version.outputs.tag }}
files: artifacts/*.tar.gz
generate_release_notes: true
fail_on_unmatched_files: true
28 changes: 0 additions & 28 deletions .github/workflows/test.yml

This file was deleted.

5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules
vendor/*
!vendor/source
yarn.lock
vendor/optipng*
vendor/temp
.DS_Store
10 changes: 6 additions & 4 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import {spawn} from 'node:child_process';
import process from 'node:process';
import optipng from './index.js';

const input = process.argv.slice(2);

spawn(optipng, input, {stdio: 'inherit'})
.on('exit', process.exit);
const child = spawn(optipng, process.argv.slice(2), {stdio: 'inherit'});
child.on('exit', code => process.exit(code ?? 0));
child.on('error', error => {
console.error(error);
process.exit(1);
});
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import lib from './lib/index.js';
import {BINARY_PATH} from './lib/binary.js';

export default lib.path();
export default BINARY_PATH;
12 changes: 12 additions & 0 deletions lib/binary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import path from 'node:path';
import process from 'node:process';
import {fileURLToPath} from 'node:url';

export const PACKAGE_ROOT = fileURLToPath(new URL('..', import.meta.url));
export const VENDOR_DIR = path.join(PACKAGE_ROOT, 'vendor');

export function binaryName() {
return process.platform === 'win32' ? 'optipng.exe' : 'optipng';
}

export const BINARY_PATH = path.join(VENDOR_DIR, binaryName());
16 changes: 0 additions & 16 deletions lib/filename.js

This file was deleted.

18 changes: 0 additions & 18 deletions lib/index.js

This file was deleted.

Loading
Loading