From ba9cd98966982b8141b05043ff2abf1febd1cd82 Mon Sep 17 00:00:00 2001 From: Derek Cormier Date: Mon, 6 Jan 2025 15:39:12 -0800 Subject: [PATCH] refactor: build with bazel --- .aspect/bazelrc/BUILD.bazel | 15 + .aspect/bazelrc/bazel7.bazelrc | 15 + .aspect/bazelrc/convenience.bazelrc | 28 ++ .aspect/bazelrc/correctness.bazelrc | 75 ++++ .aspect/bazelrc/debug.bazelrc | 19 + .aspect/bazelrc/javascript.bazelrc | 11 + .aspect/bazelrc/performance.bazelrc | 27 ++ .aspect/cli/config.yaml | 3 + .bazelignore | 2 + .bazeliskrc | 2 + .bazelrc | 13 + .bazelversion | 1 + .github/workflows/ci.bazelrc | 15 + .github/workflows/ci.yaml | 39 ++ .gitignore | 9 + BUILD.bazel | 53 +++ MODULE.bazel | 78 ++++ WORKSPACE | 0 bazel/jest/BUILD.bazel | 0 bazel/jest/defs.bzl | 37 ++ bazel/ts/BUILD.bazel | 0 bazel/ts/defs.bzl | 27 ++ e2e/BUILD.bazel | 41 ++ e2e/__snapshots__/e2e.spec.js.snap | 575 +++++++++++++++++++++++++ e2e/helpers/BUILD.bazel | 30 ++ e2e/helpers/fixture.ts | 12 +- e2e/stubs/BUILD.bazel | 24 ++ e2e/stubs/cloud-functions.ts | 6 +- jest.config.js | 16 +- package.json | 5 +- pnpm-lock.yaml | 356 +++++++-------- src/BUILD.bazel | 16 + src/application/BUILD.bazel | 22 + src/application/webhook/BUILD.bazel | 24 ++ src/domain/BUILD.bazel | 79 ++++ src/domain/find-registry-fork.spec.ts | 15 +- src/domain/find-registry-fork.ts | 4 +- src/domain/metadata-file.spec.ts | 2 +- src/domain/release-archive.spec.ts | 1 - src/infrastructure/BUILD.bazel | 29 ++ src/infrastructure/xzdec/.gitignore | 2 - src/infrastructure/xzdec/BUILD.bazel | 21 +- src/infrastructure/xzdec/MODULE.bazel | 45 -- src/infrastructure/xzdec/xzdec.wasm.gz | Bin 55677 -> 0 bytes src/test/BUILD.bazel | 15 + tsconfig.json | 3 +- 46 files changed, 1557 insertions(+), 255 deletions(-) create mode 100644 .aspect/bazelrc/BUILD.bazel create mode 100644 .aspect/bazelrc/bazel7.bazelrc create mode 100644 .aspect/bazelrc/convenience.bazelrc create mode 100644 .aspect/bazelrc/correctness.bazelrc create mode 100644 .aspect/bazelrc/debug.bazelrc create mode 100644 .aspect/bazelrc/javascript.bazelrc create mode 100644 .aspect/bazelrc/performance.bazelrc create mode 100644 .aspect/cli/config.yaml create mode 100644 .bazelignore create mode 100644 .bazeliskrc create mode 100644 .bazelrc create mode 100644 .bazelversion create mode 100644 .github/workflows/ci.bazelrc create mode 100644 BUILD.bazel create mode 100644 MODULE.bazel create mode 100644 WORKSPACE create mode 100644 bazel/jest/BUILD.bazel create mode 100644 bazel/jest/defs.bzl create mode 100644 bazel/ts/BUILD.bazel create mode 100644 bazel/ts/defs.bzl create mode 100644 e2e/BUILD.bazel create mode 100644 e2e/__snapshots__/e2e.spec.js.snap create mode 100644 e2e/helpers/BUILD.bazel create mode 100644 e2e/stubs/BUILD.bazel create mode 100644 src/BUILD.bazel create mode 100644 src/application/BUILD.bazel create mode 100644 src/application/webhook/BUILD.bazel create mode 100644 src/domain/BUILD.bazel create mode 100644 src/infrastructure/BUILD.bazel delete mode 100644 src/infrastructure/xzdec/.gitignore delete mode 100644 src/infrastructure/xzdec/MODULE.bazel delete mode 100644 src/infrastructure/xzdec/xzdec.wasm.gz create mode 100644 src/test/BUILD.bazel diff --git a/.aspect/bazelrc/BUILD.bazel b/.aspect/bazelrc/BUILD.bazel new file mode 100644 index 0000000..35b148c --- /dev/null +++ b/.aspect/bazelrc/BUILD.bazel @@ -0,0 +1,15 @@ +"Aspect bazelrc presets; see https://docs.aspect.build/guides/bazelrc" + +load("@aspect_bazel_lib//lib:bazelrc_presets.bzl", "write_aspect_bazelrc_presets") + +write_aspect_bazelrc_presets( + name = "update_aspect_bazelrc_presets", + presets = [ + "bazel7", + "convenience", + "correctness", + "debug", + "javascript", + "performance", + ], +) diff --git a/.aspect/bazelrc/bazel7.bazelrc b/.aspect/bazelrc/bazel7.bazelrc new file mode 100644 index 0000000..dbd40ee --- /dev/null +++ b/.aspect/bazelrc/bazel7.bazelrc @@ -0,0 +1,15 @@ +# Speed up all builds by not checking if external repository files have been modified. +# Docs: https://github.com/bazelbuild/bazel/blob/1af61b21df99edc2fc66939cdf14449c2661f873/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java#L244 +common --noexperimental_check_external_repository_files + +# Don't report when the root module's lower bound for a dependency happens to be less than the resolved version. +# This is expected and should NOT prompt an engineer to update our lower bound to match. +# WARNING: For repository 'aspect_bazel_lib', the root module requires module version aspect_bazel_lib@1.30.2, +# but got aspect_bazel_lib@1.31.2 in the resolved dependency graph. +common --check_direct_dependencies=off + +# Directories used by sandboxed non-worker execution may be reused to avoid unnecessary setup costs. +# Save time on Sandbox creation and deletion when many of the same kind of action run during the +# build. +# Docs: https://bazel.build/reference/command-line-reference#flag--reuse_sandbox_directories +build --reuse_sandbox_directories diff --git a/.aspect/bazelrc/convenience.bazelrc b/.aspect/bazelrc/convenience.bazelrc new file mode 100644 index 0000000..796675a --- /dev/null +++ b/.aspect/bazelrc/convenience.bazelrc @@ -0,0 +1,28 @@ +# Attempt to build & test every target whose prerequisites were successfully built. +# Docs: https://bazel.build/docs/user-manual#keep-going +build --keep_going + +# Output test errors to stderr so users don't have to `cat` or open test failure log files when test +# fail. This makes the log noisier in exchange for reducing the time-to-feedback on test failures for +# users. +# Docs: https://bazel.build/docs/user-manual#test-output +test --test_output=errors + +# Show the output files created by builds that requested more than one target. This helps users +# locate the build outputs in more cases +# Docs: https://bazel.build/docs/user-manual#show-result +build --show_result=20 + +# Bazel picks up host-OS-specific config lines from bazelrc files. For example, if the host OS is +# Linux and you run bazel build, Bazel picks up lines starting with build:linux. Supported OS +# identifiers are `linux`, `macos`, `windows`, `freebsd`, and `openbsd`. Enabling this flag is +# equivalent to using `--config=linux` on Linux, `--config=windows` on Windows, etc. +# Docs: https://bazel.build/reference/command-line-reference#flag--enable_platform_specific_config +common --enable_platform_specific_config + +# Output a heap dump if an OOM is thrown during a Bazel invocation +# (including OOMs due to `--experimental_oom_more_eagerly_threshold`). +# The dump will be written to `/.heapdump.hprof`. +# You may need to configure CI to capture this artifact and upload for later use. +# Docs: https://bazel.build/reference/command-line-reference#flag--heap_dump_on_oom +common --heap_dump_on_oom diff --git a/.aspect/bazelrc/correctness.bazelrc b/.aspect/bazelrc/correctness.bazelrc new file mode 100644 index 0000000..a146698 --- /dev/null +++ b/.aspect/bazelrc/correctness.bazelrc @@ -0,0 +1,75 @@ +# Do not upload locally executed action results to the remote cache. +# This should be the default for local builds so local builds cannot poison the remote cache. +# It should be flipped to `--remote_upload_local_results` on CI +# by using `--bazelrc=.aspect/bazelrc/ci.bazelrc`. +# Docs: https://bazel.build/reference/command-line-reference#flag--remote_upload_local_results +build --noremote_upload_local_results + +# Don't allow network access for build actions in the sandbox. +# Ensures that you don't accidentally make non-hermetic actions/tests which depend on remote +# services. +# Developers should tag targets with `tags=["requires-network"]` to opt-out of the enforcement. +# Docs: https://bazel.build/reference/command-line-reference#flag--sandbox_default_allow_network +build --sandbox_default_allow_network=false + +# Warn if a test's timeout is significantly longer than the test's actual execution time. +# Bazel's default for test_timeout is medium (5 min), but most tests should instead be short (1 min). +# While a test's timeout should be set such that it is not flaky, a test that has a highly +# over-generous timeout can hide real problems that crop up unexpectedly. +# For instance, a test that normally executes in a minute or two should not have a timeout of +# ETERNAL or LONG as these are much, much too generous. +# Docs: https://bazel.build/docs/user-manual#test-verbose-timeout-warnings +test --test_verbose_timeout_warnings + +# Allow the Bazel server to check directory sources for changes. Ensures that the Bazel server +# notices when a directory changes, if you have a directory listed in the srcs of some target. +# Recommended when using +# [copy_directory](https://github.com/bazel-contrib/bazel-lib/blob/main/docs/copy_directory.md) and +# [rules_js](https://github.com/aspect-build/rules_js) since npm package are source directories +# inputs to copy_directory actions. +# Docs: https://bazel.build/reference/command-line-reference#flag--host_jvm_args +startup --host_jvm_args=-DBAZEL_TRACK_SOURCE_DIRECTORIES=1 + +# Allow exclusive tests to run in the sandbox. Fixes a bug where Bazel doesn't enable sandboxing for +# tests with `tags=["exclusive"]`. +# Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_exclusive_test_sandboxed +test --incompatible_exclusive_test_sandboxed + +# Use a static value for `PATH` and does not inherit `LD_LIBRARY_PATH`. Doesn't let environment +# variables like `PATH` sneak into the build, which can cause massive cache misses when they change. +# Use `--action_env=ENV_VARIABLE` if you want to inherit specific environment variables from the +# client, but note that doing so can prevent cross-user caching if a shared cache is used. +# Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_strict_action_env +build --incompatible_strict_action_env + +# Propagate tags from a target declaration to the actions' execution requirements. +# Ensures that tags applied in your BUILD file, like `tags=["no-remote"]` +# get propagated to actions created by the rule. +# Without this option, you rely on rules authors to manually check the tags you passed +# and apply relevant ones to the actions they create. +# See https://github.com/bazelbuild/bazel/issues/8830 for details. +# Docs: https://bazel.build/reference/command-line-reference#flag--experimental_allow_tags_propagation +build --experimental_allow_tags_propagation +fetch --experimental_allow_tags_propagation +query --experimental_allow_tags_propagation + +# Do not automatically create `__init__.py` files in the runfiles of Python targets. Fixes the wrong +# default that comes from Google's internal monorepo by using `__init__.py` to delimit a Python +# package. Precisely, when a `py_binary` or `py_test` target has `legacy_create_init` set to `auto (the +# default), it is treated as false if and only if this flag is set. See +# https://github.com/bazelbuild/bazel/issues/10076. +# Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_default_to_explicit_init_py +build --incompatible_default_to_explicit_init_py + +# Set default value of `allow_empty` to `False` in `glob()`. This prevents a common mistake when +# attempting to use `glob()` to match files in a subdirectory that is opaque to the current package +# because it contains a BUILD file. See https://github.com/bazelbuild/bazel/issues/8195. +# Docs: https://bazel.build/reference/command-line-reference#flag--incompatible_disallow_empty_glob +common --incompatible_disallow_empty_glob + +# Always download coverage files for tests from the remote cache. By default, coverage files are not +# downloaded on test result cache hits when --remote_download_minimal is enabled, making it impossible +# to generate a full coverage report. +# Docs: https://bazel.build/reference/command-line-reference#flag--experimental_fetch_all_coverage_outputs +# detching remote cache results +test --experimental_fetch_all_coverage_outputs diff --git a/.aspect/bazelrc/debug.bazelrc b/.aspect/bazelrc/debug.bazelrc new file mode 100644 index 0000000..bfb0bdd --- /dev/null +++ b/.aspect/bazelrc/debug.bazelrc @@ -0,0 +1,19 @@ +############################################################ +# Use `bazel test --config=debug` to enable these settings # +############################################################ + +# Stream stdout/stderr output from each test in real-time. +# Docs: https://bazel.build/docs/user-manual#test-output +test:debug --test_output=streamed + +# Run one test at a time. +# Docs: https://bazel.build/reference/command-line-reference#flag--test_strategy +test:debug --test_strategy=exclusive + +# Prevent long running tests from timing out. +# Docs: https://bazel.build/docs/user-manual#test-timeout +test:debug --test_timeout=9999 + +# Always run tests even if they have cached results. +# Docs: https://bazel.build/docs/user-manual#cache-test-results +test:debug --nocache_test_results diff --git a/.aspect/bazelrc/javascript.bazelrc b/.aspect/bazelrc/javascript.bazelrc new file mode 100644 index 0000000..ace9d60 --- /dev/null +++ b/.aspect/bazelrc/javascript.bazelrc @@ -0,0 +1,11 @@ +# Aspect recommended Bazel flags when using Aspect's JavaScript rules: https://github.com/aspect-build/rules_js +# Docs for Node.js flags: https://nodejs.org/en/docs/guides/debugging-getting-started/#command-line-options + +# Support for debugging Node.js tests. Use bazel run with `--config=debug` to turn on the NodeJS +# inspector agent. The node process will break before user code starts and wait for the debugger to +# connect. Pass the --inspect-brk option to all tests which enables the node inspector agent. See +# https://nodejs.org/de/docs/guides/debugging-getting-started/#command-line-options for more +# details. +# Docs: https://nodejs.org/en/docs/guides/debugging-getting-started/#command-line-options +run:debug -- --node_options=--inspect-brk +test:debug --test_env=NODE_OPTIONS=--inspect-brk diff --git a/.aspect/bazelrc/performance.bazelrc b/.aspect/bazelrc/performance.bazelrc new file mode 100644 index 0000000..fc404c2 --- /dev/null +++ b/.aspect/bazelrc/performance.bazelrc @@ -0,0 +1,27 @@ +# Directories used by sandboxed non-worker execution may be reused to avoid unnecessary setup costs. +# Save time on Sandbox creation and deletion when many of the same kind of action run during the +# build. +# No longer experimental in Bazel 6: https://github.com/bazelbuild/bazel/commit/c1a95501a5611878e5cc43a3cc531f2b9e47835b +# Docs: https://bazel.build/reference/command-line-reference#flag--reuse_sandbox_directories +build --experimental_reuse_sandbox_directories + +# Do not build runfiles symlink forests for external repositories under +# `.runfiles/wsname/external/repo` (in addition to `.runfiles/repo`). This reduces runfiles & +# sandbox creation times & prevents accidentally depending on this feature which may flip to off by +# default in the future. Note, some rules may fail under this flag, please file issues with the rule +# author. +# Docs: https://bazel.build/reference/command-line-reference#flag--legacy_external_runfiles +build --nolegacy_external_runfiles + +# Avoid creating a runfiles tree for binaries or tests until it is needed. +# Docs: https://bazel.build/reference/command-line-reference#flag--build_runfile_links +# See https://github.com/bazelbuild/bazel/issues/6627 +# +# This may break local workflows that `build` a binary target, then run the resulting program +# outside of `bazel run`. In those cases, the script will need to call +# `bazel build --build_runfile_links //my/binary:target` and then execute the resulting program. +build --nobuild_runfile_links + +# Needed prior to Bazel 8; see +# https://github.com/bazelbuild/bazel/issues/20577 +coverage --build_runfile_links diff --git a/.aspect/cli/config.yaml b/.aspect/cli/config.yaml new file mode 100644 index 0000000..dd0bf53 --- /dev/null +++ b/.aspect/cli/config.yaml @@ -0,0 +1,3 @@ +configure: + languages: + javascript: true \ No newline at end of file diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 0000000..76add87 --- /dev/null +++ b/.bazelignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/.bazeliskrc b/.bazeliskrc new file mode 100644 index 0000000..5ca935b --- /dev/null +++ b/.bazeliskrc @@ -0,0 +1,2 @@ +BAZELISK_BASE_URL=https://static.aspect.build/aspect +USE_BAZEL_VERSION=aspect/2024.51.11 \ No newline at end of file diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..1ca6fdf --- /dev/null +++ b/.bazelrc @@ -0,0 +1,13 @@ +# Import Aspect bazelrc presets +import %workspace%/.aspect/bazelrc/bazel7.bazelrc +import %workspace%/.aspect/bazelrc/convenience.bazelrc +import %workspace%/.aspect/bazelrc/correctness.bazelrc +import %workspace%/.aspect/bazelrc/debug.bazelrc +import %workspace%/.aspect/bazelrc/javascript.bazelrc +import %workspace%/.aspect/bazelrc/performance.bazelrc + +# honor the setting of `skipLibCheck` in the tsconfig.json file +common --@aspect_rules_ts//ts:skipLibCheck=honor_tsconfig + +# Use "tsc" as the transpiler when ts_project has no `transpiler` set. +common --@aspect_rules_ts//ts:default_to_tsc_transpiler \ No newline at end of file diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000..fa5fce0 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +8.0.0 \ No newline at end of file diff --git a/.github/workflows/ci.bazelrc b/.github/workflows/ci.bazelrc new file mode 100644 index 0000000..2a5a8a3 --- /dev/null +++ b/.github/workflows/ci.bazelrc @@ -0,0 +1,15 @@ +# This file contains Bazel settings to apply on CI only. +# It is referenced with a --bazelrc option in the call to bazel in ci.yaml + +# Debug where options came from +build --announce_rc +# This directory is configured in GitHub actions to be persisted between runs. +# We do not enable the repository cache to cache downloaded external artifacts +# as these are generally faster to download again than to fetch them from the +# GitHub actions cache. +build --disk_cache=~/.cache/bazel +# Don't rely on test logs being easily accessible from the test runner, +# though it makes the log noisier. +test --test_output=errors +# Allows tests to run bazelisk-in-bazel, since this is the cache folder used +test --test_env=XDG_CACHE_HOME \ No newline at end of file diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6d23a60..89b19b2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,6 +9,12 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: +concurrency: + # Cancel previous actions from the same PR or branch except 'main' branch. + # See https://docs.github.com/en/actions/using-jobs/using-concurrency and https://docs.github.com/en/actions/learn-github-actions/contexts for more info. + group: concurrency-group::${{ github.workflow }}::${{ github.event.pull_request.number > 0 && format('pr-{0}', github.event.pull_request.number) || github.ref_name }}${{ github.ref_name == 'main' && format('::{0}', github.run_id) || ''}} + cancel-in-progress: ${{ github.ref_name != 'main' }} + jobs: test: runs-on: ubuntu-latest @@ -40,3 +46,36 @@ jobs: cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm run e2e + + bazel: + uses: bazel-contrib/.github/.github/workflows/bazel.yaml@v6 + with: + folders: | + [ + ".", + ] + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pre-commit/action@v3.0.1 + # For branch protection settings, this job provides a "stable" name that can be used to gate PR merges + # on "all matrix jobs were successful". + conclusion: + needs: [test, pre-commit] + runs-on: ubuntu-latest + if: always() + steps: + - uses: technote-space/workflow-conclusion-action@45ce8e0eb155657ab8ccf346ade734257fd196a5 # v3.0.3 + + # Note: possible conclusion values: + # https://github.com/technote-space/workflow-conclusion-action/blob/main/src/constant.ts + - name: report success + if: ${{ env.WORKFLOW_CONCLUSION == 'success' }} + working-directory: /tmp + run: echo ${{ env.WORKFLOW_CONCLUSION }} && exit 0 + + - name: report failure + if: ${{ env.WORKFLOW_CONCLUSION == 'failure' }} + working-directory: /tmp + run: echo ${{ env.WORKFLOW_CONCLUSION }} && exit 1 diff --git a/.gitignore b/.gitignore index bb11fa8..edf8472 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,14 @@ +bazel-* build dist +# Bazel's MODULE lockfile isn't ready to check in yet as of Bazel 7.1. +# Do allow for it to be created, however, since it gives a performance boost for local development. +# [Store resolved repository attributes in the Bzlmod lockfile](https://github.com/bazelbuild/bazel/issues/19026) +# [MODULE.bazel.lock file contains user specific paths](https://github.com/bazelbuild/bazel/issues/19621) +# [Consider skipping bazel_tools@_ from lockfile](https://github.com/bazelbuild/bazel/issues/19971) +# [MODULE.bazel.lock file "reads through" already-locked package manager](https://github.com/bazelbuild/bazel/issues/20272) +# [moduleFileHash in MODULE.bazel.lock causes frequent Git merge conflicts](https://github.com/bazelbuild/bazel/issues/20369) +MODULE.bazel.lock node_modules .DS_Store .terraform diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000..c21d37c --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,53 @@ +load("@aspect_rules_js//js:defs.bzl", "js_library") +load("@npm//:defs.bzl", "npm_link_all_packages") +load("//bazel/ts:defs.bzl", "ts_config", "ts_project") + +exports_files( + [ + "babel.config.json", + "pnpm-lock.yaml", + ], + visibility = ["//:__subpackages__"], +) + +npm_link_all_packages(name = "node_modules") + +ts_project( + name = "root", + srcs = [ + "jest.setup.ts", + ], + visibility = ["//src:__subpackages__"], + deps = [ + ":node_modules/@jest/globals", + ":node_modules/jest-extended", + "//:node_modules/@types/jest", # keep + ], +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + visibility = [ + "//e2e:__subpackages__", + "//src:__subpackages__", + ], +) + +js_library( + name = "package_json", + srcs = ["package.json"], + visibility = ["//:__subpackages__"], +) + +js_library( + name = "jest_config", + srcs = [ + "jest.config.js", + ], + visibility = ["//:__subpackages__"], + deps = [ + "//:node_modules/babel-jest", + "//:root", + ], +) diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..06179ef --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,78 @@ +module(name = "publish-to-bcr") + +bazel_dep(name = "aspect_bazel_lib", version = "2.9.3") +bazel_dep(name = "aspect_rules_jest", version = "0.22.0") +bazel_dep(name = "aspect_rules_js", version = "2.1.2") +bazel_dep(name = "aspect_rules_ts", version = "3.3.2") +bazel_dep(name = "platforms", version = "0.0.10") +bazel_dep(name = "rules_nodejs", version = "6.3.2") +bazel_dep(name = "toolchains_llvm", version = "1.2.0") +bazel_dep(name = "xz", version = "5.4.5.bcr.5") + +# FIXME: Remove when a new `toolchains_llvm` has been released. +git_override( + module_name = "toolchains_llvm", + commit = "bda1c9fbf232b682c30d039f8e4a5e3cf3025d0f", + remote = "https://github.com/bazel-contrib/toolchains_llvm", +) + +node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node", dev_dependency = True) +node.toolchain(node_version = "18.20.4") + +pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm") +pnpm.pnpm( + name = "pnpm", + pnpm_version = "9.7.0", +) +# Allows developers to use the matching pnpm version, for example: +# bazel run -- @pnpm//:pnpm install --dir $PWD --lockfile-only +use_repo(pnpm, "pnpm") + +npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm", dev_dependency = True) +npm.npm_translate_lock( + name = "npm", + bins = { + "@google-cloud/functions-framework": [ + "functions-framework=build/src/main.js" + ] + }, + pnpm_lock = "//:pnpm-lock.yaml", + npmrc = "//:.npmrc", + verify_node_modules_ignored = "//:.bazelignore", +) + +use_repo(npm, "npm") + +rules_ts_ext = use_extension("@aspect_rules_ts//ts:extensions.bzl", "ext", dev_dependency = True) + +rules_ts_ext.deps( + ts_version_from = "//:package.json", +) + +use_repo(rules_ts_ext, "npm_typescript") + +llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm") +llvm.toolchain( + libclang_rt = { + "@libclang_rt-wasm32-wasi//:libclang_rt.builtins-wasm32.a": "wasm32-unknown-unknown/libclang_rt.builtins.a", + }, + llvm_versions = { + "": "19.1.0", + }, + stdlib = {"wasm32": "libc"}, +) +llvm.sysroot( + label = "@wasi-sysroot//sysroots/wasm32-wasip2", + targets = ["wasm32"], +) +use_repo(llvm, "llvm_toolchain") + +register_toolchains("@llvm_toolchain//:all") + +wasi_sysroot = use_repo_rule("//src/infrastructure/xzdec:wasm.bzl", "wasi_sysroot") + +wasm32_libclang_rt = use_repo_rule("//src/infrastructure/xzdec:wasm.bzl", "wasm32_libclang_rt") + +wasi_sysroot(name = "wasi-sysroot") + +wasm32_libclang_rt(name = "libclang_rt-wasm32-wasi") diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..e69de29 diff --git a/bazel/jest/BUILD.bazel b/bazel/jest/BUILD.bazel new file mode 100644 index 0000000..e69de29 diff --git a/bazel/jest/defs.bzl b/bazel/jest/defs.bzl new file mode 100644 index 0000000..3ebd4c7 --- /dev/null +++ b/bazel/jest/defs.bzl @@ -0,0 +1,37 @@ +""" +Defaults for jest tests +""" +load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file") +load("@aspect_rules_jest//jest:defs.bzl", _jest_test = "jest_test") + +def jest_test(name, **kwargs): + """Defaults for jest_test. + + Args: + name: Name of the jest_test target + **kwargs: Additional attributes to pass to the jest_test rule + """ + + # Tell jest to use babel to transform ESM into commonjs before + # running tests, because testing ESM in jest is a nightmare. + # The location of the babel config file must be next to the jest + # root in order to work, so copy it here. + copy_file( + name = "{}__babel_config".format(name), + src = "//:babel.config.json", + out = "babel.config.json", + ) + + data = kwargs.pop("data", []) + + _jest_test( + name = name, + config = "//:jest_config", + data = data + [ + "babel.config.json", + "//:node_modules/@babel/preset-env", + "//:package_json", + ], + node_modules = "//:node_modules", + **kwargs + ) \ No newline at end of file diff --git a/bazel/ts/BUILD.bazel b/bazel/ts/BUILD.bazel new file mode 100644 index 0000000..e69de29 diff --git a/bazel/ts/defs.bzl b/bazel/ts/defs.bzl new file mode 100644 index 0000000..6941209 --- /dev/null +++ b/bazel/ts/defs.bzl @@ -0,0 +1,27 @@ +""" +Defaults for Typescript projects +""" + +load("@aspect_rules_ts//ts:defs.bzl", _ts_config = "ts_config", _ts_project = "ts_project") + +ts_config = _ts_config + +def ts_project(name, **kwargs): + """Defaults for ts_project. + + Args: + name: Name of the ts_project target + **kwargs: Additional attributes to pass to the ts_project rule + """ + + tsconfig = kwargs.pop("tsconfig", "//:tsconfig") + + _ts_project( + name = name, + declaration = True, + source_map = True, + allow_js = True, + tsconfig = {"include": ["**/*.ts"]}, + extends = tsconfig, + **kwargs + ) \ No newline at end of file diff --git a/e2e/BUILD.bazel b/e2e/BUILD.bazel new file mode 100644 index 0000000..427eba6 --- /dev/null +++ b/e2e/BUILD.bazel @@ -0,0 +1,41 @@ +load("//bazel/jest:defs.bzl", "jest_test") +load("//bazel/ts:defs.bzl", "ts_project") + +ts_project( + name = "e2e_tests", + testonly = True, + srcs = ["e2e.spec.ts"], + data = glob(["fixtures/**/*"]) + [ + # e2e tests run a cloud function emulator which runs on the + # distribution package. The emulator (unlike GCP) doesn't install + # deps from the included pnpm-lock.yaml file. For simplicity just + # link all node_modules so that the emulation can resolve its deps. + "//:node_modules", + ], + deps = [ + "//:node_modules/@octokit/core", + "//:node_modules/@octokit/webhooks-types", + "//:node_modules/@types/imapflow", + "//:node_modules/@types/mailparser", + "//:node_modules/@types/node", + "//:node_modules/@types/nodemailer", + "//:node_modules/imapflow", + "//:node_modules/mailparser", + "//:node_modules/mockttp", + "//:node_modules/nodemailer", + "//:node_modules/simple-git", + "//e2e/helpers", + "//e2e/stubs", + "//src/infrastructure", + ], +) + +jest_test( + name = "test", + timeout = "moderate", + data = [ + ":e2e_tests", + ], + snapshots = ["__snapshots__/e2e.spec.js.snap"], + tags = ["requires-network"], +) diff --git a/e2e/__snapshots__/e2e.spec.js.snap b/e2e/__snapshots__/e2e.spec.js.snap new file mode 100644 index 0000000..1ad7aa2 --- /dev/null +++ b/e2e/__snapshots__/e2e.spec.js.snap @@ -0,0 +1,575 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`e2e tests [snapshot] empty strip prefix 1`] = ` +"---------------------------------------------------- +modules/no-prefix/1.0.0/MODULE.bazel +---------------------------------------------------- +module( + name = "no-prefix", + version = "1.0.0", +) +---------------------------------------------------- +modules/no-prefix/1.0.0/presubmit.yml +---------------------------------------------------- +bcr_test_module: + module_path: "e2e/bzlmod" + matrix: + platform: ["debian10", "macos", "ubuntu2004", "windows"] + bazel: [6.x, 7.x] + tasks: + run_tests: + name: "Run test module" + platform: \${{ platform }} + bazel: \${{ bazel }} + test_targets: + - "//..." + +---------------------------------------------------- +modules/no-prefix/1.0.0/source.json +---------------------------------------------------- +{ + "integrity": "sha256-vV6teCsT89MjpwiZjOMoTrIOerNBO5WIiFYpVw009bM=", + "url": "https://github.com/testorg/no-prefix/archive/refs/tags/v1.0.0.tar.gz" +} + +---------------------------------------------------- +modules/no-prefix/metadata.json +---------------------------------------------------- +{ + "homepage": "https://github.com/testorg/no-prefix", + "maintainers": [ + { + "name": "Foo McBar", + "email": "foo@test.org", + "github": "foobar" + } + ], + "repository": [ + "github:testorg/no-prefix" + ], + "versions": [ + "1.0.0" + ], + "yanked_versions": {} +} + +" +`; + +exports[`e2e tests [snapshot] error email for incorrect strip prefix 1`] = ` +"TO: releaser@test.org, foo@test.org +SUBJECT: Publish to BCR + +Failed to publish entry for testorg/versioned@v1.0.0 to the Bazel Central Registry. + +Could not find MODULE.bazel in release archive at ./versioned-1.0.0/MODULE.bazel. +Is the strip prefix in source.template.json correct? (currently it's 'versioned-1.0.0') + + +" +`; + +exports[`e2e tests [snapshot] error email for incorrect strip prefix in multi module repo 1`] = ` +"TO: releaser@test.org, foo@test.org, moo@test.org +SUBJECT: Publish to BCR + +Failed to publish entry for testorg/multi-module@v1.0.0 to the Bazel Central Registry. + +Failed to download release archive from http://localhost:8001/testorg/multi-module/releases/download/v1.0.0.tar.gz. Received status 503 + + +" +`; + +exports[`e2e tests [snapshot] missing strip prefix 1`] = ` +"---------------------------------------------------- +modules/empty-prefix/1.0.0/MODULE.bazel +---------------------------------------------------- +module( + name = "empty-prefix", + version = "1.0.0", +) +---------------------------------------------------- +modules/empty-prefix/1.0.0/presubmit.yml +---------------------------------------------------- +bcr_test_module: + module_path: "e2e/bzlmod" + matrix: + platform: ["debian10", "macos", "ubuntu2004", "windows"] + bazel: [6.x, 7.x] + tasks: + run_tests: + name: "Run test module" + platform: \${{ platform }} + bazel: \${{ bazel }} + test_targets: + - "//..." + +---------------------------------------------------- +modules/empty-prefix/1.0.0/source.json +---------------------------------------------------- +{ + "integrity": "sha256-7MREuSV4sRSIJjbh8IKcbBOyWKbygLljphYs7hB++tg=", + "strip_prefix": "", + "url": "https://github.com/testorg/empty-prefix/archive/refs/tags/v1.0.0.tar.gz" +} + +---------------------------------------------------- +modules/empty-prefix/metadata.json +---------------------------------------------------- +{ + "homepage": "https://github.com/testorg/empty-prefix", + "maintainers": [ + { + "name": "Foo McBar", + "email": "foo@test.org", + "github": "foobar" + } + ], + "repository": [ + "github:testorg/empty-prefix" + ], + "versions": [ + "1.0.0" + ], + "yanked_versions": {} +} + +" +`; + +exports[`e2e tests [snapshot] multiple modules 1`] = ` +"---------------------------------------------------- +modules/module/1.0.0/MODULE.bazel +---------------------------------------------------- +module( + name = "module", + version = "1.0.0", +) +---------------------------------------------------- +modules/module/1.0.0/presubmit.yml +---------------------------------------------------- +bcr_test_module: + module_path: "e2e/bzlmod" + matrix: + platform: ["debian10", "macos", "ubuntu2004", "windows"] + bazel: [6.x, 7.x] + tasks: + run_tests: + name: "Run test module" + platform: \${{ platform }} + bazel: \${{ bazel }} + test_targets: + - "//..." + +---------------------------------------------------- +modules/module/1.0.0/source.json +---------------------------------------------------- +{ + "integrity": "sha256-A9Iqh3g+IkW9cntnxwKFfCeKlWJPl2dEpQ5oxdYiq3I=", + "strip_prefix": "multi-module-1.0.0", + "url": "https://github.com/testorg/multi-module/releases/download/v1.0.0.tar.gz" +} + +---------------------------------------------------- +modules/module/metadata.json +---------------------------------------------------- +{ + "homepage": "https://github.com/testorg/multi-module", + "maintainers": [ + { + "name": "Foo McBar", + "email": "foo@test.org", + "github": "foobar" + } + ], + "repository": [ + "github:testorg/multi-module" + ], + "versions": [ + "1.0.0" + ], + "yanked_versions": {} +} + +---------------------------------------------------- +modules/submodule/1.0.0/MODULE.bazel +---------------------------------------------------- +module( + name = "submodule", + version = "1.0.0", +) +---------------------------------------------------- +modules/submodule/1.0.0/presubmit.yml +---------------------------------------------------- +bcr_test_module: + module_path: "e2e/bzlmod" + matrix: + platform: ["debian10", "macos", "ubuntu2004", "windows"] + bazel: [6.x, 7.x] + tasks: + run_tests: + name: "Run test module" + platform: \${{ platform }} + bazel: \${{ bazel }} + test_targets: + - "//..." + +---------------------------------------------------- +modules/submodule/1.0.0/source.json +---------------------------------------------------- +{ + "integrity": "sha256-A9Iqh3g+IkW9cntnxwKFfCeKlWJPl2dEpQ5oxdYiq3I=", + "strip_prefix": "multi-module-1.0.0/submodule", + "url": "https://github.com/testorg/multi-module/releases/download/v1.0.0.tar.gz" +} + +---------------------------------------------------- +modules/submodule/metadata.json +---------------------------------------------------- +{ + "homepage": "https://github.com/testorg/multi-module", + "maintainers": [ + { + "name": "Foo McBar", + "email": "foo@test.org", + "github": "foobar" + }, + { + "name": "Moo Cow", + "email": "moo@test.org", + "github": "moocow" + } + ], + "repository": [ + "github:testorg/multi-module" + ], + "versions": [ + "1.0.0" + ], + "yanked_versions": {} +} + +" +`; + +exports[`e2e tests [snapshot] ruleset with tarball release archive 1`] = ` +"---------------------------------------------------- +modules/tarball/1.0.0/MODULE.bazel +---------------------------------------------------- +module( + name = "tarball", + version = "1.0.0", +) +---------------------------------------------------- +modules/tarball/1.0.0/presubmit.yml +---------------------------------------------------- +bcr_test_module: + module_path: "e2e/bzlmod" + matrix: + platform: ["debian10", "macos", "ubuntu2004", "windows"] + bazel: [6.x, 7.x] + tasks: + run_tests: + name: "Run test module" + platform: \${{ platform }} + bazel: \${{ bazel }} + test_targets: + - "//..." + +---------------------------------------------------- +modules/tarball/1.0.0/source.json +---------------------------------------------------- +{ + "integrity": "sha256-QuCA9f+ENsEV+mes0JMiyWv1mprq8xkzKkgO9eCWwaU=", + "strip_prefix": "tarball-1.0.0", + "url": "https://github.com/testorg/tarball/archive/refs/tags/v1.0.0.tar.gz" +} + +---------------------------------------------------- +modules/tarball/metadata.json +---------------------------------------------------- +{ + "homepage": "https://github.com/testorg/tarball", + "maintainers": [ + { + "name": "Foo McBar", + "email": "foo@test.org", + "github": "foobar" + } + ], + "repository": [ + "github:testorg/tarball" + ], + "versions": [ + "1.0.0" + ], + "yanked_versions": {} +} + +" +`; + +exports[`e2e tests [snapshot] ruleset with unversioned module in source 1`] = ` +"---------------------------------------------------- +modules/unversioned/1.0.0/MODULE.bazel +---------------------------------------------------- +module( + name = "unversioned", + version = "1.0.0", +) + +---------------------------------------------------- +modules/unversioned/1.0.0/patches/module_dot_bazel_version.patch +---------------------------------------------------- +=================================================================== +--- a/MODULE.bazel ++++ b/MODULE.bazel +@@ -1,3 +1,4 @@ + module( +- name = "unversioned" ++ name = "unversioned", ++ version = "1.0.0", + ) + +---------------------------------------------------- +modules/unversioned/1.0.0/presubmit.yml +---------------------------------------------------- +bcr_test_module: + module_path: "e2e/bzlmod" + matrix: + platform: ["debian10", "macos", "ubuntu2004", "windows"] + bazel: [6.x, 7.x] + tasks: + run_tests: + name: "Run test module" + platform: \${{ platform }} + bazel: \${{ bazel }} + test_targets: + - "//..." + +---------------------------------------------------- +modules/unversioned/1.0.0/source.json +---------------------------------------------------- +{ + "integrity": "sha256-wfBkZmCOTy249ZVzFGVDAAOSVIQmaCGEp53JtVAG+Gg=", + "strip_prefix": "unversioned-1.0.0", + "url": "https://github.com/testorg/unversioned/archive/refs/tags/v1.0.0.tar.gz", + "patches": { + "module_dot_bazel_version.patch": "sha256-kZesuygBz9d247Nv0mshe3bXxzIo+Ly4X0wYy8HYVEQ=" + }, + "patch_strip": 1 +} + +---------------------------------------------------- +modules/unversioned/metadata.json +---------------------------------------------------- +{ + "homepage": "https://github.com/testorg/unversioned", + "maintainers": [ + { + "name": "Foo McBar", + "email": "foo@test.org", + "github": "foobar" + } + ], + "repository": [ + "github:testorg/unversioned" + ], + "versions": [ + "1.0.0" + ], + "yanked_versions": {} +} + +" +`; + +exports[`e2e tests [snapshot] ruleset with versioned module in source 1`] = ` +"---------------------------------------------------- +modules/versioned/1.0.0/MODULE.bazel +---------------------------------------------------- +module( + name = "versioned", + version = "1.0.0", +) +---------------------------------------------------- +modules/versioned/1.0.0/presubmit.yml +---------------------------------------------------- +bcr_test_module: + module_path: "e2e/bzlmod" + matrix: + platform: ["debian10", "macos", "ubuntu2004", "windows"] + bazel: [6.x, 7.x] + tasks: + run_tests: + name: "Run test module" + platform: \${{ platform }} + bazel: \${{ bazel }} + test_targets: + - "//..." + +---------------------------------------------------- +modules/versioned/1.0.0/source.json +---------------------------------------------------- +{ + "integrity": "sha256-4bwOuihESu6nG6AgzMOw4dNVCINixJqHBXW4qXr+384=", + "strip_prefix": "versioned-1.0.0", + "url": "https://github.com/testorg/versioned/archive/refs/tags/v1.0.0.tar.gz" +} + +---------------------------------------------------- +modules/versioned/metadata.json +---------------------------------------------------- +{ + "homepage": "https://github.com/testorg/versioned", + "maintainers": [ + { + "name": "Foo McBar", + "email": "foo@test.org", + "github": "foobar" + } + ], + "repository": [ + "github:testorg/versioned" + ], + "versions": [ + "1.0.0" + ], + "yanked_versions": {} +} + +" +`; + +exports[`e2e tests [snapshot] ruleset with zero-versioned module in source 1`] = ` +"---------------------------------------------------- +modules/zero-versioned/1.0.0/MODULE.bazel +---------------------------------------------------- +module( + name = "zero-versioned", + version = "1.0.0", +) + +---------------------------------------------------- +modules/zero-versioned/1.0.0/patches/module_dot_bazel_version.patch +---------------------------------------------------- +=================================================================== +--- a/MODULE.bazel ++++ b/MODULE.bazel +@@ -1,4 +1,4 @@ + module( + name = "zero-versioned", +- version = "0.0.0", ++ version = "1.0.0", + ) + +---------------------------------------------------- +modules/zero-versioned/1.0.0/presubmit.yml +---------------------------------------------------- +bcr_test_module: + module_path: "e2e/bzlmod" + matrix: + platform: ["debian10", "macos", "ubuntu2004", "windows"] + bazel: [6.x, 7.x] + tasks: + run_tests: + name: "Run test module" + platform: \${{ platform }} + bazel: \${{ bazel }} + test_targets: + - "//..." + +---------------------------------------------------- +modules/zero-versioned/1.0.0/source.json +---------------------------------------------------- +{ + "integrity": "sha256-OHSXEuWyo2djYr8CjWMT9dq38TDS9P/vIzNBfLQ5E+Q=", + "strip_prefix": "zero-versioned-1.0.0", + "url": "https://github.com/testorg/zero-versioned/archive/refs/tags/v1.0.0.tar.gz", + "patches": { + "module_dot_bazel_version.patch": "sha256-KpbuC1vv5mfhdTs5nnTl3/pH7Y/6JCnD1b1XLsqyOAo=" + }, + "patch_strip": 1 +} + +---------------------------------------------------- +modules/zero-versioned/metadata.json +---------------------------------------------------- +{ + "homepage": "https://github.com/testorg/zero-versioned", + "maintainers": [ + { + "name": "Foo McBar", + "email": "foo@test.org", + "github": "foobar" + } + ], + "repository": [ + "github:testorg/zero-versioned" + ], + "versions": [ + "1.0.0" + ], + "yanked_versions": {} +} + +" +`; + +exports[`e2e tests [snapshot] ruleset with zip release archive 1`] = ` +"---------------------------------------------------- +modules/zip/1.0.0/MODULE.bazel +---------------------------------------------------- +module( + name = "zip", + version = "1.0.0", +) +---------------------------------------------------- +modules/zip/1.0.0/presubmit.yml +---------------------------------------------------- +bcr_test_module: + module_path: "e2e/bzlmod" + matrix: + platform: ["debian10", "macos", "ubuntu2004", "windows"] + bazel: [6.x, 7.x] + tasks: + run_tests: + name: "Run test module" + platform: \${{ platform }} + bazel: \${{ bazel }} + test_targets: + - "//..." + +---------------------------------------------------- +modules/zip/1.0.0/source.json +---------------------------------------------------- +{ + "integrity": "sha256-RQfQO5DLbCsPnLGMLYGksr29V5aM7K7fegmwp2K/jx0=", + "strip_prefix": "zip-1.0.0", + "url": "https://github.com/testorg/zip/archive/refs/tags/v1.0.0.zip" +} + +---------------------------------------------------- +modules/zip/metadata.json +---------------------------------------------------- +{ + "homepage": "https://github.com/testorg/zip", + "maintainers": [ + { + "name": "Foo McBar", + "email": "foo@test.org", + "github": "foobar" + } + ], + "repository": [ + "github:testorg/zip" + ], + "versions": [ + "1.0.0" + ], + "yanked_versions": {} +} + +" +`; diff --git a/e2e/helpers/BUILD.bazel b/e2e/helpers/BUILD.bazel new file mode 100644 index 0000000..6fb828a --- /dev/null +++ b/e2e/helpers/BUILD.bazel @@ -0,0 +1,30 @@ +load("//bazel/ts:defs.bzl", "ts_project") + +ts_project( + name = "helpers", + srcs = [ + "archive.ts", + "email.ts", + "fixture.ts", + "types.ts", + "webhook.ts", + ], + visibility = ["//e2e:__subpackages__"], + deps = [ + "//:node_modules/@octokit/webhooks-methods", + "//:node_modules/@octokit/webhooks-types", + "//:node_modules/@types/archiver", + "//:node_modules/@types/imapflow", + "//:node_modules/@types/mailparser", + "//:node_modules/@types/node", + "//:node_modules/@types/nodemailer", + "//:node_modules/@types/tar", + "//:node_modules/archiver", + "//:node_modules/axios", + "//:node_modules/imapflow", + "//:node_modules/mailparser", + "//:node_modules/nodemailer", + "//:node_modules/simple-git", + "//:node_modules/tar", + ], +) diff --git a/e2e/helpers/fixture.ts b/e2e/helpers/fixture.ts index 6ac70ca..5f9c871 100644 --- a/e2e/helpers/fixture.ts +++ b/e2e/helpers/fixture.ts @@ -1,19 +1,11 @@ import { User } from "@octokit/webhooks-types"; import fs from "node:fs"; -import os from "node:os"; import path from "node:path"; import simpleGit, { SimpleGit } from "simple-git"; -export const FIXTURES_PATH = path.join( - __dirname, - "..", - "..", - "..", - "e2e", - "fixtures" -); +export const FIXTURES_PATH = path.join("e2e", "fixtures"); export const PREPARED_FIXTURES_PATH = fs.mkdtempSync( - os.tmpdir() + path.sep + "fixtures-" + process.env.TEST_TMPDIR + path.sep + "fixtures-" ); export enum Fixture { diff --git a/e2e/stubs/BUILD.bazel b/e2e/stubs/BUILD.bazel new file mode 100644 index 0000000..e76df9a --- /dev/null +++ b/e2e/stubs/BUILD.bazel @@ -0,0 +1,24 @@ +load("//bazel/ts:defs.bzl", "ts_project") + +ts_project( + name = "stubs", + srcs = [ + "cloud-functions.ts", + "fake-github.ts", + "fake-secrets.ts", + "stubbed-server.ts", + ], + visibility = ["//e2e:__subpackages__"], + deps = [ + "//:node_modules/@google-cloud/functions-framework", # keep + "//:node_modules/@octokit/webhooks-types", + "//:node_modules/@types/jest", # keep + "//:node_modules/@types/node", + "//:node_modules/@types/nodemailer", + "//:node_modules/mockttp", + "//:node_modules/nodemailer", + "//:node_modules/portfinder", + "//src:dist", # keep + "//e2e/helpers", + ], +) diff --git a/e2e/stubs/cloud-functions.ts b/e2e/stubs/cloud-functions.ts index af79c8b..c26f9a3 100644 --- a/e2e/stubs/cloud-functions.ts +++ b/e2e/stubs/cloud-functions.ts @@ -1,5 +1,4 @@ import { ChildProcess, spawn } from "child_process"; -import path from "node:path"; import { TestAccount } from "nodemailer"; import portfinder from "portfinder"; import { PREPARED_FIXTURES_PATH } from "../helpers/fixture"; @@ -22,15 +21,16 @@ export class CloudFunctions implements StubbedServer { this.port = await portfinder.getPortPromise(); this.functionsFrameworkProcess = spawn( - "functions-framework", + `${process.env.TEST_SRCDIR}/${process.env.TEST_WORKSPACE}/node_modules/.bin/functions-framework`, [ "--target=handleGithubWebhookEvent", "--signature-type=http", `--port=${this.port}`, + `--source=src/dist`, ], { stdio: "inherit", - cwd: path.join(__dirname, "..", "..", "..", "dist", "publish-to-bcr"), + // cwd: path.join(__dirname, "..", "..", "..", "dist", "publish-to-bcr"), env: { ...process.env, INTEGRATION_TESTING: "1", diff --git a/jest.config.js b/jest.config.js index 6152dc5..0e8bc22 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,21 +3,29 @@ * https://jestjs.io/docs/configuration */ +import path from "path"; + export default { // Automatically clear mock calls, instances, contexts and results before every test clearMocks: true, // The root directory that Jest should scan for tests and modules within. - rootDir: "build", + // rootDir: "build", // A list of paths to directories that Jest should use to search for files in. - roots: ["src", "e2e"], + // roots: ["src", "e2e"], // A list of paths to modules that run some code to configure or set up the testing framework before each test - setupFilesAfterEnv: ["./jest.setup.js"], + setupFilesAfterEnv: [ + path.join( + process.env.TEST_SRCDIR, + process.env.TEST_WORKSPACE, + "jest.setup.js" + ), + ], // The path to a module that can resolve test<->snapshot path. - snapshotResolver: "./jest-snapshot-resolver.js", + // snapshotResolver: "./jest-snapshot-resolver.js", // A map from regular expressions to paths to transformers transform: { diff --git a/package.json b/package.json index 1563c7d..119ca6f 100644 --- a/package.json +++ b/package.json @@ -64,14 +64,17 @@ "globby": "^14.0.0", "imapflow": "^1.0.147", "jest": "^29.7.0", + "jest-cli": "^29.7.0", + "jest-junit": "^16.0.0", "jest-extended": "^4.0.2", "jest-mock": "^29.7.0", "mailparser": "3.6.6", "mockttp": "^3.10.0", "portfinder": "^1.0.32", - "typescript": "^5.0.0" + "typescript": "5.6.3" }, "pnpm": { + "onlyBuiltDependencies": [], "packageExtensions": { "@google-cloud/secret-manager": { "dependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c2bfe1d..0fcd9a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,8 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +onlyBuiltDependencies: [] + packageExtensionsChecksum: 3f4e93adca9209d652f9612730512d6a dependencies: @@ -138,9 +140,15 @@ devDependencies: jest: specifier: ^29.7.0 version: 29.7.0(@types/node@18.19.70) + jest-cli: + specifier: ^29.7.0 + version: 29.7.0(@types/node@18.19.70) jest-extended: specifier: ^4.0.2 version: 4.0.2(jest@29.7.0) + jest-junit: + specifier: ^16.0.0 + version: 16.0.0 jest-mock: specifier: ^29.7.0 version: 29.7.0 @@ -154,8 +162,8 @@ devDependencies: specifier: ^1.0.32 version: 1.0.32 typescript: - specifier: ^5.0.0 - version: 5.7.3 + specifier: 5.6.3 + version: 5.6.3 packages: @@ -175,8 +183,8 @@ packages: js-tokens: 4.0.0 picocolors: 1.1.1 - /@babel/compat-data@7.26.3: - resolution: {integrity: sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==} + /@babel/compat-data@7.26.5: + resolution: {integrity: sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==} engines: {node: '>=6.9.0'} dev: true @@ -186,14 +194,14 @@ packages: dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.3 - '@babel/helper-compilation-targets': 7.25.9 + '@babel/generator': 7.26.5 + '@babel/helper-compilation-targets': 7.26.5 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) '@babel/helpers': 7.26.0 - '@babel/parser': 7.26.3 + '@babel/parser': 7.26.5 '@babel/template': 7.25.9 - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 convert-source-map: 2.0.0 debug: 4.4.0 gensync: 1.0.0-beta.2 @@ -203,12 +211,12 @@ packages: - supports-color dev: true - /@babel/generator@7.26.3: - resolution: {integrity: sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==} + /@babel/generator@7.26.5: + resolution: {integrity: sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 @@ -218,14 +226,14 @@ packages: resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.26.3 + '@babel/types': 7.26.5 dev: true - /@babel/helper-compilation-targets@7.25.9: - resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} + /@babel/helper-compilation-targets@7.26.5: + resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/compat-data': 7.26.3 + '@babel/compat-data': 7.26.5 '@babel/helper-validator-option': 7.25.9 browserslist: 4.24.4 lru-cache: 5.1.1 @@ -242,9 +250,9 @@ packages: '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-member-expression-to-functions': 7.25.9 '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0) '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.5 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -268,8 +276,8 @@ packages: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 debug: 4.4.0 lodash.debounce: 4.0.8 resolve: 1.22.10 @@ -281,8 +289,8 @@ packages: resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -291,8 +299,8 @@ packages: resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -306,7 +314,7 @@ packages: '@babel/core': 7.26.0 '@babel/helper-module-imports': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -315,11 +323,11 @@ packages: resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.26.3 + '@babel/types': 7.26.5 dev: true - /@babel/helper-plugin-utils@7.25.9: - resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} + /@babel/helper-plugin-utils@7.26.5: + resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} engines: {node: '>=6.9.0'} dev: true @@ -332,13 +340,13 @@ packages: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-wrap-function': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color dev: true - /@babel/helper-replace-supers@7.25.9(@babel/core@7.26.0): - resolution: {integrity: sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==} + /@babel/helper-replace-supers@7.26.5(@babel/core@7.26.0): + resolution: {integrity: sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -346,7 +354,7 @@ packages: '@babel/core': 7.26.0 '@babel/helper-member-expression-to-functions': 7.25.9 '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -355,8 +363,8 @@ packages: resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -380,8 +388,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.25.9 - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -391,15 +399,15 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.25.9 - '@babel/types': 7.26.3 + '@babel/types': 7.26.5 dev: true - /@babel/parser@7.26.3: - resolution: {integrity: sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==} + /@babel/parser@7.26.5: + resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.26.3 + '@babel/types': 7.26.5 dev: true /@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.0): @@ -409,8 +417,8 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -422,7 +430,7 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.26.0): @@ -432,7 +440,7 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.26.0): @@ -442,7 +450,7 @@ packages: '@babel/core': ^7.13.0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) transitivePeerDependencies: @@ -456,8 +464,8 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -477,7 +485,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.0): @@ -486,7 +494,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.0): @@ -495,7 +503,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.0): @@ -505,7 +513,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.0): @@ -515,7 +523,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.0): @@ -525,7 +533,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.0): @@ -534,7 +542,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.0): @@ -543,7 +551,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0): @@ -553,7 +561,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.0): @@ -562,7 +570,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.0): @@ -571,7 +579,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.0): @@ -580,7 +588,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.0): @@ -589,7 +597,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.0): @@ -598,7 +606,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.0): @@ -607,7 +615,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.0): @@ -617,7 +625,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.0): @@ -627,7 +635,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0): @@ -637,7 +645,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.0): @@ -648,7 +656,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.0): @@ -658,7 +666,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-async-generator-functions@7.25.9(@babel/core@7.26.0): @@ -668,9 +676,9 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -683,20 +691,20 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-module-imports': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-block-scoped-functions@7.25.9(@babel/core@7.26.0): - resolution: {integrity: sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==} + /@babel/plugin-transform-block-scoped-functions@7.26.5(@babel/core@7.26.0): + resolution: {integrity: sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.0): @@ -706,7 +714,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.0): @@ -717,7 +725,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -730,7 +738,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -743,10 +751,10 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) - '@babel/traverse': 7.26.4 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0) + '@babel/traverse': 7.26.5 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -759,7 +767,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/template': 7.25.9 dev: true @@ -770,7 +778,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.0): @@ -781,7 +789,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.0): @@ -791,7 +799,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0): @@ -802,7 +810,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.0): @@ -812,7 +820,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-exponentiation-operator@7.26.3(@babel/core@7.26.0): @@ -822,7 +830,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.0): @@ -832,7 +840,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.0): @@ -842,7 +850,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 transitivePeerDependencies: - supports-color @@ -855,9 +863,9 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -869,7 +877,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.0): @@ -879,7 +887,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.0): @@ -889,7 +897,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.0): @@ -899,7 +907,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.26.0): @@ -910,7 +918,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -923,7 +931,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -936,9 +944,9 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -951,7 +959,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -964,7 +972,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.0): @@ -974,17 +982,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true - /@babel/plugin-transform-nullish-coalescing-operator@7.25.9(@babel/core@7.26.0): - resolution: {integrity: sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==} + /@babel/plugin-transform-nullish-coalescing-operator@7.26.5(@babel/core@7.26.0): + resolution: {integrity: sha512-OHqczNm4NTQlW1ghrVY43FPoiRzbmzNVbcgVnMKZN/RQYezHUSdjACjaX50CD3B7UIAjv39+MlsrVDb3v741FA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.0): @@ -994,7 +1002,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.0): @@ -1004,8 +1012,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) dev: true @@ -1016,8 +1024,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0) transitivePeerDependencies: - supports-color dev: true @@ -1029,7 +1037,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.0): @@ -1039,7 +1047,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 transitivePeerDependencies: - supports-color @@ -1052,7 +1060,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.0): @@ -1063,7 +1071,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -1077,7 +1085,7 @@ packages: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color dev: true @@ -1089,7 +1097,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.0): @@ -1099,7 +1107,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 regenerator-transform: 0.15.2 dev: true @@ -1111,7 +1119,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.26.0): @@ -1121,7 +1129,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.0): @@ -1131,7 +1139,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.0): @@ -1141,7 +1149,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 transitivePeerDependencies: - supports-color @@ -1154,7 +1162,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-template-literals@7.25.9(@babel/core@7.26.0): @@ -1164,7 +1172,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-typeof-symbol@7.25.9(@babel/core@7.26.0): @@ -1174,7 +1182,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-unicode-escapes@7.25.9(@babel/core@7.26.0): @@ -1184,7 +1192,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.26.0): @@ -1195,7 +1203,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.26.0): @@ -1206,7 +1214,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.0): @@ -1217,7 +1225,7 @@ packages: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 dev: true /@babel/preset-env@7.26.0(@babel/core@7.26.0): @@ -1226,10 +1234,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.26.3 + '@babel/compat-data': 7.26.5 '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-validator-option': 7.25.9 '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.9(@babel/core@7.26.0) '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.9(@babel/core@7.26.0) @@ -1243,7 +1251,7 @@ packages: '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-async-generator-functions': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-block-scoped-functions': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-block-scoped-functions': 7.26.5(@babel/core@7.26.0) '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-class-static-block': 7.26.0(@babel/core@7.26.0) @@ -1268,7 +1276,7 @@ packages: '@babel/plugin-transform-modules-umd': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-new-target': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.26.5(@babel/core@7.26.0) '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-object-super': 7.25.9(@babel/core@7.26.0) @@ -1306,8 +1314,8 @@ packages: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/types': 7.26.3 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/types': 7.26.5 esutils: 2.0.3 dev: true @@ -1323,27 +1331,27 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.26.2 - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 dev: true - /@babel/traverse@7.26.4: - resolution: {integrity: sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==} + /@babel/traverse@7.26.5: + resolution: {integrity: sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.3 - '@babel/parser': 7.26.3 + '@babel/generator': 7.26.5 + '@babel/parser': 7.26.5 '@babel/template': 7.25.9 - '@babel/types': 7.26.3 + '@babel/types': 7.26.5 debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/types@7.26.3: - resolution: {integrity: sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==} + /@babel/types@7.26.5: + resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.25.9 @@ -1809,7 +1817,6 @@ packages: /@nestjs/core@10.4.15(@nestjs/common@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1): resolution: {integrity: sha512-UBejmdiYwaH6fTsz2QFBlC1cJHM+3UDeLZN+CiP9I1fRv2KlBZsmozGLbV5eS1JAVWJB4T5N5yQ0gjN8ZvcS2w==} - requiresBuild: true peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/microservices': ^10.0.0 @@ -2183,8 +2190,8 @@ packages: /@types/babel__core@7.20.5: resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} dependencies: - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.6 @@ -2193,20 +2200,20 @@ packages: /@types/babel__generator@7.6.8: resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} dependencies: - '@babel/types': 7.26.3 + '@babel/types': 7.26.5 dev: true /@types/babel__template@7.4.4: resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} dependencies: - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 dev: true /@types/babel__traverse@7.20.6: resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} dependencies: - '@babel/types': 7.26.3 + '@babel/types': 7.26.5 dev: true /@types/body-parser@1.19.5: @@ -2414,7 +2421,6 @@ packages: /@types/yauzl@2.10.3: resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - requiresBuild: true dependencies: '@types/node': 18.19.70 dev: false @@ -2622,7 +2628,7 @@ packages: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} dependencies: - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 5.2.1 @@ -2636,7 +2642,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/template': 7.25.9 - '@babel/types': 7.26.3 + '@babel/types': 7.26.5 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.6 dev: true @@ -2646,7 +2652,7 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/compat-data': 7.26.3 + '@babel/compat-data': 7.26.5 '@babel/core': 7.26.0 '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) semver: 6.3.1 @@ -2719,9 +2725,8 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true - /bare-events@2.5.3: - resolution: {integrity: sha512-pCO3aoRJ0MBiRMu8B7vUga0qL3L7gO1+SW7ku6qlSsMLwuhaawnuvZDyzJY/kyC63Un0XAB0OPUcfF1eTO/V+Q==} - requiresBuild: true + /bare-events@2.5.4: + resolution: {integrity: sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==} dev: true optional: true @@ -3626,7 +3631,6 @@ packages: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - requiresBuild: true dev: true optional: true @@ -4173,7 +4177,7 @@ packages: engines: {node: '>=8'} dependencies: '@babel/core': 7.26.0 - '@babel/parser': 7.26.3 + '@babel/parser': 7.26.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -4186,7 +4190,7 @@ packages: engines: {node: '>=10'} dependencies: '@babel/core': 7.26.0 - '@babel/parser': 7.26.3 + '@babel/parser': 7.26.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.6.3 @@ -4430,6 +4434,16 @@ packages: fsevents: 2.3.3 dev: true + /jest-junit@16.0.0: + resolution: {integrity: sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==} + engines: {node: '>=10.12.0'} + dependencies: + mkdirp: 1.0.4 + strip-ansi: 6.0.1 + uuid: 8.3.2 + xml: 1.0.1 + dev: true + /jest-leak-detector@29.7.0: resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4603,10 +4617,10 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.26.0 - '@babel/generator': 7.26.3 + '@babel/generator': 7.26.5 '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) - '@babel/types': 7.26.3 + '@babel/types': 7.26.5 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 @@ -5085,7 +5099,6 @@ packages: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true - dev: false /mockttp@3.15.5: resolution: {integrity: sha512-Efhz6RcAga1Zn93W5XWOwUSvIq9NNhJHig4gCCm6whemDKgt7aYalhrGIi7sdK7Cn7ipbG3x9n3ux+TljB+o6w==} @@ -5492,7 +5505,6 @@ packages: /protobufjs@7.4.0: resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} engines: {node: '>=12.0.0'} - requiresBuild: true dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -6036,7 +6048,7 @@ packages: queue-tick: 1.0.1 text-decoder: 1.2.3 optionalDependencies: - bare-events: 2.5.3 + bare-events: 2.5.4 dev: true /string-length@4.0.2: @@ -6226,8 +6238,8 @@ packages: engines: {node: '>=6.0.0', npm: '>=3.0.0'} dev: true - /typescript@5.7.3: - resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + /typescript@5.6.3: + resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} engines: {node: '>=14.17'} hasBin: true dev: true @@ -6422,6 +6434,10 @@ packages: optional: true dev: true + /xml@1.0.1: + resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} + dev: true + /xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} diff --git a/src/BUILD.bazel b/src/BUILD.bazel new file mode 100644 index 0000000..eb13736 --- /dev/null +++ b/src/BUILD.bazel @@ -0,0 +1,16 @@ +load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory") + +copy_to_directory( + name = "dist", + srcs = [ + "//:package_json", + "//:pnpm-lock.yaml", + "//src/application", + "//src/application/webhook", + "//src/domain", + "//src/infrastructure", + "//src/infrastructure/xzdec", + "//src/infrastructure/xzdec:xzdec_wasm_gz", + ], + visibility = ["//e2e:__subpackages__"], +) diff --git a/src/application/BUILD.bazel b/src/application/BUILD.bazel new file mode 100644 index 0000000..2b8d956 --- /dev/null +++ b/src/application/BUILD.bazel @@ -0,0 +1,22 @@ +load("//bazel/ts:defs.bzl", "ts_project") + +ts_project( + name = "application", + srcs = [ + "notifications.ts", + "octokit.ts", + "release-event-handler.ts", + ], + visibility = [ + "//src:__pkg__", + "//src/application:__subpackages__", + ], + deps = [ + "//:node_modules/@nestjs/common", + "//:node_modules/@octokit/rest", + "//:node_modules/@octokit/webhooks", + "//:node_modules/@octokit/webhooks-types", + "//src/domain", + "//src/infrastructure", + ], +) diff --git a/src/application/webhook/BUILD.bazel b/src/application/webhook/BUILD.bazel new file mode 100644 index 0000000..46e0c03 --- /dev/null +++ b/src/application/webhook/BUILD.bazel @@ -0,0 +1,24 @@ +load("//bazel/ts:defs.bzl", "ts_project") + +ts_project( + name = "webhook", + srcs = [ + "app.module.ts", + "index.ts", + "main.ts", + "providers.ts", + ], + visibility = ["//src:__pkg__"], + deps = [ + "//:node_modules/@google-cloud/functions-framework", + "//:node_modules/@nestjs/common", + "//:node_modules/@nestjs/core", + "//:node_modules/@octokit/rest", + "//:node_modules/@octokit/webhooks", + "//:node_modules/@types/source-map-support", + "//:node_modules/source-map-support", + "//src/application", + "//src/domain", + "//src/infrastructure", + ], +) diff --git a/src/domain/BUILD.bazel b/src/domain/BUILD.bazel new file mode 100644 index 0000000..fea7c4a --- /dev/null +++ b/src/domain/BUILD.bazel @@ -0,0 +1,79 @@ +load("//bazel/jest:defs.bzl", "jest_test") +load("//bazel/ts:defs.bzl", "ts_project") + +ts_project( + name = "domain", + srcs = [ + "config.ts", + "create-entry.ts", + "error.ts", + "find-registry-fork.ts", + "integrity-hash.ts", + "metadata-file.ts", + "module-file.ts", + "publish-entry.ts", + "release-archive.ts", + "repository.ts", + "ruleset-repository.ts", + "source-template.ts", + "user.ts", + "version.ts", + ], + visibility = ["//src:__subpackages__"], + deps = [ + "//:node_modules/@nestjs/common", + "//:node_modules/@types/diff", + "//:node_modules/@types/node", + "//:node_modules/@types/tar", + "//:node_modules/axios", + "//:node_modules/axios-retry", + "//:node_modules/diff", + "//:node_modules/exponential-backoff", + "//:node_modules/extract-zip", + "//:node_modules/tar", + "//:node_modules/yaml", + "//src/infrastructure", + "//src/infrastructure/xzdec", + ], +) + +ts_project( + name = "domain_tests", + testonly = True, + srcs = [ + "create-entry.spec.ts", + "find-registry-fork.spec.ts", + "integrity-hash.spec.ts", + "metadata-file.spec.ts", + "module-file.spec.ts", + "publish-entry.spec.ts", + "release-archive.spec.ts", + "repository.spec.ts", + "ruleset-repository.spec.ts", + "source-template.spec.ts", + "version.spec.ts", + ], + deps = [ + ":domain", + "//:node_modules/@types/diff", + "//:node_modules/@types/jest", # keep + "//:node_modules/@types/node", + "//:node_modules/@types/tar", + "//:node_modules/axios", + "//:node_modules/axios-retry", + "//:node_modules/diff", + "//:node_modules/exponential-backoff", + "//:node_modules/jest-extended", # keep + "//:node_modules/jest-mock", + "//:node_modules/tar", + "//src/infrastructure", + "//src/test", + ], +) + +jest_test( + name = "test", + data = [ + ":domain_tests", + ], +) diff --git a/src/domain/find-registry-fork.spec.ts b/src/domain/find-registry-fork.spec.ts index 21f17a6..f2ad601 100644 --- a/src/domain/find-registry-fork.spec.ts +++ b/src/domain/find-registry-fork.spec.ts @@ -63,7 +63,7 @@ describe("findCandidateForks", () => { mockRepositoryService.getSourceRepository.mockImplementation( async (repository) => { - if (repository.equals(ownerBcrFork)) { + if (repository.owner === ownerBcrFork.owner && repository.name === ownerBcrFork.name) { return CANONICAL_BCR; } return repository; @@ -110,7 +110,7 @@ describe("findCandidateForks", () => { mockRepositoryService.getSourceRepository.mockImplementation( async (repository) => { - if (repository.equals(releaserBcrFork)) { + if (repository.owner === releaserBcrFork.owner && repository.name === releaserBcrFork.name) { return CANONICAL_BCR; } return repository; @@ -161,8 +161,8 @@ describe("findCandidateForks", () => { mockRepositoryService.getSourceRepository.mockImplementation( async (repository) => { if ( - repository.equals(releaserBcrFork) || - repository.equals(ownerBcrFork) + (repository.owner === releaserBcrFork.owner && repository.name === releaserBcrFork.name) || + (repository.owner === ownerBcrFork.owner && repository.name === ownerBcrFork.name) ) { return CANONICAL_BCR; } @@ -172,7 +172,8 @@ describe("findCandidateForks", () => { mockRepositoryService.hasAppInstallation.mockImplementation( async (repository) => - repository.equals(ownerBcrFork) || repository.equals(releaserBcrFork) + (repository.owner === releaserBcrFork.owner && repository.name === releaserBcrFork.name) || + (repository.owner === ownerBcrFork.owner && repository.name === ownerBcrFork.name) ); const forks = await findRegistryForkService.findCandidateForks( @@ -210,7 +211,7 @@ describe("findCandidateForks", () => { mockRepositoryService.getSourceRepository.mockImplementation( async (repository) => { - if (repository.equals(ownerBcrFork)) { + if (repository.owner === ownerBcrFork.owner && repository.name === ownerBcrFork.name) { return new Repository("bazel-central-registry", "not-google"); } return repository; @@ -251,7 +252,7 @@ describe("findCandidateForks", () => { mockRepositoryService.getSourceRepository.mockImplementation( async (repository) => { - if (repository.equals(releaserBcrFork)) { + if (repository.owner === releaserBcrFork.owner && repository.name === releaserBcrFork.name) { return CANONICAL_BCR; } return repository; diff --git a/src/domain/find-registry-fork.ts b/src/domain/find-registry-fork.ts index 44ee887..3631e05 100644 --- a/src/domain/find-registry-fork.ts +++ b/src/domain/find-registry-fork.ts @@ -36,12 +36,12 @@ export class FindRegistryForkService { potentialForkOwners.add(releaser.username); const allForks = ( - await Promise.all( + (await Promise.all( Array.from(potentialForkOwners.values()).map((owner) => this.repositoryService.getForkedRepositoriesByOwner(owner) ) ) - ).reduce((acc, curr) => acc.concat(curr), []); + ).reduce((acc, curr) => acc.concat(curr), [])).map(r => new Repository(r.name, r.owner)); let candidateForks = allForks.filter( (repo) => repo.name === "bazel-central-registry" diff --git a/src/domain/metadata-file.spec.ts b/src/domain/metadata-file.spec.ts index 8c1ba4a..1f68436 100644 --- a/src/domain/metadata-file.spec.ts +++ b/src/domain/metadata-file.spec.ts @@ -1,6 +1,6 @@ +import "jest-extended"; import { mocked } from "jest-mock"; import fs from "node:fs"; -import "../../jest.setup"; import { MetadataFile, MetadataFileError } from "./metadata-file"; jest.mock("node:fs"); diff --git a/src/domain/release-archive.spec.ts b/src/domain/release-archive.spec.ts index e33984b..5ec3c1c 100644 --- a/src/domain/release-archive.spec.ts +++ b/src/domain/release-archive.spec.ts @@ -5,7 +5,6 @@ import fs, { WriteStream } from "node:fs"; import os from "node:os"; import path from "node:path"; import tar from "tar"; -import "../../jest.setup"; import { fakeModuleFile } from "../test/mock-template-files"; import { expectThrownError } from "../test/util"; import { diff --git a/src/infrastructure/BUILD.bazel b/src/infrastructure/BUILD.bazel new file mode 100644 index 0000000..2f9e672 --- /dev/null +++ b/src/infrastructure/BUILD.bazel @@ -0,0 +1,29 @@ +load("//bazel/ts:defs.bzl", "ts_project") + +ts_project( + name = "infrastructure", + srcs = [ + "email.ts", + "git.ts", + "github.ts", + "secrets.ts", + ], + visibility = [ + "//e2e:__pkg__", + "//src:__pkg__", + "//src/application:__subpackages__", + "//src/domain:__subpackages__", + ], + deps = [ + "//:node_modules/@google-cloud/secret-manager", + "//:node_modules/@nestjs/common", + "//:node_modules/@octokit/auth-app", + "//:node_modules/@octokit/rest", + "//:node_modules/@octokit/types", + "//:node_modules/@types/nodemailer", + "//:node_modules/gcp-metadata", + "//:node_modules/google-auth-library", + "//:node_modules/nodemailer", + "//:node_modules/simple-git", + ], +) diff --git a/src/infrastructure/xzdec/.gitignore b/src/infrastructure/xzdec/.gitignore deleted file mode 100644 index 2f0f755..0000000 --- a/src/infrastructure/xzdec/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/bazel-* -/MODULE.bazel.lock diff --git a/src/infrastructure/xzdec/BUILD.bazel b/src/infrastructure/xzdec/BUILD.bazel index 2ae7e30..6d8e73f 100644 --- a/src/infrastructure/xzdec/BUILD.bazel +++ b/src/infrastructure/xzdec/BUILD.bazel @@ -1,8 +1,9 @@ load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_file") +load("//bazel/ts:defs.bzl", "ts_project") load(":wasm.bzl", "wasm_binary") cc_binary( - name = "xzdec", + name = "xzdec_cc", srcs = ["xzdec.c"], linkopts = [ "-nostdlib", @@ -16,18 +17,24 @@ cc_binary( wasm_binary( name = "xzdec_wasm", out = "xzdec.wasm", - lib = ":xzdec", + lib = ":xzdec_cc", ) genrule( name = "xzdec_wasm_gz", srcs = [":xzdec_wasm"], - outs = ["xzdec_wasm_gz/xzdec.wasm.gz"], + outs = ["xzdec.wasm.gz"], cmd = "cat $< | gzip -9 -k -n > $@", + visibility = ["//src:__pkg__"], ) -write_source_file( - name = "write_xzdec_wasm_gz_to_source_tree", - in_file = ":xzdec_wasm_gz", - out_file = "xzdec.wasm.gz", +ts_project( + name = "xzdec", + srcs = ["xzdec.ts"], + data = [":xzdec_wasm_gz"], + visibility = [ + "//src:__pkg__", + "//src/domain:__subpackages__", + ], + deps = ["//:node_modules/@types/node"], ) diff --git a/src/infrastructure/xzdec/MODULE.bazel b/src/infrastructure/xzdec/MODULE.bazel deleted file mode 100644 index 9aab199..0000000 --- a/src/infrastructure/xzdec/MODULE.bazel +++ /dev/null @@ -1,45 +0,0 @@ -module(name = "publish-to-bcr") - -bazel_dep(name = "aspect_bazel_lib", version = "2.9.3") -bazel_dep(name = "platforms", version = "0.0.10") -bazel_dep(name = "toolchains_llvm", version = "1.2.0") -bazel_dep(name = "xz", version = "5.4.5.bcr.5") - -# https://github.com/bazel-contrib/toolchains_llvm/pull/405 -# -# FIXME: Remove when a new `toolchains_llvm` has been released. -git_override( - module_name = "toolchains_llvm", - commit = "bda1c9fbf232b682c30d039f8e4a5e3cf3025d0f", - remote = "https://github.com/bazel-contrib/toolchains_llvm", -) - -llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm") -llvm.toolchain( - libclang_rt = { - "@libclang_rt-wasm32-wasi//:libclang_rt.builtins-wasm32.a": "wasm32-unknown-unknown/libclang_rt.builtins.a", - }, - llvm_versions = { - # Pin to an older LLVM version due to a stray Homebrew dependency - # in the macOS build of v19.1.0. - # - # https://github.com/llvm/llvm-project/issues/110070 - "": "18.1.8", - }, - stdlib = {"wasm32": "libc"}, -) -llvm.sysroot( - label = "@wasi-sysroot//sysroots/wasm32-wasip2", - targets = ["wasm32"], -) -use_repo(llvm, "llvm_toolchain") - -register_toolchains("@llvm_toolchain//:all") - -wasi_sysroot = use_repo_rule("//:wasm.bzl", "wasi_sysroot") - -wasm32_libclang_rt = use_repo_rule("//:wasm.bzl", "wasm32_libclang_rt") - -wasi_sysroot(name = "wasi-sysroot") - -wasm32_libclang_rt(name = "libclang_rt-wasm32-wasi") diff --git a/src/infrastructure/xzdec/xzdec.wasm.gz b/src/infrastructure/xzdec/xzdec.wasm.gz deleted file mode 100644 index c3c457bddb7239b5d1f947ca13f6b6fa98d9f2a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55677 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

iq3xuRF=Fe^S|*NsmM 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 diff --git a/src/test/BUILD.bazel b/src/test/BUILD.bazel new file mode 100644 index 0000000..fb0d8c1 --- /dev/null +++ b/src/test/BUILD.bazel @@ -0,0 +1,15 @@ +load("//bazel/ts:defs.bzl", "ts_project") + +ts_project( + name = "test", + srcs = [ + "mock-template-files.ts", + "util.ts", + ], + visibility = ["//src/domain:__pkg__"], + deps = [ + "//:node_modules/@types/jest", # keep + "//:node_modules/@types/node", + "//src/domain", + ], +) diff --git a/tsconfig.json b/tsconfig.json index 7434af5..97c0907 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,6 @@ "noImplicitAny": true, "esModuleInterop": true, "sourceMap": true, - "outDir": "build", "module": "es2020", "target": "es2020", "moduleResolution": "node", @@ -11,7 +10,7 @@ "experimentalDecorators": true, "emitDecoratorMetadata": true, "allowJs": true, - "types": ["jest", "node"] + "skipLibCheck": true }, "include": ["src/**/*.ts", "e2e/**/*.ts", "*.js"], "exclude": ["node_modules", "src/**/bazel-*"]