diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml new file mode 100644 index 00000000..1d70dba0 --- /dev/null +++ b/.github/workflows/build-and-release.yml @@ -0,0 +1,179 @@ +name: Build and Release + +on: + push: + branches: [ main ] + tags: [ 'v*', 'rc-*' ] + pull_request: + branches: [ main ] + workflow_dispatch: + +permissions: + id-token: write + contents: read + +jobs: + get-version: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - uses: actions/checkout@v4 + - name: Get version + id: version + run: | + BASE_VERSION=$(node -p "require('./package.json').version") + echo "version=$BASE_VERSION" >> $GITHUB_OUTPUT + + build: + needs: [get-version] + timeout-minutes: 30 + strategy: + matrix: + include: + - arch: x86_64 + runner: codebuild-project-awsaws-lambda-nodejs-runtime-interface-client-${{ github.run_id }}-${{ github.run_attempt }} + - arch: aarch64 + runner: codebuild-project-awsaws-lambda-nodejs-runtime-interface-client-${{ github.run_id }}-${{ github.run_attempt }} + runs-on: ${{ matrix.runner }} + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install build dependencies + run: | + apt-get update + apt-get install -y cmake make g++ autotools-dev automake libtool + + - name: Build natively for ${{ matrix.arch }} + run: | + echo "Building for architecture: ${{ matrix.arch }}" + + # Build native dependencies and JavaScript + BUILD=1 npm install + npm run build + + # Verify required files were created + if [ ! -f "dist/rapid-client.node" ] || [ ! -f "dist/index.mjs" ] || [ ! -f "dist/UserFunction.js" ]; then + echo "Error: Required files not found in dist directory" + exit 1 + fi + + # Copy architecture-specific package.json to dist + node -e " + const pkg = require('./package.json'); + pkg.name = 'aws-lambda-ric-${{ matrix.arch }}'; + require('fs').writeFileSync('./dist/package.json', JSON.stringify(pkg, null, 2)); + " + + # Create tarball with only required files + tar -czf aws-lambda-ric-${{ matrix.arch }}-${{ needs.get-version.outputs.version }}.tgz \ + -C dist package.json index.mjs UserFunction.js rapid-client.node + + - name: Generate checksums + run: | + PACKAGE_FILE="aws-lambda-ric-${{ matrix.arch }}-${{ needs.get-version.outputs.version }}.tgz" + sha256sum $PACKAGE_FILE > checksums-${{ matrix.arch }}.sha256 + sha512sum $PACKAGE_FILE > checksums-${{ matrix.arch }}.sha512 + echo "Package: $PACKAGE_FILE (${{ matrix.arch }}) with version: ${{ needs.get-version.outputs.version }}" > checksums-${{ matrix.arch }}.txt + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: package-${{ matrix.arch }}-${{ needs.get-version.outputs.version }} + path: | + aws-lambda-ric-*-${{ needs.get-version.outputs.version }}.tgz + checksums-* + retention-days: 30 + + test: + needs: [get-version, build] + strategy: + matrix: + node-version: [18, 20, 22] + include: + - arch: x86_64 + runner: ubuntu-latest + - arch: aarch64 + runner: ubuntu-latest + runs-on: ${{ matrix.runner }} + steps: + - uses: actions/checkout@v4 + + - name: Run unit tests - Node ${{ matrix.node-version }} (native $(arch)) + run: | + docker build \ + -f test/unit/Dockerfile.nodejs${{ matrix.node-version }}.x \ + -t unit/nodejs.${{ matrix.node-version }}x \ + . + docker run --rm unit/nodejs.${{ matrix.node-version }}x + + publish: + if: startsWith(github.ref, 'refs/tags/') + runs-on: codebuild-project-awsaws-lambda-nodejs-runtime-interface-client-${{ github.run_id }}-${{ github.run_attempt }} + needs: [get-version, build, test] + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - name: Download x86_64 artifacts + uses: actions/download-artifact@v4 + with: + name: package-x86_64-${{ needs.get-version.outputs.version }} + path: ./artifacts/x86_64 + + - name: Download aarch64 artifacts + uses: actions/download-artifact@v4 + with: + name: package-aarch64-${{ needs.get-version.outputs.version }} + path: ./artifacts/aarch64 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Setup NPM authentication + run: | + NPM_TOKEN=$(aws secretsmanager get-secret-value --secret-id aws-lambda-runtimes/github/nodejs/npm-token --query SecretString --output text) + echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc + chmod 0600 .npmrc + + - name: Determine version and publish packages + id: version + run: | + if [[ "${{ github.ref }}" == refs/tags/rc-* ]]; then + RC_NUMBER=${GITHUB_REF#refs/tags/rc-} + PACKAGE_VERSION="${{ needs.get-version.outputs.version }}-rc.${RC_NUMBER}" + echo "package_version=$PACKAGE_VERSION" >> $GITHUB_OUTPUT + echo "is_rc=true" >> $GITHUB_OUTPUT + TAG_FLAG="--tag rc" + else + echo "package_version=${{ needs.get-version.outputs.version }}" >> $GITHUB_OUTPUT + TAG_FLAG="" + fi + + # Build and publish full package to npm + BUILD=1 npm install + npm run build + npm publish $TAG_FLAG --access=public + + - name: Combine checksums + run: | + cat ./artifacts/*/checksums-*.txt > combined-checksums.txt + cat ./artifacts/*/checksums-*.sha256 > combined-checksums.sha256 + cat ./artifacts/*/checksums-*.sha512 > combined-checksums.sha512 + + - name: Create GitHub Release + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v2 + with: + files: | + ./artifacts/*/aws-lambda-ric-*-*.tgz + combined-checksums.* + prerelease: ${{ steps.version.outputs.is_rc }} diff --git a/.npmignore b/.npmignore index af2d322b..35e35f70 100644 --- a/.npmignore +++ b/.npmignore @@ -4,6 +4,8 @@ build src/* # Rapid-client.c to be used with node-gyp !src/rapid-client.cc +# Include built native binary +!dist/rapid-client.node test # Logs diff --git a/README.md b/README.md index 3c6dc8a4..b4b49550 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,39 @@ When modifying dependencies (`package.json`), make sure to: We require package-lock.json to be checked in to ensure consistent installations across development environments. +### Changelog Generation +This project maintains a changelog in `RELEASE.CHANGELOG.md`. The changelog is automatically updated when a new tag is created. + +To manually generate a changelog entry for testing or preview purposes, run: +```shell script +npm run changelog +``` + +To manually update the RELEASE.CHANGELOG.md file with a new entry: +```shell script +npm run changelog -- --update +``` + +### Copyright Headers +All source files in the bin, scripts, src, and test folders must include a copyright header containing both the words "Copyright" and "Amazon.com". The standard header format is: + +``` +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +``` + +To check if your files have proper headers: +```shell script +npm run check-headers +``` + +To automatically add headers to files missing them: +```shell script +npm run add-headers +``` + ### Troubleshooting While running integration tests, you might encounter the Docker Hub rate limit error with the following body: diff --git a/package-lock.json b/package-lock.json index 024e454c..83c0f0d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "eslint": "8.42.0", "eslint-config-prettier": "8.8.0", "eslint-plugin-prettier": "4.2.1", + "glob": "^10.3.10", "husky": "^8.0.3", "lambda-runtime": "file:./src/", "mocha": "^10.8.2", @@ -896,9 +897,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { "node": ">=12" }, @@ -1500,75 +1501,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/cacache/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/cacache/node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/glob": { - "version": "10.2.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.7.tgz", - "integrity": "sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" - }, - "bin": { - "glob": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -2618,19 +2550,19 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "*" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2648,6 +2580,62 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/glob/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "13.20.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", @@ -3159,15 +3147,12 @@ } }, "node_modules/jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=14" - }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -3742,6 +3727,26 @@ "node": "^12.13 || ^14.13 || >=16" } }, + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -3862,6 +3867,27 @@ "node": ">=8" } }, + "node_modules/nyc/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/nyc/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -4094,6 +4120,11 @@ "node": ">=8" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4141,27 +4172,24 @@ } }, "node_modules/path-scurry": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", - "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dependencies": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", - "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/picocolors": { "version": "1.0.0", @@ -4476,6 +4504,26 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4974,6 +5022,27 @@ "node": ">=8" } }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", diff --git a/package.json b/package.json index dbf0360a..345b92f2 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,8 @@ "gypfile": true, "scripts": { "archive": "npx rimraf aws-lambda-ric-*.tgz && npm install && npm run build && npm pack", + "changelog": "node ./scripts/generate-changelog.js", + "changelog:update": "node ./scripts/generate-changelog.js --update", "clean": "npx rimraf build node_modules package-lock.json", "copy-files": "mkdir -p dist && cp src/types/* dist/", "update-deps": "./scripts/update_dependencies.sh", @@ -18,7 +20,10 @@ "format": "npm run format:src && npm run format:test", "format:src": "prettier --check \"src/*.*js\" --write", "format:test": "prettier --check \"test/**/*.*js\" --write", - "lint": "eslint --ext \".js\" src test", + "check-headers": "./scripts/check-headers.sh", + "add-headers": "./scripts/add-headers.sh", + "lint": "eslint --ext \".js\" src test && npm run check-headers", + "fix": "eslint --fix --ext \".js\" src test", "test": "npm run test:unit", "test:unit": "mocha --recursive ./test/unit --reporter ./test/util/StdoutReporter.test", @@ -63,6 +68,7 @@ "eslint": "8.42.0", "eslint-config-prettier": "8.8.0", "eslint-plugin-prettier": "4.2.1", + "husky": "^8.0.3", "lambda-runtime": "file:./src/", "mocha": "^10.8.2", diff --git a/scripts/add-headers.sh b/scripts/add-headers.sh new file mode 100755 index 00000000..4ffea2a9 --- /dev/null +++ b/scripts/add-headers.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -e + +script_dir="$(dirname "$0")" +files_modified=0 + +while IFS= read -r file; do + if ! grep -q 'Copyright.*Amazon\.com' "$file"; then + if [[ "$file" == *.sh ]]; then + sed "s|PLACEHOLDER|$file|" "$script_dir/patches/sh-files.patch" | git apply + else + sed "s|PLACEHOLDER|$file|" "$script_dir/patches/js-files.patch" | git apply + fi + files_modified=$((files_modified + 1)) + fi +done < <(git ls-files 'bin/**' 'scripts/**' 'src/**' 'test/**' | grep -E '\.(js|ts|mjs|mts|jsx|tsx|c|cpp|h|sh)$') + +if [ "$files_modified" -eq 0 ]; then + echo "✓ All files already have copyright headers" +else + echo "✓ Copyright headers added to $files_modified files" + echo "Run 'git diff' to review changes" +fi \ No newline at end of file diff --git a/scripts/check-headers.sh b/scripts/check-headers.sh new file mode 100755 index 00000000..6b69d8b0 --- /dev/null +++ b/scripts/check-headers.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -e + +missing_files=() + +while IFS= read -r file; do + if ! grep -q 'Copyright.*Amazon\.com' "$file"; then + missing_files+=("$file") + fi +done < <(git ls-files 'bin/**' 'scripts/**' 'src/**' 'test/**' | grep -E '\.(js|ts|mjs|mts|jsx|tsx|c|cpp|h|sh)$') + +if [ ${#missing_files[@]} -gt 0 ]; then + echo "❌ Copyright header check failed." + echo "Files missing headers:" + printf ' %s\n' "${missing_files[@]}" + echo + echo "Run 'npm run add-headers' to fix these files." + exit 1 +fi + +echo "✅ All files have proper copyright headers." \ No newline at end of file diff --git a/scripts/generate-changelog.js b/scripts/generate-changelog.js new file mode 100755 index 00000000..858914c0 --- /dev/null +++ b/scripts/generate-changelog.js @@ -0,0 +1,96 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +/** + * Script to generate and update changelog entries in RELEASE.CHANGELOG.md + * It automatically adds new entries when cutting a new tag + */ + +// Parse command line arguments +const args = process.argv.slice(2); +const updateFile = args.includes('--update'); +const outputFile = args.find((arg, i) => arg === '--output' && i + 1 < args.length) + ? args[args.indexOf('--output') + 1] + : null; + +// Get the last tag +let lastTag; +try { + lastTag = execSync('git describe --tags --abbrev=0').toString().trim(); + console.log(`Last tag: ${lastTag}`); +} catch (error) { + console.error('No tags found.'); + process.exit(1); +} + +// Get current version from package.json +const packageJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')); +const version = packageJson.version; +console.log(`Current version: ${version}`); + +// Get commits since last tag +const gitLogFormat = '%h %s'; +const gitLog = execSync(`git log ${lastTag}..HEAD --pretty=format:"${gitLogFormat}"`).toString(); + +// Filter and format commits +const commits = gitLog.split('\n') + .filter(line => line.trim()) + .filter(line => { + // Filter out version commits + const message = line.substring(line.indexOf(' ') + 1); + return !/^v?\d+\.\d+\.\d+(-.*)?$/.test(message) && + !message.startsWith('working on ') && + !message.startsWith('Working on '); + }) + .map(line => { + const hash = line.substring(0, line.indexOf(' ')); + const message = line.substring(line.indexOf(' ') + 1); + + // Check for PR number in message + const prMatch = message.match(/\(#(\d+)\)|\(https:\/\/github\.com\/[^\/]+\/[^\/]+\/pull\/(\d+)\)/); + const prNumber = prMatch ? prMatch[1] || prMatch[2] : null; + + if (prNumber) { + return `- ${message} ([#${prNumber}](https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/pull/${prNumber}))`; + } else { + return `- ${message}`; + } + }); + +// Generate changelog entry +const today = new Date(); +const formattedDate = `${today.toLocaleString('en-US', { month: 'short' })} ${today.getDate()}, ${today.getFullYear()}`; + +const changelogEntry = `### ${formattedDate} +\`${version}\` +${commits.join('\n')} + +`; + +// Output the changelog entry +if (outputFile) { + fs.writeFileSync(outputFile, changelogEntry); + console.log(`Changelog entry written to ${outputFile}`); +} else { + console.log('\nChangelog entry:'); + console.log(changelogEntry); +} + +// Update RELEASE.CHANGELOG.md if requested +if (updateFile) { + const changelogPath = path.join(process.cwd(), 'RELEASE.CHANGELOG.md'); + if (fs.existsSync(changelogPath)) { + const existingChangelog = fs.readFileSync(changelogPath, 'utf8'); + fs.writeFileSync(changelogPath, changelogEntry + existingChangelog); + console.log(`Updated ${changelogPath} with new entry`); + } else { + fs.writeFileSync(changelogPath, changelogEntry); + console.log(`Created ${changelogPath} with new entry`); + } +} \ No newline at end of file diff --git a/scripts/patches/js-files.patch b/scripts/patches/js-files.patch new file mode 100644 index 00000000..6f476e01 --- /dev/null +++ b/scripts/patches/js-files.patch @@ -0,0 +1,7 @@ +--- /dev/null ++++ b/PLACEHOLDER +@@ -0,0 +1,4 @@ ++/* ++Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. ++SPDX-License-Identifier: Apache-2.0 ++*/ \ No newline at end of file diff --git a/scripts/patches/sh-files.patch b/scripts/patches/sh-files.patch new file mode 100644 index 00000000..add3c434 --- /dev/null +++ b/scripts/patches/sh-files.patch @@ -0,0 +1,6 @@ +--- /dev/null ++++ b/PLACEHOLDER +@@ -0,0 +1,3 @@ ++#!/bin/bash ++# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. ++# SPDX-License-Identifier: Apache-2.0 \ No newline at end of file diff --git a/scripts/test_local.sh b/scripts/test_local.sh new file mode 100755 index 00000000..0674abd1 --- /dev/null +++ b/scripts/test_local.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail +trap 'echo "Error on line $LINENO"; exit 1' ERR + +echo "Running local unit tests for AWS Lambda NodeJS RIC" + +# Check if Docker is available +if ! command -v docker &> /dev/null; then + echo "Error: Docker is not installed or not in PATH" + echo "Please install Docker Desktop: https://www.docker.com/products/docker-desktop/" + exit 1 +fi + +# Check if Docker daemon is running +if ! docker info &> /dev/null; then + echo "Error: Docker daemon is not running" + echo "Please start Docker Desktop and wait for it to initialize" + exit 1 +fi + +# Function to run unit tests for specific Node version +run_unit_tests() { + local node_version=$1 + echo "Running unit tests for Node.js $node_version..." + docker build -f test/unit/Dockerfile.nodejs${node_version}.x -t unit/nodejs.${node_version}x . || { + echo "Build failed for Node.js $node_version" + exit 1 + } + docker run --rm unit/nodejs.${node_version}x || { + echo "Tests failed for Node.js $node_version" + exit 1 + } +} + +# Parse command line arguments +case "${1:-all}" in + "unit") + NODE_VERSION=${2:-"20"} + run_unit_tests $NODE_VERSION + ;; + "all") + echo "Running unit tests for all Node versions..." + for version in 18 20 22; do + run_unit_tests $version + done + ;; + *) + echo "Usage: $0 [unit|all] [node_version]" + echo "Examples:" + echo " $0 unit 20 # Run unit tests for Node 20" + echo " $0 all # Run unit tests for all Node versions" + exit 1 + ;; +esac diff --git a/src/build.js b/src/build.js index cb7baf48..f7642f5d 100644 --- a/src/build.js +++ b/src/build.js @@ -28,22 +28,29 @@ const buildOneSet = (target) => { target, }); - // Keep backward compatibility for Node14 - if (process.version.startsWith('v14')) { - build({ - ...shared, - format: 'cjs', - entryPoints: ['UserFunction.js'], - banner: { - js: '(function (){', - }, - footer: { - js: '})();', - }, - outfile: `../dist/UserFunction.js`, - target, - }); - } + // Always build UserFunction.js + build({ + ...shared, + format: 'cjs', + entryPoints: ['UserFunction.js'], + banner: { + js: '(function (){', + }, + footer: { + js: '})();', + }, + outfile: `../dist/UserFunction.js`, + target, + }); + + // Copy rapid-client + fs.mkdirSync(`../dist`, { + recursive: true, + }); + fs.copyFileSync( + '../build/Release/rapid-client.node', + `../dist/rapid-client.node`, + ); }; -buildOneSet('node14.21.3'); +buildOneSet('node16.20.2'); diff --git a/src/types/awslambda.d.ts b/src/types/awslambda.d.ts index 1c5153ed..e87e0ee9 100644 --- a/src/types/awslambda.d.ts +++ b/src/types/awslambda.d.ts @@ -1,3 +1,8 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + export class HttpResponseStream { static from(underlyingStream: any, prelude: any): any; } diff --git a/src/types/index.d.mts b/src/types/index.d.mts index e08f6b0d..6f588ce5 100644 --- a/src/types/index.d.mts +++ b/src/types/index.d.mts @@ -1 +1,6 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + export function run(appRootOrHandler: any, handler?: string): Promise; \ No newline at end of file