From 6969276327f198b17ec8f2e44f5069449072d6e1 Mon Sep 17 00:00:00 2001 From: PeterNigh Date: Tue, 28 Apr 2026 14:31:20 -0400 Subject: [PATCH] fix: Resolve multiple customer-blocking bugs (#126, #131, #132) (#134) * fix: Resolve multiple customer-blocking bugs (#126, #131, #132) Fix class-transformer version mismatch (0.3.1 -> 0.5.1) causing silent deserialization failures in generated projects. Remove @ts-ignore from Integer interface so consumers with skipLibCheck:false can compile. Fix timezone-dependent test failure in log-delivery tests. Bump lib version to 1.0.7. Closes #126, #132 * ci: Modernize CI/CD workflows and tooling Replace macos-12 (removed by GitHub) with macos-13. Drop EOL Python 3.8/3.9, add 3.12. Bump python_requires from >=3.8 to >=3.10. Bump all GitHub Actions to v4. Fix upload-artifact unique name requirement. Fix pre-commit eslint hook: replace mirrors-eslint with local hook using project node_modules to prevent version drift. Bump flake8 5.0.4 -> 7.1.2 and bandit 1.7.1 -> 1.8.3 for Python 3.12 compat. Add PyPI release workflow for automated plugin publishing (requires PYPI_API_KEY_CLOUDFORMATION_CLI_TYPESCRIPT_PLUGIN secret). Add pyproject.toml for PEP 517 build system declaration. Install cloudformation-cli from GitHub master in CI to pick up pkg_resources fix not yet published to PyPI. Bump plugin version to 1.0.5. Closes #131 --- .github/workflows/cd.yml | 32 ++++----- .github/workflows/ci.yml | 45 ++++++------ .github/workflows/pypi-release.yaml | 25 +++++++ .gitignore | 2 + .pre-commit-config.yaml | 17 +++-- package.json | 2 +- pyproject.toml | 3 + python/rpdk/typescript/__init__.py | 2 +- python/rpdk/typescript/templates/package.json | 2 +- setup.py | 5 +- src/interface.ts | 71 ++++++++++--------- tests/lib/log-delivery.test.ts | 8 +-- 12 files changed, 123 insertions(+), 91 deletions(-) create mode 100644 .github/workflows/pypi-release.yaml create mode 100644 pyproject.toml diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index e87853c5..fd38e96a 100755 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,4 +1,3 @@ ---- # Continuous Delivery (Release) name: cd @@ -12,8 +11,8 @@ jobs: name: Prepare for NPM runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 20 registry-url: https://registry.npmjs.org/ @@ -28,7 +27,7 @@ jobs: npm pack - name: Upload NPM Artifacts id: upload_npm - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: package-npm path: cfn-rpdk-${{ env.VERSION }}.tgz @@ -36,23 +35,20 @@ jobs: delivery-python: name: Prepare for PyPI runs-on: ubuntu-latest - strategy: - matrix: - python: [3.8] steps: - - uses: actions/checkout@v3 - - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python }} + python-version: "3.12" - name: Install Dependencies and Package Project id: installing run: | python -m pip install --upgrade pip setuptools wheel python3 setup.py sdist bdist_wheel - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: - name: dist-py${{ matrix.python }} + name: dist-python path: dist delivery-github: @@ -60,15 +56,15 @@ jobs: needs: [delivery-nodejs, delivery-python] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Download NPM Artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: package-npm - - name: Download Python 3.8 Artifacts - uses: actions/download-artifact@v3 + - name: Download Python Artifacts + uses: actions/download-artifact@v4 with: - name: dist-py3.8 + name: dist-python path: dist/ - name: List Artifacts run: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c36fd97..fab2b974 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,3 @@ ---- # Continous Integration name: ci @@ -8,17 +7,29 @@ jobs: build: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: "3.12" + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + - name: Install and Build + run: | + npm ci --include=optional + npm run build + - name: TypeScript Tests + run: | + npx jest --ci os_build: runs-on: ${{ matrix.os }} strategy: matrix: os: - ubuntu-latest - - macos-12 # Later versions of ARM-based macOS runners fail because the hypervisor framework required for Docker is not supported - python: [ "3.8", "3.9", "3.10", "3.11"] + - macos-13 + python: [ "3.10", "3.11", "3.12"] node: [ 20 ] env: SAM_CLI_TELEMETRY: "0" @@ -30,14 +41,14 @@ jobs: PIP_LOG_FILE: /tmp/pip.log HOMEBREW_NO_AUTO_UPDATE: 1 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Update Homebrew and save docker version if: runner.os == 'macOS' run: | brew tap homebrew/core cat "$(brew --repository)/Library/Taps/homebrew/homebrew-core/Formula/d/docker.rb" > .github/brew-formulae - name: Configure Homebrew docker cache files - uses: actions/cache@v3 + uses: actions/cache@v4 if: runner.os == 'macOS' with: path: | @@ -51,18 +62,12 @@ jobs: run: | brew install docker --cask brew install colima - # Docker engine is no longer available because of licensing - # Alternative Colima is part of the github macOS runner - # SAM v1.47.0+ needed for colima support, unable to use Python 3.6 colima start - # Ensure colima is configured for later user echo "DOCKER_HOST=unix://$HOME/.colima/default/docker.sock" >> $GITHUB_ENV - # Verify Docker docker ps docker --version - # Verify colima colima status - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} cache: 'pip' @@ -72,19 +77,19 @@ jobs: mkdir "$LOG_PATH" pip install --upgrade pip pip install --upgrade setuptools wheel aws-sam-cli -r https://raw.githubusercontent.com/aws-cloudformation/cloudformation-cli/master/requirements.txt + pip install git+https://github.com/aws-cloudformation/cloudformation-cli.git@master pip install . - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} cache: 'npm' - name: Install Dependencies Node.js id: install_nodejs - # Touch needed because of https://github.com/aws/aws-cli/issues/2639 run: | npm ci --include=optional find ./node_modules/* -mtime +10950 -exec touch {} \; npm run build - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ~/.cache/pre-commit/ key: ${{ matrix.os }}-${{ env.pythonLocation }}${{ hashFiles('.pre-commit-config.yaml') }} @@ -100,9 +105,9 @@ jobs: bash codecov.sh -f coverage/ts/coverage-final.json -F unittests -n codecov-typescript - name: Upload Coverage Artifacts id: upload_coverage - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: coverage + name: coverage-${{ matrix.os }}-py${{ matrix.python }} path: coverage/ - name: Run Integration Tests id: integration_testing @@ -140,7 +145,7 @@ jobs: - name: Upload Debug Artifacts id: upload_logs if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: debug-logs + name: debug-logs-${{ matrix.os }}-py${{ matrix.python }} path: ${{ env.LOG_PATH }} diff --git a/.github/workflows/pypi-release.yaml b/.github/workflows/pypi-release.yaml new file mode 100644 index 00000000..912695d3 --- /dev/null +++ b/.github/workflows/pypi-release.yaml @@ -0,0 +1,25 @@ +# This workflow will release project to PyPI +name: CloudFormation CLI TypeScript Plugin Release +on: + release: + types: [published] +jobs: + build: + if: endsWith(github.ref, '-plugin') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: 3.11 + - name: Install dependencies + run: | + pip install --upgrade wheel twine + - name: Package project + run: | + python setup.py sdist bdist_wheel + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_KEY_CLOUDFORMATION_CLI_TYPESCRIPT_PLUGIN }} diff --git a/.gitignore b/.gitignore index 227448a8..61e4be8c 100644 --- a/.gitignore +++ b/.gitignore @@ -141,3 +141,5 @@ node_modules/ coverage/ *.iml +# Kiro IDE +.kiro/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 713a733d..4287217d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: - id: check-merge-conflict # - id: check-yaml # have jinja yml templates so skipping this - repo: https://github.com/pycqa/flake8 - rev: "5.0.4" + rev: "7.1.2" hooks: - id: flake8 additional_dependencies: @@ -45,19 +45,18 @@ repos: - id: python-check-mock-methods - id: python-no-log-warn - repo: https://github.com/PyCQA/bandit - rev: 1.7.1 + rev: 1.8.3 hooks: - id: bandit files: "^python/" - - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.26.0 + - repo: local hooks: - id: eslint - args: [--fix] - types: [] - files: (.*.js$|.*.ts$) - additional_dependencies: - - eslint@8.21.0 + name: eslint + description: Run eslint from local node_modules + entry: npx eslint --fix + language: system + files: \.(js|ts)$ - repo: local hooks: - id: pylint-local diff --git a/package.json b/package.json index 42485849..bff0480a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@amazon-web-services-cloudformation/cloudformation-cli-typescript-lib", - "version": "1.0.6", + "version": "1.0.7", "description": "The CloudFormation Resource Provider Development Kit (RPDK) allows you to author your own resource providers that can be used by CloudFormation. This plugin library helps to provide runtime bindings for the execution of your providers by CloudFormation.", "private": false, "main": "dist/index.js", diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..4e24dc1e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=68.0", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/python/rpdk/typescript/__init__.py b/python/rpdk/typescript/__init__.py index affdd604..e54d9925 100644 --- a/python/rpdk/typescript/__init__.py +++ b/python/rpdk/typescript/__init__.py @@ -1,5 +1,5 @@ import logging -__version__ = "1.0.4" +__version__ = "1.0.5" logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/python/rpdk/typescript/templates/package.json b/python/rpdk/typescript/templates/package.json index f644accb..c760bdb8 100644 --- a/python/rpdk/typescript/templates/package.json +++ b/python/rpdk/typescript/templates/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "{{lib_name}}": "{{lib_path}}", - "class-transformer": "0.3.1" + "class-transformer": "0.5.1" }, "devDependencies": { "@types/node": "^20.0.0", diff --git a/setup.py b/setup.py index a26d836d..2e5acb1c 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ def find_version(*file_paths): # package_data -> use MANIFEST.in instead include_package_data=True, zip_safe=True, - python_requires=">=3.8", + python_requires=">=3.10", install_requires=[ "cloudformation-cli>=0.1.14", "zipfile38>=0.0.3,<0.2", @@ -60,10 +60,9 @@ def find_version(*file_paths): "Topic :: Software Development :: Code Generators", "Operating System :: OS Independent", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ], keywords="Amazon Web Services AWS CloudFormation", ) diff --git a/src/interface.ts b/src/interface.ts index d4ba07e0..446ff8ef 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -66,9 +66,7 @@ export interface Callable, T> { (...args: R): T; } -// @ts-ignore -// eslint-disable-next-line -interface Integer extends BigInt { +interface Integer { /** * Defines the default JSON representation of * Integer (BigInt) to be a number. @@ -78,11 +76,12 @@ interface Integer extends BigInt { /** Returns the primitive value of the specified object. */ valueOf(): integer; + toString(radix?: number): string; + readonly [Symbol.toStringTag]: 'Integer'; } -// @ts-ignore -interface IntegerConstructor extends BigIntConstructor { +interface IntegerConstructor { (value?: bigint | integer | boolean | number | string): bigint; readonly prototype: Integer; /** @@ -96,36 +95,40 @@ interface IntegerConstructor extends BigIntConstructor { /** * Wrapper with additional JSON serialization for bigint type */ -// @ts-ignore -export const Integer: IntegerConstructor = new Proxy(BigInt, { - // @ts-ignore - apply( - target: IntegerConstructor, - _thisArg: unknown, - argArray?: unknown[] - ): integer { - target.prototype.toJSON = function (): number { - return Number(this.valueOf()); - }; - const isSafeInteger = (value: bigint): boolean => { - if ( - value && - (value < BigInt(Number.MIN_SAFE_INTEGER) || - value > BigInt(Number.MAX_SAFE_INTEGER)) - ) { - return false; +export const Integer: IntegerConstructor = new Proxy( + BigInt as unknown as IntegerConstructor, + { + apply( + target: IntegerConstructor, + _thisArg: unknown, + argArray: unknown[] + ): integer { + target.prototype.toJSON = function (): number { + return Number(this.valueOf()); + }; + const isSafeInteger = (value: bigint): boolean => { + if ( + value && + (value < BigInt(Number.MIN_SAFE_INTEGER) || + value > BigInt(Number.MAX_SAFE_INTEGER)) + ) { + return false; + } + return true; + }; + target.isSafeInteger = isSafeInteger; + const value = target( + ...(argArray as [bigint | integer | boolean | number | string]) + ); + if (value && !isSafeInteger(value)) { + throw new RangeError( + `Value is not a safe integer: ${value.toString()}` + ); } - return true; - }; - target.isSafeInteger = isSafeInteger; - // @ts-expect-error argArray is unknown - const value = target(...argArray); - if (value && !isSafeInteger(value)) { - throw new RangeError(`Value is not a safe integer: ${value.toString()}`); - } - return value; - }, -}) as IntegerConstructor; + return value; + }, + } +) as IntegerConstructor; export enum Action { Create = 'CREATE', diff --git a/tests/lib/log-delivery.test.ts b/tests/lib/log-delivery.test.ts index fd0a73b1..1b321446 100644 --- a/tests/lib/log-delivery.test.ts +++ b/tests/lib/log-delivery.test.ts @@ -1271,11 +1271,11 @@ describe('when delivering logs', () => { loggerProxy.addLogPublisher(s3Logger); loggerProxy.log('count: [%d]', 5.12); - loggerProxy.log('timestamp: [%s]', new Date('2020-01-01')); + loggerProxy.log('timestamp: [%s]', new Date('2020-01-01').toISOString()); - loggerProxy.log('timestamp: [%s]', new Date('2020-01-02')); - loggerProxy.log('timestamp: [%s]', new Date('2020-01-03')); - loggerProxy.log('timestamp: [%s]', new Date('2020-01-04')); + loggerProxy.log('timestamp: [%s]', new Date('2020-01-02').toISOString()); + loggerProxy.log('timestamp: [%s]', new Date('2020-01-03').toISOString()); + loggerProxy.log('timestamp: [%s]', new Date('2020-01-04').toISOString()); expect(inspect.defaultOptions.depth).toBe(8); await loggerProxy.waitCompletion();