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 .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!dist
50 changes: 45 additions & 5 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ jobs:
if-no-files-found: error
retention-days: 1

test:
test-non-linux:
name: "Test ${{ matrix.os }} Python:${{ matrix.python-version }} NodeJS:${{ matrix.nodejs-version }}"
runs-on: ${{ matrix.os }}
needs: [build-wheels]
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
nodejs-version: ['16.15.1', '14.19.3', '18.4.0']
os: [windows-latest, macos-latest]
nodejs-version: ['14.19.3', '16.15.1', '18.4.0']
python-version: ['3.7', '3.8', '3.9', '3.10']

steps:
Expand Down Expand Up @@ -77,7 +77,47 @@ jobs:
pip install dist\nodejs_bin-${{matrix.nodejs-version}}a3-py3-none-win_amd64.whl
- name: Test Package
run:
python -m nodejs --version
python -m nodejs.npm --version
python -W error -m nodejs --version
python -W error -m nodejs.npm --version
python -W error -m nodejs.npx --version
python -W error -m nodejs.corepack --version

test-linux:
name: "Test Docker OS:${{ matrix.os-variant }} Python:${{ matrix.python-version }} NodeJS:${{ matrix.nodejs-version }}"
runs-on: ubuntu-latest
needs: [build-wheels]
strategy:
fail-fast: false
matrix:
os-variant: [alpine, slim-buster, slim-bullseye]
python-version: ['3.7', '3.8', '3.9', '3.10']
nodejs-version: ['14.19.3', '16.15.1', '18.4.0']

steps:
- uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
install: true
- uses: actions/download-artifact@v3
with:
name: nodejs-pip-wheels
path: dist
- name: Docker build
run: |
if [[ ${{ matrix.os-variant }} =~ "alpine" ]]; then
WHEEL_TO_INSTALL=nodejs_bin-${{ matrix.nodejs-version }}a3-py3-none-musllinux_1_1_x86_64.whl
else
WHEEL_TO_INSTALL=nodejs_bin-${{ matrix.nodejs-version }}a3-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl
fi
echo "WHEEL_TO_INSTALL=${WHEEL_TO_INSTALL}"
docker build \
-f Dockerfile \
--build-arg PYTHON_VERSION=${{ matrix.python-version }} \
--build-arg OS_VARIANT=${{ matrix.os-variant }} \
--build-arg WHEEL_TO_INSTALL=${WHEEL_TO_INSTALL} \
.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ nodejs-cmd/*.egg-info
.DS_Store
env*/
__pycache__/
*.py[cod]
*.py[cod]
venv
package.json
node_modules
package-lock.json
28 changes: 28 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
ARG PYTHON_VERSION=3.10
ARG OS_VARIANT=bullseye-slim

FROM python:${PYTHON_VERSION}-${OS_VARIANT}

ARG PYTHON_VERSION
ENV PYTHON_VERSION=${PYTHON_VERSION}
ARG OS_VARIANT
ENV OS_VARIANT=${OS_VARIANT}

# This is required should be supplied as a build-arg
ARG WHEEL_TO_INSTALL
RUN test -n "${WHEEL_TO_INSTALL}" || (echo "Must supply WHEEL_TO_INSTALL as build arg"; exit 1)

COPY dist/${WHEEL_TO_INSTALL} dist/${WHEEL_TO_INSTALL}

# NodeJS needs libstdc++ to be present
# https://github.com/nodejs/unofficial-builds/#builds
RUN if echo "${OS_VARIANT}" | grep -e "alpine"; then \
apk add libstdc++; \
fi

RUN pip install dist/${WHEEL_TO_INSTALL}

RUN python -W error -m nodejs --version
RUN python -W error -m nodejs.npm --version
RUN python -W error -m nodejs.npx --version
RUN python -W error -m nodejs.corepack --version
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ Node.js PyPI distribution

The [nodejs-bin][pypi] Python package redistributes Node.js so that it can be used as a dependency of Python projects. With `nodejs-bin` you can call `nodejs`, `npm` and `npx` from both the [command line](#command-line-usage) and a [Python API](#python-api-usage).

**Note: this is an unofficial Node.js distribution.**
**Note: this is an unofficial Node.js distribution.** However, it _does_ use only official bits distributed by the official NodeJS maintainers from one of the following sources:

* NodeJS official releases: https://nodejs.org/en/download/releases/
* NodeJS "unofficial" builds: https://github.com/nodejs/unofficial-builds/

**This is intended for use within Python virtual environments and containers, it should probably not be used for global installation.**

Expand Down
33 changes: 27 additions & 6 deletions make_wheels.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import hashlib
import pathlib
import urllib.request
import libarchive
from email.message import EmailMessage
Expand Down Expand Up @@ -45,8 +46,17 @@
'linux-x64': 'manylinux_2_12_x86_64.manylinux2010_x86_64',
'linux-armv7l': 'manylinux_2_17_armv7l.manylinux2014_armv7l',
'linux-arm64': 'manylinux_2_17_aarch64.manylinux2014_aarch64',
'linux-x64-musl': 'musllinux_1_1_x86_64'
}

# https://github.com/nodejs/unofficial-builds/
# Versions added here should match the keys above
UNOFFICIAL_NODEJS_BUILDS = {'linux-x64-musl'}

_mismatched_versions = UNOFFICIAL_NODEJS_BUILDS - set(PLATFORMS.keys())
if _mismatched_versions:
raise Exception(f"A version mismatch occurred. Check the usage of {_mismatched_versions}")


class ReproducibleWheelFile(WheelFile):
def writestr(self, zinfo, *args, **kwargs):
Expand Down Expand Up @@ -113,6 +123,11 @@ def write_nodejs_wheel(out_dir, *, node_version, version, platform, archive):
entry_points = {}
init_imports = []

# Create the output directory if it does not exist
out_dir_path = pathlib.Path(out_dir)
if not out_dir_path.exists():
out_dir_path.mkdir(parents=True)

with libarchive.memory_reader(archive) as archive:
for entry in archive:
entry_name = '/'.join(entry.name.split('/')[1:])
Expand Down Expand Up @@ -246,13 +261,16 @@ def main() -> None:
""").encode('ascii')

contents['nodejs/__init__.py'] = (cleandoc("""
import sys
from .node import path as path, main as main, call as call, run as run, Popen as Popen
{init_imports}
if not '-m' in sys.argv:
{init_imports}

__version__ = "{version}"
node_version = "{node_version}"
""")).format(
init_imports='\n'.join(init_imports),
# Note: two space indentation above and below is necessary to align
init_imports='\n '.join(init_imports),
version=version,
node_version=node_version,
).encode('ascii')
Expand Down Expand Up @@ -294,10 +312,13 @@ def make_nodejs_version(node_version, suffix=''):
print('Suffix:', suffix)

for node_platform, python_platform in PLATFORMS.items():
print(f'- Making Wheel for {node_platform}')
node_url = f'https://nodejs.org/dist/v{node_version}/node-v{node_version}-{node_platform}.' + \
('zip' if node_platform.startswith('win-') else 'tar.xz')

filetype = 'zip' if node_platform.startswith('win-') else 'tar.xz'
if node_platform in UNOFFICIAL_NODEJS_BUILDS:
node_url = f'https://unofficial-builds.nodejs.org/download/release/v{node_version}/node-v{node_version}-{node_platform}.{filetype}'
else:
node_url = f'https://nodejs.org/dist/v{node_version}/node-v{node_version}-{node_platform}.{filetype}'

print(f'- Making Wheel for {node_platform} from {node_url}')
try:
with urllib.request.urlopen(node_url) as request:
node_archive = request.read()
Expand Down