mirror of
https://github.com/hoverkraft-tech/compose-action.git
synced 2026-07-04 04:32:51 +08:00
Compare commits
No commits in common. "main" and "v2.6.0" have entirely different histories.
@ -2,8 +2,8 @@
|
|||||||
"name": "Debian",
|
"name": "Debian",
|
||||||
"image": "mcr.microsoft.com/devcontainers/base:bullseye",
|
"image": "mcr.microsoft.com/devcontainers/base:bullseye",
|
||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers/features/node:2": {},
|
"ghcr.io/devcontainers/features/node:1": {},
|
||||||
"ghcr.io/devcontainers/features/docker-in-docker:": {},
|
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
|
||||||
"ghcr.io/devcontainers/features/github-cli:1": {}
|
"ghcr.io/devcontainers/features/github-cli:1": {}
|
||||||
},
|
},
|
||||||
"remoteEnv": {
|
"remoteEnv": {
|
||||||
@ -18,9 +18,8 @@
|
|||||||
"github.vscode-github-actions",
|
"github.vscode-github-actions",
|
||||||
"ms-vscode.makefile-tools",
|
"ms-vscode.makefile-tools",
|
||||||
"bierner.markdown-preview-github-styles",
|
"bierner.markdown-preview-github-styles",
|
||||||
"esbenp.prettier-vscode",
|
"dbaeumer.vscode-eslint",
|
||||||
"biomejs.biome",
|
"esbenp.prettier-vscode"
|
||||||
"vitest.explorer"
|
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
"terminal.integrated.defaultProfile.linux": "zsh"
|
"terminal.integrated.defaultProfile.linux": "zsh"
|
||||||
|
|||||||
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@ -3,8 +3,6 @@ updates:
|
|||||||
- package-ecosystem: github-actions
|
- package-ecosystem: github-actions
|
||||||
directory: /
|
directory: /
|
||||||
open-pull-requests-limit: 20
|
open-pull-requests-limit: 20
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: weekly
|
||||||
day: friday
|
day: friday
|
||||||
@ -19,8 +17,6 @@ updates:
|
|||||||
- "/test"
|
- "/test"
|
||||||
- "/"
|
- "/"
|
||||||
open-pull-requests-limit: 20
|
open-pull-requests-limit: 20
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: weekly
|
||||||
day: friday
|
day: friday
|
||||||
@ -33,8 +29,6 @@ updates:
|
|||||||
- package-ecosystem: docker-compose
|
- package-ecosystem: docker-compose
|
||||||
directory: "/test"
|
directory: "/test"
|
||||||
open-pull-requests-limit: 20
|
open-pull-requests-limit: 20
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: weekly
|
||||||
day: friday
|
day: friday
|
||||||
@ -48,8 +42,6 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
open-pull-requests-limit: 20
|
open-pull-requests-limit: 20
|
||||||
versioning-strategy: increase
|
versioning-strategy: increase
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: weekly
|
||||||
day: friday
|
day: friday
|
||||||
@ -70,8 +62,6 @@ updates:
|
|||||||
- package-ecosystem: "devcontainers"
|
- package-ecosystem: "devcontainers"
|
||||||
open-pull-requests-limit: 20
|
open-pull-requests-limit: 20
|
||||||
directory: "/"
|
directory: "/"
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: weekly
|
||||||
day: friday
|
day: friday
|
||||||
|
|||||||
4
.github/workflows/__check-action.yml
vendored
4
.github/workflows/__check-action.yml
vendored
@ -181,13 +181,13 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
DOCKER_COMPOSE_VERSION: ${{ matrix.expected-compose-version || '' }}
|
DOCKER_COMPOSE_VERSION: ${{ matrix.expected-compose-version || '' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Set up Docker context
|
- name: Set up Docker context
|
||||||
if: ${{ matrix.docker-context }}
|
if: ${{ matrix.docker-context }}
|
||||||
uses: docker/setup-docker-action@0234bb73ccb40f0c430b795634f9247e2b5c2d23 # v5.2.0
|
uses: docker/setup-docker-action@1a6edb0ba9ac496f6850236981f15d8f9a82254d # v5.0.0
|
||||||
with:
|
with:
|
||||||
context: ${{ matrix.docker-context }}
|
context: ${{ matrix.docker-context }}
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/__check-dist.yml
vendored
4
.github/workflows/__check-dist.yml
vendored
@ -12,12 +12,12 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- id: setup-node
|
- id: setup-node
|
||||||
uses: hoverkraft-tech/ci-github-nodejs/actions/setup-node@df348077afa4e79725151d50606e9dc63f86dcb6 # 0.24.4
|
uses: hoverkraft-tech/ci-github-nodejs/actions/setup-node@775ce0902c528062cc94141dd7d13261083b752a # 0.22.0
|
||||||
|
|
||||||
- name: Build dist/ Directory
|
- name: Build dist/ Directory
|
||||||
id: package
|
id: package
|
||||||
|
|||||||
2
.github/workflows/__check-nodejs.yml
vendored
2
.github/workflows/__check-nodejs.yml
vendored
@ -7,7 +7,7 @@ permissions: {}
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-nodejs:
|
test-nodejs:
|
||||||
uses: hoverkraft-tech/ci-github-nodejs/.github/workflows/continuous-integration.yml@df348077afa4e79725151d50606e9dc63f86dcb6 # 0.24.4
|
uses: hoverkraft-tech/ci-github-nodejs/.github/workflows/continuous-integration.yml@775ce0902c528062cc94141dd7d13261083b752a # 0.22.0
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
|
|||||||
8
.github/workflows/__shared-ci.yml
vendored
8
.github/workflows/__shared-ci.yml
vendored
@ -7,18 +7,15 @@ permissions: {}
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
linter:
|
linter:
|
||||||
uses: hoverkraft-tech/ci-github-common/.github/workflows/linter.yml@624be17604ee0a7378488191aacb35851e7cf001 # 0.37.1
|
uses: hoverkraft-tech/ci-github-common/.github/workflows/linter.yml@4b53189212d5810f710bed89711002626977215b # 0.33.0
|
||||||
permissions:
|
permissions:
|
||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
issues: write
|
|
||||||
packages: read
|
|
||||||
pull-requests: write
|
|
||||||
security-events: write
|
security-events: write
|
||||||
statuses: write
|
statuses: write
|
||||||
with:
|
with:
|
||||||
linter-env: |
|
linter-env: |
|
||||||
FILTER_REGEX_EXCLUDE=dist/**/*|.github/social-preview.svg|.github/logo.svg
|
FILTER_REGEX_EXCLUDE=dist/**/*
|
||||||
VALIDATE_JSCPD=false
|
VALIDATE_JSCPD=false
|
||||||
VALIDATE_TYPESCRIPT_STANDARD=false
|
VALIDATE_TYPESCRIPT_STANDARD=false
|
||||||
VALIDATE_TYPESCRIPT_ES=false
|
VALIDATE_TYPESCRIPT_ES=false
|
||||||
@ -36,6 +33,7 @@ jobs:
|
|||||||
packages: read
|
packages: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
security-events: write
|
security-events: write
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
check-dist:
|
check-dist:
|
||||||
name: Test nodejs
|
name: Test nodejs
|
||||||
|
|||||||
4
.github/workflows/greetings.yml
vendored
4
.github/workflows/greetings.yml
vendored
@ -3,14 +3,14 @@ name: Greetings
|
|||||||
on:
|
on:
|
||||||
issues:
|
issues:
|
||||||
types: [opened]
|
types: [opened]
|
||||||
pull_request_target: # zizmor: ignore[dangerous-triggers] metadata-only reusable workflow with explicit write permissions and no checkout
|
pull_request_target:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
|
|
||||||
permissions: {}
|
permissions: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
greetings:
|
greetings:
|
||||||
uses: hoverkraft-tech/ci-github-common/.github/workflows/greetings.yml@624be17604ee0a7378488191aacb35851e7cf001 # 0.37.1
|
uses: hoverkraft-tech/ci-github-common/.github/workflows/greetings.yml@4b53189212d5810f710bed89711002626977215b # 0.33.0
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
issues: write
|
issues: write
|
||||||
|
|||||||
25
.github/workflows/main-ci.yml
vendored
25
.github/workflows/main-ci.yml
vendored
@ -23,11 +23,11 @@ jobs:
|
|||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
issues: write
|
|
||||||
packages: read
|
packages: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
security-events: write
|
security-events: write
|
||||||
statuses: write
|
statuses: write
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
prepare-docs:
|
prepare-docs:
|
||||||
needs: ci
|
needs: ci
|
||||||
@ -38,22 +38,17 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
artifact-id: ${{ steps.upload-artifact.outputs.artifact-id }}
|
artifact-id: ${{ steps.upload-artifact.outputs.artifact-id }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: 📖 Generate documentation
|
- name: 📖 Generate documentation
|
||||||
id: generate-documentation
|
id: generate-documentation
|
||||||
uses: hoverkraft-tech/ci-dokumentor@3258ef0de948ec25e5939618d5ef510445aed869 # 0.3.1
|
uses: hoverkraft-tech/ci-dokumentor@c46a1a108957237cf485103a80b060c35c7dba33 # 0.2.2
|
||||||
with:
|
with:
|
||||||
source: action.yml
|
source: action.yml
|
||||||
# yamllint disable rule:line-length
|
|
||||||
extra-badges: |
|
extra-badges: |
|
||||||
[
|
[
|
||||||
{
|
|
||||||
"label": "GitHub Verified Creator",
|
|
||||||
"url": "https://img.shields.io/badge/GitHub-Verified%20Creator-4493F8?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJyZ2IoNjgsIDE0NywgMjQ4KSI+CiAgPHBhdGggZD0ibTkuNTg1LjUyLjkyOS42OGMuMTUzLjExMi4zMzEuMTg2LjUxOC4yMTVsMS4xMzguMTc1YTIuNjc4IDIuNjc4IDAgMCAxIDIuMjQgMi4yNGwuMTc0IDEuMTM5Yy4wMjkuMTg3LjEwMy4zNjUuMjE1LjUxOGwuNjguOTI4YTIuNjc3IDIuNjc3IDAgMCAxIDAgMy4xN2wtLjY4LjkyOGExLjE3NCAxLjE3NCAwIDAgMC0uMjE1LjUxOGwtLjE3NSAxLjEzOGEyLjY3OCAyLjY3OCAwIDAgMS0yLjI0MSAyLjI0MWwtMS4xMzguMTc1YTEuMTcgMS4xNyAwIDAgMC0uNTE4LjIxNWwtLjkyOC42OGEyLjY3NyAyLjY3NyAwIDAgMS0zLjE3IDBsLS45MjgtLjY4YTEuMTc0IDEuMTc0IDAgMCAwLS41MTgtLjIxNUwzLjgzIDE0LjQxYTIuNjc4IDIuNjc4IDAgMCAxLTIuMjQtMi4yNGwtLjE3NS0xLjEzOGExLjE3IDEuMTcgMCAwIDAtLjIxNS0uNTE4bC0uNjgtLjkyOGEyLjY3NyAyLjY3NyAwIDAgMSAwLTMuMTdsLjY4LS45MjhjLjExMi0uMTUzLjE4Ni0uMzMxLjIxNS0uNTE4bC4xNzUtMS4xNGEyLjY3OCAyLjY3OCAwIDAgMSAyLjI0LTIuMjRsMS4xMzktLjE3NWMuMTg3LS4wMjkuMzY1LS4xMDMuNTE4LS4yMTVsLjkyOC0uNjhhMi42NzcgMi42NzcgMCAwIDEgMy4xNyAwWk03LjMwMyAxLjcyOGwtLjkyNy42OGEyLjY3IDIuNjcgMCAwIDEtMS4xOC40ODlsLTEuMTM3LjE3NGExLjE3OSAxLjE3OSAwIDAgMC0uOTg3Ljk4N2wtLjE3NCAxLjEzNmEyLjY3NyAyLjY3NyAwIDAgMS0uNDg5IDEuMThsLS42OC45MjhhMS4xOCAxLjE4IDAgMCAwIDAgMS4zOTRsLjY4LjkyN2MuMjU2LjM0OC40MjQuNzUzLjQ4OSAxLjE4bC4xNzQgMS4xMzdjLjA3OC41MDkuNDc4LjkwOS45ODcuOTg3bDEuMTM2LjE3NGEyLjY3IDIuNjcgMCAwIDEgMS4xOC40ODlsLjkyOC42OGMuNDE0LjMwNS45NzkuMzA1IDEuMzk0IDBsLjkyNy0uNjhhMi42NyAyLjY3IDAgMCAxIDEuMTgtLjQ4OWwxLjEzNy0uMTc0YTEuMTggMS4xOCAwIDAgMCAuOTg3LS45ODdsLjE3NC0xLjEzNmEyLjY3IDIuNjcgMCAwIDEgLjQ4OS0xLjE4bC42OC0uOTI4YTEuMTc2IDEuMTc2IDAgMCAwIDAtMS4zOTRsLS42OC0uOTI3YTIuNjg2IDIuNjg2IDAgMCAxLS40ODktMS4xOGwtLjE3NC0xLjEzN2ExLjE3OSAxLjE3OSAwIDAgMC0uOTg3LS45ODdsLTEuMTM2LS4xNzRhMi42NzcgMi42NzcgMCAwIDEtMS4xOC0uNDg5bC0uOTI4LS42OGExLjE3NiAxLjE3NiAwIDAgMC0xLjM5NCAwWk0xMS4yOCA2Ljc4bC0zLjc1IDMuNzVhLjc1Ljc1IDAgMCAxLTEuMDYgMEw0LjcyIDguNzhhLjc1MS43NTEgMCAwIDEgLjAxOC0xLjA0Mi43NTEuNzUxIDAgMCAxIDEuMDQyLS4wMThMNyA4Ljk0bDMuMjItMy4yMmEuNzUxLjc1MSAwIDAgMSAxLjA0Mi4wMTguNzUxLjc1MSAwIDAgMSAuMDE4IDEuMDQyWiI+PC9wYXRoPgo8L3N2Zz4K"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label":"codecov",
|
"label":"codecov",
|
||||||
"url":"https://codecov.io/gh/hoverkraft-tech/compose-action/graph/badge.svg?token=90JXB7EIMA",
|
"url":"https://codecov.io/gh/hoverkraft-tech/compose-action/graph/badge.svg?token=90JXB7EIMA",
|
||||||
@ -61,13 +56,13 @@ jobs:
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
- uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
|
||||||
id: generate-token
|
id: generate-token
|
||||||
with:
|
with:
|
||||||
client-id: ${{ vars.CI_BOT_APP_CLIENT_ID }}
|
app-id: ${{ vars.CI_BOT_APP_ID }}
|
||||||
private-key: ${{ secrets.CI_BOT_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] repository automation uses a dedicated app secret without untrusted code execution
|
private-key: ${{ secrets.CI_BOT_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
- uses: hoverkraft-tech/ci-github-common/actions/create-and-merge-pull-request@624be17604ee0a7378488191aacb35851e7cf001 # 0.37.1
|
- uses: hoverkraft-tech/ci-github-common/actions/create-and-merge-pull-request@4b53189212d5810f710bed89711002626977215b # 0.33.0
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.generate-token.outputs.token }}
|
github-token: ${{ steps.generate-token.outputs.token }}
|
||||||
branch: docs/actions-workflows-documentation-update
|
branch: docs/actions-workflows-documentation-update
|
||||||
@ -79,7 +74,7 @@ jobs:
|
|||||||
[skip ci]
|
[skip ci]
|
||||||
|
|
||||||
- id: upload-artifact
|
- id: upload-artifact
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: documentation-files-${{ github.run_id }}
|
name: documentation-files-${{ github.run_id }}
|
||||||
path: |
|
path: |
|
||||||
@ -89,11 +84,11 @@ jobs:
|
|||||||
sync-docs:
|
sync-docs:
|
||||||
needs: prepare-docs
|
needs: prepare-docs
|
||||||
if: needs.prepare-docs.outputs.artifact-id
|
if: needs.prepare-docs.outputs.artifact-id
|
||||||
uses: hoverkraft-tech/public-docs/.github/workflows/sync-docs-dispatcher.yml@f3c9291760d927e6214e8d5f0a376af2d537c369 # 0.4.0
|
uses: hoverkraft-tech/public-docs/.github/workflows/sync-docs-dispatcher.yml@c40c17f7d6a8090950b3ef4bfc70502707a6bb9f # 0.3.0
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
with:
|
with:
|
||||||
artifact-id: ${{ needs.prepare-docs.outputs.artifact-id }}
|
artifact-id: ${{ needs.prepare-docs.outputs.artifact-id }}
|
||||||
github-app-client-id: ${{ vars.CI_BOT_APP_CLIENT_ID }}
|
github-app-id: ${{ vars.CI_BOT_APP_ID }}
|
||||||
secrets:
|
secrets:
|
||||||
github-app-key: ${{ secrets.CI_BOT_APP_PRIVATE_KEY }}
|
github-app-key: ${{ secrets.CI_BOT_APP_PRIVATE_KEY }}
|
||||||
|
|||||||
7
.github/workflows/need-fix-to-issue.yml
vendored
7
.github/workflows/need-fix-to-issue.yml
vendored
@ -11,16 +11,15 @@ on:
|
|||||||
description: "The SHA of the commit to get the diff for"
|
description: "The SHA of the commit to get the diff for"
|
||||||
required: true
|
required: true
|
||||||
manual-base-ref:
|
manual-base-ref:
|
||||||
description: >-
|
description: "By default, the commit entered above is compared to the one directly
|
||||||
By default, the commit entered above is compared to the one directly
|
before it; to go back further, enter an earlier SHA here"
|
||||||
before it; to go back further, enter an earlier SHA here
|
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
permissions: {}
|
permissions: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
main:
|
main:
|
||||||
uses: hoverkraft-tech/ci-github-common/.github/workflows/need-fix-to-issue.yml@624be17604ee0a7378488191aacb35851e7cf001 # 0.37.1
|
uses: hoverkraft-tech/ci-github-common/.github/workflows/need-fix-to-issue.yml@4b53189212d5810f710bed89711002626977215b # 0.33.0
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
issues: write
|
issues: write
|
||||||
|
|||||||
3
.github/workflows/prepare-release.yml
vendored
3
.github/workflows/prepare-release.yml
vendored
@ -15,7 +15,8 @@ permissions: {}
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
uses: hoverkraft-tech/ci-github-publish/.github/workflows/prepare-release.yml@b2562b46714e535a0113f90f554b55e1248212c1 # 0.26.3
|
uses: hoverkraft-tech/ci-github-publish/.github/workflows/prepare-release.yml@b56be562f38e0e3e712f09691a8fe930aae9db1b # 0.22.0
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
id-token: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|||||||
2
.github/workflows/pull-request-ci.yml
vendored
2
.github/workflows/pull-request-ci.yml
vendored
@ -18,8 +18,8 @@ jobs:
|
|||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
issues: write
|
|
||||||
packages: read
|
packages: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
security-events: write
|
security-events: write
|
||||||
statuses: write
|
statuses: write
|
||||||
|
secrets: inherit
|
||||||
|
|||||||
4
.github/workflows/semantic-pull-request.yml
vendored
4
.github/workflows/semantic-pull-request.yml
vendored
@ -1,7 +1,7 @@
|
|||||||
name: "Pull Request - Semantic Lint"
|
name: "Pull Request - Semantic Lint"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request_target: # zizmor: ignore[dangerous-triggers] validates PR metadata only through a pinned reusable workflow without checkout
|
pull_request_target:
|
||||||
types:
|
types:
|
||||||
- opened
|
- opened
|
||||||
- edited
|
- edited
|
||||||
@ -11,7 +11,7 @@ permissions: {}
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
main:
|
main:
|
||||||
uses: hoverkraft-tech/ci-github-common/.github/workflows/semantic-pull-request.yml@624be17604ee0a7378488191aacb35851e7cf001 # 0.37.1
|
uses: hoverkraft-tech/ci-github-common/.github/workflows/semantic-pull-request.yml@4b53189212d5810f710bed89711002626977215b # 0.33.0
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|||||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@ -8,7 +8,7 @@ permissions: {}
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
main:
|
main:
|
||||||
uses: hoverkraft-tech/ci-github-common/.github/workflows/stale.yml@624be17604ee0a7378488191aacb35851e7cf001 # 0.37.1
|
uses: hoverkraft-tech/ci-github-common/.github/workflows/stale.yml@4b53189212d5810f710bed89711002626977215b # 0.33.0
|
||||||
permissions:
|
permissions:
|
||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@ -12,6 +12,7 @@ lerna-debug.log*
|
|||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
eslint-report.json
|
||||||
|
|
||||||
# Runtime data
|
# Runtime data
|
||||||
pids
|
pids
|
||||||
@ -53,6 +54,9 @@ typings/
|
|||||||
# Optional npm cache directory
|
# Optional npm cache directory
|
||||||
.npm
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
# Optional REPL history
|
# Optional REPL history
|
||||||
.node_repl_history
|
.node_repl_history
|
||||||
|
|
||||||
@ -98,6 +102,3 @@ __tests__/runner/*
|
|||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
lint.sarif
|
|
||||||
biome-report.sarif
|
|
||||||
junit.xml
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
24.14.1
|
20.19.3
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
FROM ghcr.io/hoverkraft-tech/docker-base-images/super-linter:0.6.0
|
FROM ghcr.io/super-linter/super-linter:slim-v8.0.0
|
||||||
|
|
||||||
HEALTHCHECK --interval=5m --timeout=10s --start-period=30s --retries=3 CMD ["/bin/sh","-c","test -d /github/home"]
|
HEALTHCHECK --interval=5m --timeout=10s --start-period=30s --retries=3 CMD ["/bin/sh","-c","test -d /github/home"]
|
||||||
ARG UID=1000
|
ARG UID=1000
|
||||||
ARG GID=1000
|
ARG GID=1000
|
||||||
|
RUN chown -R ${UID}:${GID} /github/home
|
||||||
USER ${UID}:${GID}
|
USER ${UID}:${GID}
|
||||||
|
|
||||||
|
ENV RUN_LOCAL=true
|
||||||
|
ENV USE_FIND_ALGORITHM=true
|
||||||
|
ENV LOG_LEVEL=WARN
|
||||||
|
ENV LOG_FILE="/github/home/logs"
|
||||||
|
|||||||
14
Makefile
14
Makefile
@ -7,13 +7,13 @@ lint: ## Execute linting
|
|||||||
$(call run_linter,)
|
$(call run_linter,)
|
||||||
|
|
||||||
lint-fix: ## Execute linting and fix
|
lint-fix: ## Execute linting and fix
|
||||||
@npm run format
|
|
||||||
$(call run_linter, \
|
$(call run_linter, \
|
||||||
|
-e FIX_JSON_PRETTIER=true \
|
||||||
|
-e FIX_JAVASCRIPT_PRETTIER=true \
|
||||||
|
-e FIX_YAML_PRETTIER=true \
|
||||||
-e FIX_MARKDOWN=true \
|
-e FIX_MARKDOWN=true \
|
||||||
|
-e FIX_MARKDOWN_PRETTIER=true \
|
||||||
-e FIX_NATURAL_LANGUAGE=true \
|
-e FIX_NATURAL_LANGUAGE=true \
|
||||||
-e FIX_SHELL_SHFMT=true \
|
|
||||||
-e FIX_BIOME_LINT=true \
|
|
||||||
-e FIX_BIOME_FORMAT=true \
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ci: ## Execute all formats and checks
|
ci: ## Execute all formats and checks
|
||||||
@ -29,8 +29,12 @@ define run_linter
|
|||||||
docker build --build-arg UID=$(shell id -u) --build-arg GID=$(shell id -g) --tag $$LINTER_IMAGE .; \
|
docker build --build-arg UID=$(shell id -u) --build-arg GID=$(shell id -g) --tag $$LINTER_IMAGE .; \
|
||||||
docker run \
|
docker run \
|
||||||
-e DEFAULT_WORKSPACE="$$DEFAULT_WORKSPACE" \
|
-e DEFAULT_WORKSPACE="$$DEFAULT_WORKSPACE" \
|
||||||
-e FILTER_REGEX_EXCLUDE="dist/**/*|.github/social-preview.svg|.github/logo.svg" \
|
|
||||||
-e FILTER_REGEX_INCLUDE="$(filter-out $@,$(MAKECMDGOALS))" \
|
-e FILTER_REGEX_INCLUDE="$(filter-out $@,$(MAKECMDGOALS))" \
|
||||||
|
-e IGNORE_GITIGNORED_FILES=true \
|
||||||
|
-e FILTER_REGEX_EXCLUDE=dist/**/* \
|
||||||
|
-e VALIDATE_TYPESCRIPT_ES=false \
|
||||||
|
-e VALIDATE_TYPESCRIPT_PRETTIER=false \
|
||||||
|
-e VALIDATE_JAVASCRIPT_ES=false \
|
||||||
$(1) \
|
$(1) \
|
||||||
-v $$VOLUME \
|
-v $$VOLUME \
|
||||||
--rm \
|
--rm \
|
||||||
|
|||||||
13
README.md
13
README.md
@ -17,7 +17,6 @@
|
|||||||
[](http://choosealicense.com/licenses/mit/)
|
[](http://choosealicense.com/licenses/mit/)
|
||||||
[](https://img.shields.io/github/stars/hoverkraft-tech/compose-action?style=social)
|
[](https://img.shields.io/github/stars/hoverkraft-tech/compose-action?style=social)
|
||||||
[](https://github.com/hoverkraft-tech/compose-action/blob/main/CONTRIBUTING.md)
|
[](https://github.com/hoverkraft-tech/compose-action/blob/main/CONTRIBUTING.md)
|
||||||

|
|
||||||
[](https://codecov.io/gh/hoverkraft-tech/compose-action)
|
[](https://codecov.io/gh/hoverkraft-tech/compose-action)
|
||||||
|
|
||||||
<!-- badges:end -->
|
<!-- badges:end -->
|
||||||
@ -50,7 +49,7 @@ Some extra options can be passed to the `docker compose down` command using the
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: hoverkraft-tech/compose-action@11beaa1c2dae4e8ed7b1665aa074723b6cecb0e4 # v3.0.0
|
- uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
||||||
with:
|
with:
|
||||||
# Additional options to pass to `docker` command.
|
# Additional options to pass to `docker` command.
|
||||||
docker-flags: ""
|
docker-flags: ""
|
||||||
@ -140,7 +139,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.2.2
|
- uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- name: Run docker compose
|
- name: Run docker compose
|
||||||
uses: hoverkraft-tech/compose-action@11beaa1c2dae4e8ed7b1665aa074723b6cecb0e4 # v3.0.0
|
uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
||||||
with:
|
with:
|
||||||
compose-file: "./docker/docker-compose.yml"
|
compose-file: "./docker/docker-compose.yml"
|
||||||
|
|
||||||
@ -154,7 +153,7 @@ jobs:
|
|||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.2.2
|
- uses: actions/checkout@v4.2.2
|
||||||
- uses: hoverkraft-tech/compose-action@11beaa1c2dae4e8ed7b1665aa074723b6cecb0e4 # v3.0.0
|
- uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
||||||
with:
|
with:
|
||||||
compose-file: "./docker/docker-compose.yml"
|
compose-file: "./docker/docker-compose.yml"
|
||||||
env:
|
env:
|
||||||
@ -169,7 +168,7 @@ Perform `docker compose up` to some given service instead of all of them
|
|||||||
steps:
|
steps:
|
||||||
# need checkout before using compose-action
|
# need checkout before using compose-action
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: hoverkraft-tech/compose-action@11beaa1c2dae4e8ed7b1665aa074723b6cecb0e4 # v3.0.0
|
- uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
||||||
with:
|
with:
|
||||||
compose-file: "./docker/docker-compose.yml"
|
compose-file: "./docker/docker-compose.yml"
|
||||||
services: |
|
services: |
|
||||||
@ -207,7 +206,7 @@ A full list of flags can be found in the [Docker compose documentation](https://
|
|||||||
steps:
|
steps:
|
||||||
# need checkout before using compose-action
|
# need checkout before using compose-action
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: hoverkraft-tech/compose-action@11beaa1c2dae4e8ed7b1665aa074723b6cecb0e4 # v3.0.0
|
- uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
||||||
with:
|
with:
|
||||||
compose-file: "./docker/docker-compose.yml"
|
compose-file: "./docker/docker-compose.yml"
|
||||||
compose-flags: "--profile profile-1"
|
compose-flags: "--profile profile-1"
|
||||||
@ -221,7 +220,7 @@ This is useful when you have a base compose file and additional files for differ
|
|||||||
steps:
|
steps:
|
||||||
# need checkout before using compose-action
|
# need checkout before using compose-action
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: hoverkraft-tech/compose-action@11beaa1c2dae4e8ed7b1665aa074723b6cecb0e4 # v3.0.0
|
- uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0
|
||||||
with:
|
with:
|
||||||
compose-file: |
|
compose-file: |
|
||||||
./docker/docker-compose.yml
|
./docker/docker-compose.yml
|
||||||
|
|||||||
@ -65,6 +65,6 @@ inputs:
|
|||||||
required: false
|
required: false
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: node24
|
using: node20
|
||||||
main: dist/index.js
|
main: dist/index.js
|
||||||
post: dist/post.js
|
post: dist/post.js
|
||||||
|
|||||||
34
biome.json
34
biome.json
@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
|
|
||||||
"vcs": {
|
|
||||||
"enabled": true,
|
|
||||||
"clientKind": "git",
|
|
||||||
"useIgnoreFile": true
|
|
||||||
},
|
|
||||||
"files": {
|
|
||||||
"includes": ["**", "!!**/dist"]
|
|
||||||
},
|
|
||||||
"formatter": {
|
|
||||||
"enabled": true,
|
|
||||||
"indentStyle": "tab"
|
|
||||||
},
|
|
||||||
"linter": {
|
|
||||||
"enabled": true,
|
|
||||||
"rules": {
|
|
||||||
"recommended": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"javascript": {
|
|
||||||
"formatter": {
|
|
||||||
"quoteStyle": "double"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"assist": {
|
|
||||||
"enabled": true,
|
|
||||||
"actions": {
|
|
||||||
"source": {
|
|
||||||
"organizeImports": "on"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
402
dist/index.js
generated
vendored
402
dist/index.js
generated
vendored
@ -8451,6 +8451,8 @@ function defaultFactory (origin, opts) {
|
|||||||
|
|
||||||
class Agent extends DispatcherBase {
|
class Agent extends DispatcherBase {
|
||||||
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) {
|
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) {
|
||||||
|
super()
|
||||||
|
|
||||||
if (typeof factory !== 'function') {
|
if (typeof factory !== 'function') {
|
||||||
throw new InvalidArgumentError('factory must be a function.')
|
throw new InvalidArgumentError('factory must be a function.')
|
||||||
}
|
}
|
||||||
@ -8463,8 +8465,6 @@ class Agent extends DispatcherBase {
|
|||||||
throw new InvalidArgumentError('maxRedirections must be a positive number')
|
throw new InvalidArgumentError('maxRedirections must be a positive number')
|
||||||
}
|
}
|
||||||
|
|
||||||
super(options)
|
|
||||||
|
|
||||||
if (connect && typeof connect !== 'function') {
|
if (connect && typeof connect !== 'function') {
|
||||||
connect = { ...connect }
|
connect = { ...connect }
|
||||||
}
|
}
|
||||||
@ -8836,9 +8836,6 @@ const EMPTY_BUF = Buffer.alloc(0)
|
|||||||
const FastBuffer = Buffer[Symbol.species]
|
const FastBuffer = Buffer[Symbol.species]
|
||||||
const addListener = util.addListener
|
const addListener = util.addListener
|
||||||
const removeAllListeners = util.removeAllListeners
|
const removeAllListeners = util.removeAllListeners
|
||||||
const kIdleSocketValidation = Symbol('kIdleSocketValidation')
|
|
||||||
const kIdleSocketValidationTimeout = Symbol('kIdleSocketValidationTimeout')
|
|
||||||
const kSocketUsed = Symbol('kSocketUsed')
|
|
||||||
|
|
||||||
let extractBody
|
let extractBody
|
||||||
|
|
||||||
@ -9061,60 +9058,15 @@ class Parser {
|
|||||||
|
|
||||||
const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr
|
const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr
|
||||||
|
|
||||||
if (ret !== constants.ERROR.OK) {
|
|
||||||
const body = data.subarray(offset)
|
|
||||||
|
|
||||||
if (ret === constants.ERROR.PAUSED_UPGRADE) {
|
if (ret === constants.ERROR.PAUSED_UPGRADE) {
|
||||||
this.onUpgrade(body)
|
this.onUpgrade(data.slice(offset))
|
||||||
} else if (ret === constants.ERROR.PAUSED) {
|
} else if (ret === constants.ERROR.PAUSED) {
|
||||||
this.paused = true
|
this.paused = true
|
||||||
socket.unshift(body)
|
socket.unshift(data.slice(offset))
|
||||||
} else {
|
} else if (ret !== constants.ERROR.OK) {
|
||||||
throw this.createError(ret, body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
util.destroy(socket, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
finish () {
|
|
||||||
assert(currentParser === null)
|
|
||||||
assert(this.ptr != null)
|
|
||||||
assert(!this.paused)
|
|
||||||
|
|
||||||
const { llhttp } = this
|
|
||||||
|
|
||||||
let ret
|
|
||||||
|
|
||||||
try {
|
|
||||||
currentParser = this
|
|
||||||
ret = llhttp.llhttp_finish(this.ptr)
|
|
||||||
} finally {
|
|
||||||
currentParser = null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret === constants.ERROR.OK) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret === constants.ERROR.PAUSED || ret === constants.ERROR.PAUSED_UPGRADE) {
|
|
||||||
this.paused = true
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.createError(ret, EMPTY_BUF)
|
|
||||||
}
|
|
||||||
|
|
||||||
createError (ret, data) {
|
|
||||||
const { llhttp, contentLength, bytesRead } = this
|
|
||||||
|
|
||||||
if (contentLength && bytesRead !== parseInt(contentLength, 10)) {
|
|
||||||
return new ResponseContentLengthMismatchError()
|
|
||||||
}
|
|
||||||
|
|
||||||
const ptr = llhttp.llhttp_get_error_reason(this.ptr)
|
const ptr = llhttp.llhttp_get_error_reason(this.ptr)
|
||||||
let message = ''
|
let message = ''
|
||||||
|
/* istanbul ignore else: difficult to make a test case for */
|
||||||
if (ptr) {
|
if (ptr) {
|
||||||
const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)
|
const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)
|
||||||
message =
|
message =
|
||||||
@ -9122,8 +9074,11 @@ class Parser {
|
|||||||
Buffer.from(llhttp.memory.buffer, ptr, len).toString() +
|
Buffer.from(llhttp.memory.buffer, ptr, len).toString() +
|
||||||
')'
|
')'
|
||||||
}
|
}
|
||||||
|
throw new HTTPParserError(message, constants.ERROR[ret], data.slice(offset))
|
||||||
return new HTTPParserError(message, constants.ERROR[ret], data)
|
}
|
||||||
|
} catch (err) {
|
||||||
|
util.destroy(socket, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy () {
|
destroy () {
|
||||||
@ -9153,11 +9108,6 @@ class Parser {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client[kRunning] === 0) {
|
|
||||||
util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = client[kQueue][client[kRunningIdx]]
|
const request = client[kQueue][client[kRunningIdx]]
|
||||||
if (!request) {
|
if (!request) {
|
||||||
return -1
|
return -1
|
||||||
@ -9261,11 +9211,6 @@ class Parser {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client[kRunning] === 0) {
|
|
||||||
util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = client[kQueue][client[kRunningIdx]]
|
const request = client[kQueue][client[kRunningIdx]]
|
||||||
|
|
||||||
/* istanbul ignore next: difficult to make a test case for */
|
/* istanbul ignore next: difficult to make a test case for */
|
||||||
@ -9439,7 +9384,6 @@ class Parser {
|
|||||||
request.onComplete(headers)
|
request.onComplete(headers)
|
||||||
|
|
||||||
client[kQueue][client[kRunningIdx]++] = null
|
client[kQueue][client[kRunningIdx]++] = null
|
||||||
socket[kSocketUsed] = true
|
|
||||||
|
|
||||||
if (socket[kWriting]) {
|
if (socket[kWriting]) {
|
||||||
assert(client[kRunning] === 0)
|
assert(client[kRunning] === 0)
|
||||||
@ -9498,9 +9442,6 @@ async function connectH1 (client, socket) {
|
|||||||
socket[kWriting] = false
|
socket[kWriting] = false
|
||||||
socket[kReset] = false
|
socket[kReset] = false
|
||||||
socket[kBlocking] = false
|
socket[kBlocking] = false
|
||||||
socket[kIdleSocketValidation] = 0
|
|
||||||
socket[kIdleSocketValidationTimeout] = null
|
|
||||||
socket[kSocketUsed] = false
|
|
||||||
socket[kParser] = new Parser(client, socket, llhttpInstance)
|
socket[kParser] = new Parser(client, socket, llhttpInstance)
|
||||||
|
|
||||||
addListener(socket, 'error', function (err) {
|
addListener(socket, 'error', function (err) {
|
||||||
@ -9511,11 +9452,8 @@ async function connectH1 (client, socket) {
|
|||||||
// On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
|
// On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
|
||||||
// to the user.
|
// to the user.
|
||||||
if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {
|
if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {
|
||||||
const parserErr = parser.finish()
|
// We treat all incoming data so for as a valid response.
|
||||||
if (parserErr) {
|
parser.onMessageComplete()
|
||||||
this[kError] = parserErr
|
|
||||||
this[kClient][kOnError](parserErr)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9534,10 +9472,8 @@ async function connectH1 (client, socket) {
|
|||||||
const parser = this[kParser]
|
const parser = this[kParser]
|
||||||
|
|
||||||
if (parser.statusCode && !parser.shouldKeepAlive) {
|
if (parser.statusCode && !parser.shouldKeepAlive) {
|
||||||
const parserErr = parser.finish()
|
// We treat all incoming data so far as a valid response.
|
||||||
if (parserErr) {
|
parser.onMessageComplete()
|
||||||
util.destroy(this, parserErr)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9547,11 +9483,10 @@ async function connectH1 (client, socket) {
|
|||||||
const client = this[kClient]
|
const client = this[kClient]
|
||||||
const parser = this[kParser]
|
const parser = this[kParser]
|
||||||
|
|
||||||
clearIdleSocketValidation(this)
|
|
||||||
|
|
||||||
if (parser) {
|
if (parser) {
|
||||||
if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
|
if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
|
||||||
this[kError] = parser.finish() || this[kError]
|
// We treat all incoming data so far as a valid response.
|
||||||
|
parser.onMessageComplete()
|
||||||
}
|
}
|
||||||
|
|
||||||
this[kParser].destroy()
|
this[kParser].destroy()
|
||||||
@ -9614,7 +9549,7 @@ async function connectH1 (client, socket) {
|
|||||||
return socket.destroyed
|
return socket.destroyed
|
||||||
},
|
},
|
||||||
busy (request) {
|
busy (request) {
|
||||||
if (socket[kWriting] || socket[kReset] || socket[kBlocking] || socket[kIdleSocketValidation] === 1) {
|
if (socket[kWriting] || socket[kReset] || socket[kBlocking]) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9652,31 +9587,6 @@ async function connectH1 (client, socket) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearIdleSocketValidation (socket) {
|
|
||||||
if (socket[kIdleSocketValidationTimeout]) {
|
|
||||||
clearTimeout(socket[kIdleSocketValidationTimeout])
|
|
||||||
socket[kIdleSocketValidationTimeout] = null
|
|
||||||
}
|
|
||||||
|
|
||||||
socket[kIdleSocketValidation] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function scheduleIdleSocketValidation (client, socket) {
|
|
||||||
socket[kIdleSocketValidation] = 1
|
|
||||||
socket[kIdleSocketValidationTimeout] = setTimeout(() => {
|
|
||||||
socket[kIdleSocketValidationTimeout] = null
|
|
||||||
socket[kIdleSocketValidation] = 2
|
|
||||||
|
|
||||||
if (client[kSocket] === socket && !socket.destroyed) {
|
|
||||||
client[kResume]()
|
|
||||||
}
|
|
||||||
}, 0)
|
|
||||||
socket[kIdleSocketValidationTimeout].unref?.()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import('./client.js')} client
|
|
||||||
*/
|
|
||||||
function resumeH1 (client) {
|
function resumeH1 (client) {
|
||||||
const socket = client[kSocket]
|
const socket = client[kSocket]
|
||||||
|
|
||||||
@ -9691,32 +9601,6 @@ function resumeH1 (client) {
|
|||||||
socket[kNoRef] = false
|
socket[kNoRef] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client[kRunning] === 0 && client[kPending] > 0 && socket[kSocketUsed]) {
|
|
||||||
if (socket[kIdleSocketValidation] === 0) {
|
|
||||||
scheduleIdleSocketValidation(client, socket)
|
|
||||||
socket[kParser].readMore()
|
|
||||||
if (socket.destroyed) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (socket[kIdleSocketValidation] === 1) {
|
|
||||||
socket[kParser].readMore()
|
|
||||||
if (socket.destroyed) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client[kRunning] === 0) {
|
|
||||||
socket[kParser].readMore()
|
|
||||||
if (socket.destroyed) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client[kSize] === 0) {
|
if (client[kSize] === 0) {
|
||||||
if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {
|
if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {
|
||||||
socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE)
|
socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE)
|
||||||
@ -9810,7 +9694,6 @@ function writeH1 (client, request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const socket = client[kSocket]
|
const socket = client[kSocket]
|
||||||
clearIdleSocketValidation(socket)
|
|
||||||
|
|
||||||
const abort = (err) => {
|
const abort = (err) => {
|
||||||
if (request.aborted || request.completed) {
|
if (request.aborted || request.completed) {
|
||||||
@ -11130,10 +11013,9 @@ class Client extends DispatcherBase {
|
|||||||
autoSelectFamilyAttemptTimeout,
|
autoSelectFamilyAttemptTimeout,
|
||||||
// h2
|
// h2
|
||||||
maxConcurrentStreams,
|
maxConcurrentStreams,
|
||||||
allowH2,
|
allowH2
|
||||||
webSocket
|
|
||||||
} = {}) {
|
} = {}) {
|
||||||
super({ webSocket })
|
super()
|
||||||
|
|
||||||
if (keepAlive !== undefined) {
|
if (keepAlive !== undefined) {
|
||||||
throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead')
|
throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead')
|
||||||
@ -11665,24 +11547,15 @@ const { kDestroy, kClose, kClosed, kDestroyed, kDispatch, kInterceptors } = __nc
|
|||||||
const kOnDestroyed = Symbol('onDestroyed')
|
const kOnDestroyed = Symbol('onDestroyed')
|
||||||
const kOnClosed = Symbol('onClosed')
|
const kOnClosed = Symbol('onClosed')
|
||||||
const kInterceptedDispatch = Symbol('Intercepted Dispatch')
|
const kInterceptedDispatch = Symbol('Intercepted Dispatch')
|
||||||
const kWebSocketOptions = Symbol('webSocketOptions')
|
|
||||||
|
|
||||||
class DispatcherBase extends Dispatcher {
|
class DispatcherBase extends Dispatcher {
|
||||||
constructor (opts) {
|
constructor () {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this[kDestroyed] = false
|
this[kDestroyed] = false
|
||||||
this[kOnDestroyed] = null
|
this[kOnDestroyed] = null
|
||||||
this[kClosed] = false
|
this[kClosed] = false
|
||||||
this[kOnClosed] = []
|
this[kOnClosed] = []
|
||||||
this[kWebSocketOptions] = opts?.webSocket ?? {}
|
|
||||||
}
|
|
||||||
|
|
||||||
get webSocketOptions () {
|
|
||||||
return {
|
|
||||||
maxFragments: this[kWebSocketOptions].maxFragments ?? 131072,
|
|
||||||
maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get destroyed () {
|
get destroyed () {
|
||||||
@ -12242,8 +12115,8 @@ const kRemoveClient = Symbol('remove client')
|
|||||||
const kStats = Symbol('stats')
|
const kStats = Symbol('stats')
|
||||||
|
|
||||||
class PoolBase extends DispatcherBase {
|
class PoolBase extends DispatcherBase {
|
||||||
constructor (opts) {
|
constructor () {
|
||||||
super(opts)
|
super()
|
||||||
|
|
||||||
this[kQueue] = new FixedQueue()
|
this[kQueue] = new FixedQueue()
|
||||||
this[kClients] = []
|
this[kClients] = []
|
||||||
@ -12502,6 +12375,8 @@ class Pool extends PoolBase {
|
|||||||
allowH2,
|
allowH2,
|
||||||
...options
|
...options
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
super()
|
||||||
|
|
||||||
if (connections != null && (!Number.isFinite(connections) || connections < 0)) {
|
if (connections != null && (!Number.isFinite(connections) || connections < 0)) {
|
||||||
throw new InvalidArgumentError('invalid connections')
|
throw new InvalidArgumentError('invalid connections')
|
||||||
}
|
}
|
||||||
@ -12526,8 +12401,6 @@ class Pool extends PoolBase {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
super(options)
|
|
||||||
|
|
||||||
this[kInterceptors] = options.interceptors?.Pool && Array.isArray(options.interceptors.Pool)
|
this[kInterceptors] = options.interceptors?.Pool && Array.isArray(options.interceptors.Pool)
|
||||||
? options.interceptors.Pool
|
? options.interceptors.Pool
|
||||||
: []
|
: []
|
||||||
@ -17580,25 +17453,32 @@ function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {})
|
|||||||
// If the attribute-name case-insensitively matches the string
|
// If the attribute-name case-insensitively matches the string
|
||||||
// "SameSite", the user agent MUST process the cookie-av as follows:
|
// "SameSite", the user agent MUST process the cookie-av as follows:
|
||||||
|
|
||||||
const attributeValueLowercase = attributeValue.toLowerCase()
|
// 1. Let enforcement be "Default".
|
||||||
|
let enforcement = 'Default'
|
||||||
|
|
||||||
// 1. If cookie-av's attribute-value is a case-insensitive match for
|
const attributeValueLowercase = attributeValue.toLowerCase()
|
||||||
// "None", append an attribute to the cookie-attribute-list with an
|
|
||||||
// attribute-name of "SameSite" and an attribute-value of "None".
|
|
||||||
if (attributeValueLowercase === 'none') {
|
|
||||||
cookieAttributeList.sameSite = 'None'
|
|
||||||
} else if (attributeValueLowercase === 'strict') {
|
|
||||||
// 2. If cookie-av's attribute-value is a case-insensitive match for
|
// 2. If cookie-av's attribute-value is a case-insensitive match for
|
||||||
// "Strict", append an attribute to the cookie-attribute-list with
|
// "None", set enforcement to "None".
|
||||||
// an attribute-name of "SameSite" and an attribute-value of
|
if (attributeValueLowercase.includes('none')) {
|
||||||
// "Strict".
|
enforcement = 'None'
|
||||||
cookieAttributeList.sameSite = 'Strict'
|
|
||||||
} else if (attributeValueLowercase === 'lax') {
|
|
||||||
// 3. If cookie-av's attribute-value is a case-insensitive match for
|
|
||||||
// "Lax", append an attribute to the cookie-attribute-list with an
|
|
||||||
// attribute-name of "SameSite" and an attribute-value of "Lax".
|
|
||||||
cookieAttributeList.sameSite = 'Lax'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. If cookie-av's attribute-value is a case-insensitive match for
|
||||||
|
// "Strict", set enforcement to "Strict".
|
||||||
|
if (attributeValueLowercase.includes('strict')) {
|
||||||
|
enforcement = 'Strict'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. If cookie-av's attribute-value is a case-insensitive match for
|
||||||
|
// "Lax", set enforcement to "Lax".
|
||||||
|
if (attributeValueLowercase.includes('lax')) {
|
||||||
|
enforcement = 'Lax'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Append an attribute to the cookie-attribute-list with an
|
||||||
|
// attribute-name of "SameSite" and an attribute-value of
|
||||||
|
// enforcement.
|
||||||
|
cookieAttributeList.sameSite = enforcement
|
||||||
} else {
|
} else {
|
||||||
cookieAttributeList.unparsed ??= []
|
cookieAttributeList.unparsed ??= []
|
||||||
|
|
||||||
@ -30275,35 +30155,40 @@ const tail = Buffer.from([0x00, 0x00, 0xff, 0xff])
|
|||||||
const kBuffer = Symbol('kBuffer')
|
const kBuffer = Symbol('kBuffer')
|
||||||
const kLength = Symbol('kLength')
|
const kLength = Symbol('kLength')
|
||||||
|
|
||||||
|
// Default maximum decompressed message size: 4 MB
|
||||||
|
const kDefaultMaxDecompressedSize = 4 * 1024 * 1024
|
||||||
|
|
||||||
class PerMessageDeflate {
|
class PerMessageDeflate {
|
||||||
/** @type {import('node:zlib').InflateRaw} */
|
/** @type {import('node:zlib').InflateRaw} */
|
||||||
#inflate
|
#inflate
|
||||||
|
|
||||||
#options = {}
|
#options = {}
|
||||||
|
|
||||||
#maxPayloadSize = 0
|
/** @type {boolean} */
|
||||||
|
#aborted = false
|
||||||
|
|
||||||
|
/** @type {Function|null} */
|
||||||
|
#currentCallback = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Map<string, string>} extensions
|
* @param {Map<string, string>} extensions
|
||||||
*/
|
*/
|
||||||
constructor (extensions, options) {
|
constructor (extensions) {
|
||||||
this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover')
|
this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover')
|
||||||
this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits')
|
this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits')
|
||||||
|
|
||||||
this.#maxPayloadSize = options.maxPayloadSize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Decompress a compressed payload.
|
|
||||||
* @param {Buffer} chunk Compressed data
|
|
||||||
* @param {boolean} fin Final fragment flag
|
|
||||||
* @param {Function} callback Callback function
|
|
||||||
*/
|
|
||||||
decompress (chunk, fin, callback) {
|
decompress (chunk, fin, callback) {
|
||||||
// An endpoint uses the following algorithm to decompress a message.
|
// An endpoint uses the following algorithm to decompress a message.
|
||||||
// 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
|
// 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
|
||||||
// payload of the message.
|
// payload of the message.
|
||||||
// 2. Decompress the resulting data using DEFLATE.
|
// 2. Decompress the resulting data using DEFLATE.
|
||||||
|
|
||||||
|
if (this.#aborted) {
|
||||||
|
callback(new MessageSizeExceededError())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.#inflate) {
|
if (!this.#inflate) {
|
||||||
let windowBits = Z_DEFAULT_WINDOWBITS
|
let windowBits = Z_DEFAULT_WINDOWBITS
|
||||||
|
|
||||||
@ -30326,12 +30211,23 @@ class PerMessageDeflate {
|
|||||||
this.#inflate[kLength] = 0
|
this.#inflate[kLength] = 0
|
||||||
|
|
||||||
this.#inflate.on('data', (data) => {
|
this.#inflate.on('data', (data) => {
|
||||||
|
if (this.#aborted) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.#inflate[kLength] += data.length
|
this.#inflate[kLength] += data.length
|
||||||
|
|
||||||
if (this.#maxPayloadSize > 0 && this.#inflate[kLength] > this.#maxPayloadSize) {
|
if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) {
|
||||||
callback(new MessageSizeExceededError())
|
this.#aborted = true
|
||||||
this.#inflate.removeAllListeners()
|
this.#inflate.removeAllListeners()
|
||||||
|
this.#inflate.destroy()
|
||||||
this.#inflate = null
|
this.#inflate = null
|
||||||
|
|
||||||
|
if (this.#currentCallback) {
|
||||||
|
const cb = this.#currentCallback
|
||||||
|
this.#currentCallback = null
|
||||||
|
cb(new MessageSizeExceededError())
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30344,13 +30240,14 @@ class PerMessageDeflate {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.#currentCallback = callback
|
||||||
this.#inflate.write(chunk)
|
this.#inflate.write(chunk)
|
||||||
if (fin) {
|
if (fin) {
|
||||||
this.#inflate.write(tail)
|
this.#inflate.write(tail)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#inflate.flush(() => {
|
this.#inflate.flush(() => {
|
||||||
if (!this.#inflate) {
|
if (this.#aborted || !this.#inflate) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30358,6 +30255,7 @@ class PerMessageDeflate {
|
|||||||
|
|
||||||
this.#inflate[kBuffer].length = 0
|
this.#inflate[kBuffer].length = 0
|
||||||
this.#inflate[kLength] = 0
|
this.#inflate[kLength] = 0
|
||||||
|
this.#currentCallback = null
|
||||||
|
|
||||||
callback(null, full)
|
callback(null, full)
|
||||||
})
|
})
|
||||||
@ -30392,12 +30290,6 @@ const {
|
|||||||
const { WebsocketFrameSend } = __nccwpck_require__(3264)
|
const { WebsocketFrameSend } = __nccwpck_require__(3264)
|
||||||
const { closeWebSocketConnection } = __nccwpck_require__(6897)
|
const { closeWebSocketConnection } = __nccwpck_require__(6897)
|
||||||
const { PerMessageDeflate } = __nccwpck_require__(9469)
|
const { PerMessageDeflate } = __nccwpck_require__(9469)
|
||||||
const { MessageSizeExceededError } = __nccwpck_require__(8707)
|
|
||||||
|
|
||||||
function failWebsocketConnectionWithCode (ws, code, reason) {
|
|
||||||
closeWebSocketConnection(ws, code, reason, Buffer.byteLength(reason))
|
|
||||||
failWebsocketConnection(ws, reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This code was influenced by ws released under the MIT license.
|
// This code was influenced by ws released under the MIT license.
|
||||||
// Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
// Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
||||||
@ -30406,7 +30298,6 @@ function failWebsocketConnectionWithCode (ws, code, reason) {
|
|||||||
|
|
||||||
class ByteParser extends Writable {
|
class ByteParser extends Writable {
|
||||||
#buffers = []
|
#buffers = []
|
||||||
#fragmentsBytes = 0
|
|
||||||
#byteOffset = 0
|
#byteOffset = 0
|
||||||
#loop = false
|
#loop = false
|
||||||
|
|
||||||
@ -30418,27 +30309,18 @@ class ByteParser extends Writable {
|
|||||||
/** @type {Map<string, PerMessageDeflate>} */
|
/** @type {Map<string, PerMessageDeflate>} */
|
||||||
#extensions
|
#extensions
|
||||||
|
|
||||||
/** @type {number} */
|
|
||||||
#maxFragments
|
|
||||||
|
|
||||||
/** @type {number} */
|
|
||||||
#maxPayloadSize
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('./websocket').WebSocket} ws
|
* @param {import('./websocket').WebSocket} ws
|
||||||
* @param {Map<string, string>|null} extensions
|
* @param {Map<string, string>|null} extensions
|
||||||
* @param {{ maxFragments?: number, maxPayloadSize?: number }} [options]
|
|
||||||
*/
|
*/
|
||||||
constructor (ws, extensions, options = {}) {
|
constructor (ws, extensions) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.ws = ws
|
this.ws = ws
|
||||||
this.#extensions = extensions == null ? new Map() : extensions
|
this.#extensions = extensions == null ? new Map() : extensions
|
||||||
this.#maxFragments = options.maxFragments ?? 0
|
|
||||||
this.#maxPayloadSize = options.maxPayloadSize ?? 0
|
|
||||||
|
|
||||||
if (this.#extensions.has('permessage-deflate')) {
|
if (this.#extensions.has('permessage-deflate')) {
|
||||||
this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions, options))
|
this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30454,19 +30336,6 @@ class ByteParser extends Writable {
|
|||||||
this.run(callback)
|
this.run(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
#validatePayloadLength () {
|
|
||||||
if (
|
|
||||||
this.#maxPayloadSize > 0 &&
|
|
||||||
!isControlFrame(this.#info.opcode) &&
|
|
||||||
this.#info.payloadLength + this.#fragmentsBytes > this.#maxPayloadSize
|
|
||||||
) {
|
|
||||||
failWebsocketConnectionWithCode(this.ws, 1009, 'Payload size exceeds maximum allowed size')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs whenever a new chunk is received.
|
* Runs whenever a new chunk is received.
|
||||||
* Callback is called whenever there are no more chunks buffering,
|
* Callback is called whenever there are no more chunks buffering,
|
||||||
@ -30555,10 +30424,6 @@ class ByteParser extends Writable {
|
|||||||
if (payloadLength <= 125) {
|
if (payloadLength <= 125) {
|
||||||
this.#info.payloadLength = payloadLength
|
this.#info.payloadLength = payloadLength
|
||||||
this.#state = parserStates.READ_DATA
|
this.#state = parserStates.READ_DATA
|
||||||
|
|
||||||
if (!this.#validatePayloadLength()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if (payloadLength === 126) {
|
} else if (payloadLength === 126) {
|
||||||
this.#state = parserStates.PAYLOADLENGTH_16
|
this.#state = parserStates.PAYLOADLENGTH_16
|
||||||
} else if (payloadLength === 127) {
|
} else if (payloadLength === 127) {
|
||||||
@ -30583,10 +30448,6 @@ class ByteParser extends Writable {
|
|||||||
|
|
||||||
this.#info.payloadLength = buffer.readUInt16BE(0)
|
this.#info.payloadLength = buffer.readUInt16BE(0)
|
||||||
this.#state = parserStates.READ_DATA
|
this.#state = parserStates.READ_DATA
|
||||||
|
|
||||||
if (!this.#validatePayloadLength()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if (this.#state === parserStates.PAYLOADLENGTH_64) {
|
} else if (this.#state === parserStates.PAYLOADLENGTH_64) {
|
||||||
if (this.#byteOffset < 8) {
|
if (this.#byteOffset < 8) {
|
||||||
return callback()
|
return callback()
|
||||||
@ -30609,10 +30470,6 @@ class ByteParser extends Writable {
|
|||||||
|
|
||||||
this.#info.payloadLength = lower
|
this.#info.payloadLength = lower
|
||||||
this.#state = parserStates.READ_DATA
|
this.#state = parserStates.READ_DATA
|
||||||
|
|
||||||
if (!this.#validatePayloadLength()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if (this.#state === parserStates.READ_DATA) {
|
} else if (this.#state === parserStates.READ_DATA) {
|
||||||
if (this.#byteOffset < this.#info.payloadLength) {
|
if (this.#byteOffset < this.#info.payloadLength) {
|
||||||
return callback()
|
return callback()
|
||||||
@ -30625,43 +30482,27 @@ class ByteParser extends Writable {
|
|||||||
this.#state = parserStates.INFO
|
this.#state = parserStates.INFO
|
||||||
} else {
|
} else {
|
||||||
if (!this.#info.compressed) {
|
if (!this.#info.compressed) {
|
||||||
if (!this.writeFragments(body)) {
|
this.#fragments.push(body)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) {
|
|
||||||
failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the frame is not fragmented, a message has been received.
|
// If the frame is not fragmented, a message has been received.
|
||||||
// If the frame is fragmented, it will terminate with a fin bit set
|
// If the frame is fragmented, it will terminate with a fin bit set
|
||||||
// and an opcode of 0 (continuation), therefore we handle that when
|
// and an opcode of 0 (continuation), therefore we handle that when
|
||||||
// parsing continuation frames, not here.
|
// parsing continuation frames, not here.
|
||||||
if (!this.#info.fragmented && this.#info.fin) {
|
if (!this.#info.fragmented && this.#info.fin) {
|
||||||
websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments())
|
const fullMessage = Buffer.concat(this.#fragments)
|
||||||
|
websocketMessageReceived(this.ws, this.#info.binaryType, fullMessage)
|
||||||
|
this.#fragments.length = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#state = parserStates.INFO
|
this.#state = parserStates.INFO
|
||||||
} else {
|
} else {
|
||||||
this.#extensions.get('permessage-deflate').decompress(
|
this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => {
|
||||||
body,
|
|
||||||
this.#info.fin,
|
|
||||||
(error, data) => {
|
|
||||||
if (error) {
|
if (error) {
|
||||||
const code = error instanceof MessageSizeExceededError ? 1009 : 1007
|
failWebsocketConnection(this.ws, error.message)
|
||||||
failWebsocketConnectionWithCode(this.ws, code, error.message)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.writeFragments(data)) {
|
this.#fragments.push(data)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) {
|
|
||||||
failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.#info.fin) {
|
if (!this.#info.fin) {
|
||||||
this.#state = parserStates.INFO
|
this.#state = parserStates.INFO
|
||||||
@ -30670,13 +30511,13 @@ class ByteParser extends Writable {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments())
|
websocketMessageReceived(this.ws, this.#info.binaryType, Buffer.concat(this.#fragments))
|
||||||
|
|
||||||
this.#loop = true
|
this.#loop = true
|
||||||
this.#state = parserStates.INFO
|
this.#state = parserStates.INFO
|
||||||
|
this.#fragments.length = 0
|
||||||
this.run(callback)
|
this.run(callback)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
this.#loop = false
|
this.#loop = false
|
||||||
break
|
break
|
||||||
@ -30728,35 +30569,6 @@ class ByteParser extends Writable {
|
|||||||
return buffer
|
return buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFragments (fragment) {
|
|
||||||
if (
|
|
||||||
this.#maxFragments > 0 &&
|
|
||||||
this.#fragments.length === this.#maxFragments
|
|
||||||
) {
|
|
||||||
failWebsocketConnectionWithCode(this.ws, 1008, 'Too many message fragments')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#fragmentsBytes += fragment.length
|
|
||||||
this.#fragments.push(fragment)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
consumeFragments () {
|
|
||||||
const fragments = this.#fragments
|
|
||||||
|
|
||||||
if (fragments.length === 1) {
|
|
||||||
this.#fragmentsBytes = 0
|
|
||||||
return fragments.shift()
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = Buffer.concat(fragments, this.#fragmentsBytes)
|
|
||||||
this.#fragments = []
|
|
||||||
this.#fragmentsBytes = 0
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
parseCloseBody (data) {
|
parseCloseBody (data) {
|
||||||
assert(data.length !== 1)
|
assert(data.length !== 1)
|
||||||
|
|
||||||
@ -31788,14 +31600,7 @@ class WebSocket extends EventTarget {
|
|||||||
// once this happens, the connection is open
|
// once this happens, the connection is open
|
||||||
this[kResponse] = response
|
this[kResponse] = response
|
||||||
|
|
||||||
const webSocketOptions = this[kController]?.dispatcher?.webSocketOptions
|
const parser = new ByteParser(this, parsedExtensions)
|
||||||
const maxFragments = webSocketOptions?.maxFragments
|
|
||||||
const maxPayloadSize = webSocketOptions?.maxPayloadSize
|
|
||||||
|
|
||||||
const parser = new ByteParser(this, parsedExtensions, {
|
|
||||||
maxFragments,
|
|
||||||
maxPayloadSize
|
|
||||||
})
|
|
||||||
parser.on('drain', onParserDrain)
|
parser.on('drain', onParserDrain)
|
||||||
parser.on('error', onParserError.bind(this))
|
parser.on('error', onParserError.bind(this))
|
||||||
|
|
||||||
@ -44055,11 +43860,8 @@ class InputService {
|
|||||||
}) || null);
|
}) || null);
|
||||||
}
|
}
|
||||||
getServiceLogLevel() {
|
getServiceLogLevel() {
|
||||||
const configuredLevel = getInput(InputNames.ServiceLogLevel, {
|
const configuredLevel = getInput(InputNames.ServiceLogLevel, { required: false });
|
||||||
required: false,
|
if (configuredLevel && !Object.values(LogLevel).includes(configuredLevel)) {
|
||||||
});
|
|
||||||
if (configuredLevel &&
|
|
||||||
!Object.values(LogLevel).includes(configuredLevel)) {
|
|
||||||
throw new Error(`Invalid service log level "${configuredLevel}". Valid values are: ${Object.values(LogLevel).join(", ")}`);
|
throw new Error(`Invalid service log level "${configuredLevel}". Valid values are: ${Object.values(LogLevel).join(", ")}`);
|
||||||
}
|
}
|
||||||
return configuredLevel || LogLevel.Debug;
|
return configuredLevel || LogLevel.Debug;
|
||||||
@ -44141,12 +43943,12 @@ class DockerComposeService {
|
|||||||
parts.push("Docker Compose command failed");
|
parts.push("Docker Compose command failed");
|
||||||
}
|
}
|
||||||
// Add error stream output if available
|
// Add error stream output if available
|
||||||
if (error.err?.trim()) {
|
if (error.err && error.err.trim()) {
|
||||||
parts.push("\nError output:");
|
parts.push("\nError output:");
|
||||||
parts.push(error.err.trim());
|
parts.push(error.err.trim());
|
||||||
}
|
}
|
||||||
// Add standard output if available and different from error output
|
// Add standard output if available and different from error output
|
||||||
if (error.out?.trim() && error.out !== error.err) {
|
if (error.out && error.out.trim() && error.out !== error.err) {
|
||||||
parts.push("\nStandard output:");
|
parts.push("\nStandard output:");
|
||||||
parts.push(error.out.trim());
|
parts.push(error.out.trim());
|
||||||
}
|
}
|
||||||
@ -48435,17 +48237,14 @@ class DockerComposeInstallerService {
|
|||||||
constructor(manualInstallerAdapter) {
|
constructor(manualInstallerAdapter) {
|
||||||
this.manualInstallerAdapter = manualInstallerAdapter;
|
this.manualInstallerAdapter = manualInstallerAdapter;
|
||||||
}
|
}
|
||||||
async install({ composeVersion, cwd, githubToken, }) {
|
async install({ composeVersion, cwd, githubToken }) {
|
||||||
const currentVersion = await this.version({ cwd });
|
const currentVersion = await this.version({ cwd });
|
||||||
const normalizedCurrentVersion = currentVersion
|
const normalizedCurrentVersion = currentVersion ? this.normalizeVersion(currentVersion) : null;
|
||||||
? this.normalizeVersion(currentVersion)
|
|
||||||
: null;
|
|
||||||
const normalizedRequestedVersion = composeVersion
|
const normalizedRequestedVersion = composeVersion
|
||||||
? this.normalizeVersion(composeVersion)
|
? this.normalizeVersion(composeVersion)
|
||||||
: null;
|
: null;
|
||||||
const needsInstall = !currentVersion ||
|
const needsInstall = !currentVersion ||
|
||||||
(composeVersion &&
|
(composeVersion && normalizedRequestedVersion !== normalizedCurrentVersion);
|
||||||
normalizedRequestedVersion !== normalizedCurrentVersion);
|
|
||||||
if (!needsInstall) {
|
if (!needsInstall) {
|
||||||
return currentVersion;
|
return currentVersion;
|
||||||
}
|
}
|
||||||
@ -48459,8 +48258,7 @@ class DockerComposeInstallerService {
|
|||||||
await this.installVersion(targetVersion);
|
await this.installVersion(targetVersion);
|
||||||
const installedVersion = await this.version({ cwd });
|
const installedVersion = await this.version({ cwd });
|
||||||
if (!installedVersion ||
|
if (!installedVersion ||
|
||||||
this.normalizeVersion(installedVersion) !==
|
this.normalizeVersion(installedVersion) !== this.normalizeVersion(targetVersion)) {
|
||||||
this.normalizeVersion(targetVersion)) {
|
|
||||||
throw new Error(`Failed to install Docker Compose version "${targetVersion}", installed version is "${installedVersion ?? "unknown"}"`);
|
throw new Error(`Failed to install Docker Compose version "${targetVersion}", installed version is "${installedVersion ?? "unknown"}"`);
|
||||||
}
|
}
|
||||||
return installedVersion;
|
return installedVersion;
|
||||||
|
|||||||
390
dist/post.js
generated
vendored
390
dist/post.js
generated
vendored
@ -4940,6 +4940,8 @@ function defaultFactory (origin, opts) {
|
|||||||
|
|
||||||
class Agent extends DispatcherBase {
|
class Agent extends DispatcherBase {
|
||||||
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) {
|
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) {
|
||||||
|
super()
|
||||||
|
|
||||||
if (typeof factory !== 'function') {
|
if (typeof factory !== 'function') {
|
||||||
throw new InvalidArgumentError('factory must be a function.')
|
throw new InvalidArgumentError('factory must be a function.')
|
||||||
}
|
}
|
||||||
@ -4952,8 +4954,6 @@ class Agent extends DispatcherBase {
|
|||||||
throw new InvalidArgumentError('maxRedirections must be a positive number')
|
throw new InvalidArgumentError('maxRedirections must be a positive number')
|
||||||
}
|
}
|
||||||
|
|
||||||
super(options)
|
|
||||||
|
|
||||||
if (connect && typeof connect !== 'function') {
|
if (connect && typeof connect !== 'function') {
|
||||||
connect = { ...connect }
|
connect = { ...connect }
|
||||||
}
|
}
|
||||||
@ -5325,9 +5325,6 @@ const EMPTY_BUF = Buffer.alloc(0)
|
|||||||
const FastBuffer = Buffer[Symbol.species]
|
const FastBuffer = Buffer[Symbol.species]
|
||||||
const addListener = util.addListener
|
const addListener = util.addListener
|
||||||
const removeAllListeners = util.removeAllListeners
|
const removeAllListeners = util.removeAllListeners
|
||||||
const kIdleSocketValidation = Symbol('kIdleSocketValidation')
|
|
||||||
const kIdleSocketValidationTimeout = Symbol('kIdleSocketValidationTimeout')
|
|
||||||
const kSocketUsed = Symbol('kSocketUsed')
|
|
||||||
|
|
||||||
let extractBody
|
let extractBody
|
||||||
|
|
||||||
@ -5550,60 +5547,15 @@ class Parser {
|
|||||||
|
|
||||||
const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr
|
const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr
|
||||||
|
|
||||||
if (ret !== constants.ERROR.OK) {
|
|
||||||
const body = data.subarray(offset)
|
|
||||||
|
|
||||||
if (ret === constants.ERROR.PAUSED_UPGRADE) {
|
if (ret === constants.ERROR.PAUSED_UPGRADE) {
|
||||||
this.onUpgrade(body)
|
this.onUpgrade(data.slice(offset))
|
||||||
} else if (ret === constants.ERROR.PAUSED) {
|
} else if (ret === constants.ERROR.PAUSED) {
|
||||||
this.paused = true
|
this.paused = true
|
||||||
socket.unshift(body)
|
socket.unshift(data.slice(offset))
|
||||||
} else {
|
} else if (ret !== constants.ERROR.OK) {
|
||||||
throw this.createError(ret, body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
util.destroy(socket, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
finish () {
|
|
||||||
assert(currentParser === null)
|
|
||||||
assert(this.ptr != null)
|
|
||||||
assert(!this.paused)
|
|
||||||
|
|
||||||
const { llhttp } = this
|
|
||||||
|
|
||||||
let ret
|
|
||||||
|
|
||||||
try {
|
|
||||||
currentParser = this
|
|
||||||
ret = llhttp.llhttp_finish(this.ptr)
|
|
||||||
} finally {
|
|
||||||
currentParser = null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret === constants.ERROR.OK) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret === constants.ERROR.PAUSED || ret === constants.ERROR.PAUSED_UPGRADE) {
|
|
||||||
this.paused = true
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.createError(ret, EMPTY_BUF)
|
|
||||||
}
|
|
||||||
|
|
||||||
createError (ret, data) {
|
|
||||||
const { llhttp, contentLength, bytesRead } = this
|
|
||||||
|
|
||||||
if (contentLength && bytesRead !== parseInt(contentLength, 10)) {
|
|
||||||
return new ResponseContentLengthMismatchError()
|
|
||||||
}
|
|
||||||
|
|
||||||
const ptr = llhttp.llhttp_get_error_reason(this.ptr)
|
const ptr = llhttp.llhttp_get_error_reason(this.ptr)
|
||||||
let message = ''
|
let message = ''
|
||||||
|
/* istanbul ignore else: difficult to make a test case for */
|
||||||
if (ptr) {
|
if (ptr) {
|
||||||
const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)
|
const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)
|
||||||
message =
|
message =
|
||||||
@ -5611,8 +5563,11 @@ class Parser {
|
|||||||
Buffer.from(llhttp.memory.buffer, ptr, len).toString() +
|
Buffer.from(llhttp.memory.buffer, ptr, len).toString() +
|
||||||
')'
|
')'
|
||||||
}
|
}
|
||||||
|
throw new HTTPParserError(message, constants.ERROR[ret], data.slice(offset))
|
||||||
return new HTTPParserError(message, constants.ERROR[ret], data)
|
}
|
||||||
|
} catch (err) {
|
||||||
|
util.destroy(socket, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy () {
|
destroy () {
|
||||||
@ -5642,11 +5597,6 @@ class Parser {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client[kRunning] === 0) {
|
|
||||||
util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = client[kQueue][client[kRunningIdx]]
|
const request = client[kQueue][client[kRunningIdx]]
|
||||||
if (!request) {
|
if (!request) {
|
||||||
return -1
|
return -1
|
||||||
@ -5750,11 +5700,6 @@ class Parser {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client[kRunning] === 0) {
|
|
||||||
util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = client[kQueue][client[kRunningIdx]]
|
const request = client[kQueue][client[kRunningIdx]]
|
||||||
|
|
||||||
/* istanbul ignore next: difficult to make a test case for */
|
/* istanbul ignore next: difficult to make a test case for */
|
||||||
@ -5928,7 +5873,6 @@ class Parser {
|
|||||||
request.onComplete(headers)
|
request.onComplete(headers)
|
||||||
|
|
||||||
client[kQueue][client[kRunningIdx]++] = null
|
client[kQueue][client[kRunningIdx]++] = null
|
||||||
socket[kSocketUsed] = true
|
|
||||||
|
|
||||||
if (socket[kWriting]) {
|
if (socket[kWriting]) {
|
||||||
assert(client[kRunning] === 0)
|
assert(client[kRunning] === 0)
|
||||||
@ -5987,9 +5931,6 @@ async function connectH1 (client, socket) {
|
|||||||
socket[kWriting] = false
|
socket[kWriting] = false
|
||||||
socket[kReset] = false
|
socket[kReset] = false
|
||||||
socket[kBlocking] = false
|
socket[kBlocking] = false
|
||||||
socket[kIdleSocketValidation] = 0
|
|
||||||
socket[kIdleSocketValidationTimeout] = null
|
|
||||||
socket[kSocketUsed] = false
|
|
||||||
socket[kParser] = new Parser(client, socket, llhttpInstance)
|
socket[kParser] = new Parser(client, socket, llhttpInstance)
|
||||||
|
|
||||||
addListener(socket, 'error', function (err) {
|
addListener(socket, 'error', function (err) {
|
||||||
@ -6000,11 +5941,8 @@ async function connectH1 (client, socket) {
|
|||||||
// On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
|
// On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
|
||||||
// to the user.
|
// to the user.
|
||||||
if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {
|
if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {
|
||||||
const parserErr = parser.finish()
|
// We treat all incoming data so for as a valid response.
|
||||||
if (parserErr) {
|
parser.onMessageComplete()
|
||||||
this[kError] = parserErr
|
|
||||||
this[kClient][kOnError](parserErr)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6023,10 +5961,8 @@ async function connectH1 (client, socket) {
|
|||||||
const parser = this[kParser]
|
const parser = this[kParser]
|
||||||
|
|
||||||
if (parser.statusCode && !parser.shouldKeepAlive) {
|
if (parser.statusCode && !parser.shouldKeepAlive) {
|
||||||
const parserErr = parser.finish()
|
// We treat all incoming data so far as a valid response.
|
||||||
if (parserErr) {
|
parser.onMessageComplete()
|
||||||
util.destroy(this, parserErr)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6036,11 +5972,10 @@ async function connectH1 (client, socket) {
|
|||||||
const client = this[kClient]
|
const client = this[kClient]
|
||||||
const parser = this[kParser]
|
const parser = this[kParser]
|
||||||
|
|
||||||
clearIdleSocketValidation(this)
|
|
||||||
|
|
||||||
if (parser) {
|
if (parser) {
|
||||||
if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
|
if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
|
||||||
this[kError] = parser.finish() || this[kError]
|
// We treat all incoming data so far as a valid response.
|
||||||
|
parser.onMessageComplete()
|
||||||
}
|
}
|
||||||
|
|
||||||
this[kParser].destroy()
|
this[kParser].destroy()
|
||||||
@ -6103,7 +6038,7 @@ async function connectH1 (client, socket) {
|
|||||||
return socket.destroyed
|
return socket.destroyed
|
||||||
},
|
},
|
||||||
busy (request) {
|
busy (request) {
|
||||||
if (socket[kWriting] || socket[kReset] || socket[kBlocking] || socket[kIdleSocketValidation] === 1) {
|
if (socket[kWriting] || socket[kReset] || socket[kBlocking]) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6141,31 +6076,6 @@ async function connectH1 (client, socket) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearIdleSocketValidation (socket) {
|
|
||||||
if (socket[kIdleSocketValidationTimeout]) {
|
|
||||||
clearTimeout(socket[kIdleSocketValidationTimeout])
|
|
||||||
socket[kIdleSocketValidationTimeout] = null
|
|
||||||
}
|
|
||||||
|
|
||||||
socket[kIdleSocketValidation] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function scheduleIdleSocketValidation (client, socket) {
|
|
||||||
socket[kIdleSocketValidation] = 1
|
|
||||||
socket[kIdleSocketValidationTimeout] = setTimeout(() => {
|
|
||||||
socket[kIdleSocketValidationTimeout] = null
|
|
||||||
socket[kIdleSocketValidation] = 2
|
|
||||||
|
|
||||||
if (client[kSocket] === socket && !socket.destroyed) {
|
|
||||||
client[kResume]()
|
|
||||||
}
|
|
||||||
}, 0)
|
|
||||||
socket[kIdleSocketValidationTimeout].unref?.()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import('./client.js')} client
|
|
||||||
*/
|
|
||||||
function resumeH1 (client) {
|
function resumeH1 (client) {
|
||||||
const socket = client[kSocket]
|
const socket = client[kSocket]
|
||||||
|
|
||||||
@ -6180,32 +6090,6 @@ function resumeH1 (client) {
|
|||||||
socket[kNoRef] = false
|
socket[kNoRef] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client[kRunning] === 0 && client[kPending] > 0 && socket[kSocketUsed]) {
|
|
||||||
if (socket[kIdleSocketValidation] === 0) {
|
|
||||||
scheduleIdleSocketValidation(client, socket)
|
|
||||||
socket[kParser].readMore()
|
|
||||||
if (socket.destroyed) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (socket[kIdleSocketValidation] === 1) {
|
|
||||||
socket[kParser].readMore()
|
|
||||||
if (socket.destroyed) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client[kRunning] === 0) {
|
|
||||||
socket[kParser].readMore()
|
|
||||||
if (socket.destroyed) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client[kSize] === 0) {
|
if (client[kSize] === 0) {
|
||||||
if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {
|
if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {
|
||||||
socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE)
|
socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE)
|
||||||
@ -6299,7 +6183,6 @@ function writeH1 (client, request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const socket = client[kSocket]
|
const socket = client[kSocket]
|
||||||
clearIdleSocketValidation(socket)
|
|
||||||
|
|
||||||
const abort = (err) => {
|
const abort = (err) => {
|
||||||
if (request.aborted || request.completed) {
|
if (request.aborted || request.completed) {
|
||||||
@ -7619,10 +7502,9 @@ class Client extends DispatcherBase {
|
|||||||
autoSelectFamilyAttemptTimeout,
|
autoSelectFamilyAttemptTimeout,
|
||||||
// h2
|
// h2
|
||||||
maxConcurrentStreams,
|
maxConcurrentStreams,
|
||||||
allowH2,
|
allowH2
|
||||||
webSocket
|
|
||||||
} = {}) {
|
} = {}) {
|
||||||
super({ webSocket })
|
super()
|
||||||
|
|
||||||
if (keepAlive !== undefined) {
|
if (keepAlive !== undefined) {
|
||||||
throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead')
|
throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead')
|
||||||
@ -8154,24 +8036,15 @@ const { kDestroy, kClose, kClosed, kDestroyed, kDispatch, kInterceptors } = __nc
|
|||||||
const kOnDestroyed = Symbol('onDestroyed')
|
const kOnDestroyed = Symbol('onDestroyed')
|
||||||
const kOnClosed = Symbol('onClosed')
|
const kOnClosed = Symbol('onClosed')
|
||||||
const kInterceptedDispatch = Symbol('Intercepted Dispatch')
|
const kInterceptedDispatch = Symbol('Intercepted Dispatch')
|
||||||
const kWebSocketOptions = Symbol('webSocketOptions')
|
|
||||||
|
|
||||||
class DispatcherBase extends Dispatcher {
|
class DispatcherBase extends Dispatcher {
|
||||||
constructor (opts) {
|
constructor () {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this[kDestroyed] = false
|
this[kDestroyed] = false
|
||||||
this[kOnDestroyed] = null
|
this[kOnDestroyed] = null
|
||||||
this[kClosed] = false
|
this[kClosed] = false
|
||||||
this[kOnClosed] = []
|
this[kOnClosed] = []
|
||||||
this[kWebSocketOptions] = opts?.webSocket ?? {}
|
|
||||||
}
|
|
||||||
|
|
||||||
get webSocketOptions () {
|
|
||||||
return {
|
|
||||||
maxFragments: this[kWebSocketOptions].maxFragments ?? 131072,
|
|
||||||
maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get destroyed () {
|
get destroyed () {
|
||||||
@ -8731,8 +8604,8 @@ const kRemoveClient = Symbol('remove client')
|
|||||||
const kStats = Symbol('stats')
|
const kStats = Symbol('stats')
|
||||||
|
|
||||||
class PoolBase extends DispatcherBase {
|
class PoolBase extends DispatcherBase {
|
||||||
constructor (opts) {
|
constructor () {
|
||||||
super(opts)
|
super()
|
||||||
|
|
||||||
this[kQueue] = new FixedQueue()
|
this[kQueue] = new FixedQueue()
|
||||||
this[kClients] = []
|
this[kClients] = []
|
||||||
@ -8991,6 +8864,8 @@ class Pool extends PoolBase {
|
|||||||
allowH2,
|
allowH2,
|
||||||
...options
|
...options
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
super()
|
||||||
|
|
||||||
if (connections != null && (!Number.isFinite(connections) || connections < 0)) {
|
if (connections != null && (!Number.isFinite(connections) || connections < 0)) {
|
||||||
throw new InvalidArgumentError('invalid connections')
|
throw new InvalidArgumentError('invalid connections')
|
||||||
}
|
}
|
||||||
@ -9015,8 +8890,6 @@ class Pool extends PoolBase {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
super(options)
|
|
||||||
|
|
||||||
this[kInterceptors] = options.interceptors?.Pool && Array.isArray(options.interceptors.Pool)
|
this[kInterceptors] = options.interceptors?.Pool && Array.isArray(options.interceptors.Pool)
|
||||||
? options.interceptors.Pool
|
? options.interceptors.Pool
|
||||||
: []
|
: []
|
||||||
@ -14069,25 +13942,32 @@ function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {})
|
|||||||
// If the attribute-name case-insensitively matches the string
|
// If the attribute-name case-insensitively matches the string
|
||||||
// "SameSite", the user agent MUST process the cookie-av as follows:
|
// "SameSite", the user agent MUST process the cookie-av as follows:
|
||||||
|
|
||||||
const attributeValueLowercase = attributeValue.toLowerCase()
|
// 1. Let enforcement be "Default".
|
||||||
|
let enforcement = 'Default'
|
||||||
|
|
||||||
// 1. If cookie-av's attribute-value is a case-insensitive match for
|
const attributeValueLowercase = attributeValue.toLowerCase()
|
||||||
// "None", append an attribute to the cookie-attribute-list with an
|
|
||||||
// attribute-name of "SameSite" and an attribute-value of "None".
|
|
||||||
if (attributeValueLowercase === 'none') {
|
|
||||||
cookieAttributeList.sameSite = 'None'
|
|
||||||
} else if (attributeValueLowercase === 'strict') {
|
|
||||||
// 2. If cookie-av's attribute-value is a case-insensitive match for
|
// 2. If cookie-av's attribute-value is a case-insensitive match for
|
||||||
// "Strict", append an attribute to the cookie-attribute-list with
|
// "None", set enforcement to "None".
|
||||||
// an attribute-name of "SameSite" and an attribute-value of
|
if (attributeValueLowercase.includes('none')) {
|
||||||
// "Strict".
|
enforcement = 'None'
|
||||||
cookieAttributeList.sameSite = 'Strict'
|
|
||||||
} else if (attributeValueLowercase === 'lax') {
|
|
||||||
// 3. If cookie-av's attribute-value is a case-insensitive match for
|
|
||||||
// "Lax", append an attribute to the cookie-attribute-list with an
|
|
||||||
// attribute-name of "SameSite" and an attribute-value of "Lax".
|
|
||||||
cookieAttributeList.sameSite = 'Lax'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. If cookie-av's attribute-value is a case-insensitive match for
|
||||||
|
// "Strict", set enforcement to "Strict".
|
||||||
|
if (attributeValueLowercase.includes('strict')) {
|
||||||
|
enforcement = 'Strict'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. If cookie-av's attribute-value is a case-insensitive match for
|
||||||
|
// "Lax", set enforcement to "Lax".
|
||||||
|
if (attributeValueLowercase.includes('lax')) {
|
||||||
|
enforcement = 'Lax'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Append an attribute to the cookie-attribute-list with an
|
||||||
|
// attribute-name of "SameSite" and an attribute-value of
|
||||||
|
// enforcement.
|
||||||
|
cookieAttributeList.sameSite = enforcement
|
||||||
} else {
|
} else {
|
||||||
cookieAttributeList.unparsed ??= []
|
cookieAttributeList.unparsed ??= []
|
||||||
|
|
||||||
@ -26764,35 +26644,40 @@ const tail = Buffer.from([0x00, 0x00, 0xff, 0xff])
|
|||||||
const kBuffer = Symbol('kBuffer')
|
const kBuffer = Symbol('kBuffer')
|
||||||
const kLength = Symbol('kLength')
|
const kLength = Symbol('kLength')
|
||||||
|
|
||||||
|
// Default maximum decompressed message size: 4 MB
|
||||||
|
const kDefaultMaxDecompressedSize = 4 * 1024 * 1024
|
||||||
|
|
||||||
class PerMessageDeflate {
|
class PerMessageDeflate {
|
||||||
/** @type {import('node:zlib').InflateRaw} */
|
/** @type {import('node:zlib').InflateRaw} */
|
||||||
#inflate
|
#inflate
|
||||||
|
|
||||||
#options = {}
|
#options = {}
|
||||||
|
|
||||||
#maxPayloadSize = 0
|
/** @type {boolean} */
|
||||||
|
#aborted = false
|
||||||
|
|
||||||
|
/** @type {Function|null} */
|
||||||
|
#currentCallback = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Map<string, string>} extensions
|
* @param {Map<string, string>} extensions
|
||||||
*/
|
*/
|
||||||
constructor (extensions, options) {
|
constructor (extensions) {
|
||||||
this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover')
|
this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover')
|
||||||
this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits')
|
this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits')
|
||||||
|
|
||||||
this.#maxPayloadSize = options.maxPayloadSize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Decompress a compressed payload.
|
|
||||||
* @param {Buffer} chunk Compressed data
|
|
||||||
* @param {boolean} fin Final fragment flag
|
|
||||||
* @param {Function} callback Callback function
|
|
||||||
*/
|
|
||||||
decompress (chunk, fin, callback) {
|
decompress (chunk, fin, callback) {
|
||||||
// An endpoint uses the following algorithm to decompress a message.
|
// An endpoint uses the following algorithm to decompress a message.
|
||||||
// 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
|
// 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
|
||||||
// payload of the message.
|
// payload of the message.
|
||||||
// 2. Decompress the resulting data using DEFLATE.
|
// 2. Decompress the resulting data using DEFLATE.
|
||||||
|
|
||||||
|
if (this.#aborted) {
|
||||||
|
callback(new MessageSizeExceededError())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.#inflate) {
|
if (!this.#inflate) {
|
||||||
let windowBits = Z_DEFAULT_WINDOWBITS
|
let windowBits = Z_DEFAULT_WINDOWBITS
|
||||||
|
|
||||||
@ -26815,12 +26700,23 @@ class PerMessageDeflate {
|
|||||||
this.#inflate[kLength] = 0
|
this.#inflate[kLength] = 0
|
||||||
|
|
||||||
this.#inflate.on('data', (data) => {
|
this.#inflate.on('data', (data) => {
|
||||||
|
if (this.#aborted) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.#inflate[kLength] += data.length
|
this.#inflate[kLength] += data.length
|
||||||
|
|
||||||
if (this.#maxPayloadSize > 0 && this.#inflate[kLength] > this.#maxPayloadSize) {
|
if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) {
|
||||||
callback(new MessageSizeExceededError())
|
this.#aborted = true
|
||||||
this.#inflate.removeAllListeners()
|
this.#inflate.removeAllListeners()
|
||||||
|
this.#inflate.destroy()
|
||||||
this.#inflate = null
|
this.#inflate = null
|
||||||
|
|
||||||
|
if (this.#currentCallback) {
|
||||||
|
const cb = this.#currentCallback
|
||||||
|
this.#currentCallback = null
|
||||||
|
cb(new MessageSizeExceededError())
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26833,13 +26729,14 @@ class PerMessageDeflate {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.#currentCallback = callback
|
||||||
this.#inflate.write(chunk)
|
this.#inflate.write(chunk)
|
||||||
if (fin) {
|
if (fin) {
|
||||||
this.#inflate.write(tail)
|
this.#inflate.write(tail)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#inflate.flush(() => {
|
this.#inflate.flush(() => {
|
||||||
if (!this.#inflate) {
|
if (this.#aborted || !this.#inflate) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26847,6 +26744,7 @@ class PerMessageDeflate {
|
|||||||
|
|
||||||
this.#inflate[kBuffer].length = 0
|
this.#inflate[kBuffer].length = 0
|
||||||
this.#inflate[kLength] = 0
|
this.#inflate[kLength] = 0
|
||||||
|
this.#currentCallback = null
|
||||||
|
|
||||||
callback(null, full)
|
callback(null, full)
|
||||||
})
|
})
|
||||||
@ -26881,12 +26779,6 @@ const {
|
|||||||
const { WebsocketFrameSend } = __nccwpck_require__(3264)
|
const { WebsocketFrameSend } = __nccwpck_require__(3264)
|
||||||
const { closeWebSocketConnection } = __nccwpck_require__(6897)
|
const { closeWebSocketConnection } = __nccwpck_require__(6897)
|
||||||
const { PerMessageDeflate } = __nccwpck_require__(9469)
|
const { PerMessageDeflate } = __nccwpck_require__(9469)
|
||||||
const { MessageSizeExceededError } = __nccwpck_require__(8707)
|
|
||||||
|
|
||||||
function failWebsocketConnectionWithCode (ws, code, reason) {
|
|
||||||
closeWebSocketConnection(ws, code, reason, Buffer.byteLength(reason))
|
|
||||||
failWebsocketConnection(ws, reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This code was influenced by ws released under the MIT license.
|
// This code was influenced by ws released under the MIT license.
|
||||||
// Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
// Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
||||||
@ -26895,7 +26787,6 @@ function failWebsocketConnectionWithCode (ws, code, reason) {
|
|||||||
|
|
||||||
class ByteParser extends Writable {
|
class ByteParser extends Writable {
|
||||||
#buffers = []
|
#buffers = []
|
||||||
#fragmentsBytes = 0
|
|
||||||
#byteOffset = 0
|
#byteOffset = 0
|
||||||
#loop = false
|
#loop = false
|
||||||
|
|
||||||
@ -26907,27 +26798,18 @@ class ByteParser extends Writable {
|
|||||||
/** @type {Map<string, PerMessageDeflate>} */
|
/** @type {Map<string, PerMessageDeflate>} */
|
||||||
#extensions
|
#extensions
|
||||||
|
|
||||||
/** @type {number} */
|
|
||||||
#maxFragments
|
|
||||||
|
|
||||||
/** @type {number} */
|
|
||||||
#maxPayloadSize
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('./websocket').WebSocket} ws
|
* @param {import('./websocket').WebSocket} ws
|
||||||
* @param {Map<string, string>|null} extensions
|
* @param {Map<string, string>|null} extensions
|
||||||
* @param {{ maxFragments?: number, maxPayloadSize?: number }} [options]
|
|
||||||
*/
|
*/
|
||||||
constructor (ws, extensions, options = {}) {
|
constructor (ws, extensions) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.ws = ws
|
this.ws = ws
|
||||||
this.#extensions = extensions == null ? new Map() : extensions
|
this.#extensions = extensions == null ? new Map() : extensions
|
||||||
this.#maxFragments = options.maxFragments ?? 0
|
|
||||||
this.#maxPayloadSize = options.maxPayloadSize ?? 0
|
|
||||||
|
|
||||||
if (this.#extensions.has('permessage-deflate')) {
|
if (this.#extensions.has('permessage-deflate')) {
|
||||||
this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions, options))
|
this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26943,19 +26825,6 @@ class ByteParser extends Writable {
|
|||||||
this.run(callback)
|
this.run(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
#validatePayloadLength () {
|
|
||||||
if (
|
|
||||||
this.#maxPayloadSize > 0 &&
|
|
||||||
!isControlFrame(this.#info.opcode) &&
|
|
||||||
this.#info.payloadLength + this.#fragmentsBytes > this.#maxPayloadSize
|
|
||||||
) {
|
|
||||||
failWebsocketConnectionWithCode(this.ws, 1009, 'Payload size exceeds maximum allowed size')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs whenever a new chunk is received.
|
* Runs whenever a new chunk is received.
|
||||||
* Callback is called whenever there are no more chunks buffering,
|
* Callback is called whenever there are no more chunks buffering,
|
||||||
@ -27044,10 +26913,6 @@ class ByteParser extends Writable {
|
|||||||
if (payloadLength <= 125) {
|
if (payloadLength <= 125) {
|
||||||
this.#info.payloadLength = payloadLength
|
this.#info.payloadLength = payloadLength
|
||||||
this.#state = parserStates.READ_DATA
|
this.#state = parserStates.READ_DATA
|
||||||
|
|
||||||
if (!this.#validatePayloadLength()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if (payloadLength === 126) {
|
} else if (payloadLength === 126) {
|
||||||
this.#state = parserStates.PAYLOADLENGTH_16
|
this.#state = parserStates.PAYLOADLENGTH_16
|
||||||
} else if (payloadLength === 127) {
|
} else if (payloadLength === 127) {
|
||||||
@ -27072,10 +26937,6 @@ class ByteParser extends Writable {
|
|||||||
|
|
||||||
this.#info.payloadLength = buffer.readUInt16BE(0)
|
this.#info.payloadLength = buffer.readUInt16BE(0)
|
||||||
this.#state = parserStates.READ_DATA
|
this.#state = parserStates.READ_DATA
|
||||||
|
|
||||||
if (!this.#validatePayloadLength()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if (this.#state === parserStates.PAYLOADLENGTH_64) {
|
} else if (this.#state === parserStates.PAYLOADLENGTH_64) {
|
||||||
if (this.#byteOffset < 8) {
|
if (this.#byteOffset < 8) {
|
||||||
return callback()
|
return callback()
|
||||||
@ -27098,10 +26959,6 @@ class ByteParser extends Writable {
|
|||||||
|
|
||||||
this.#info.payloadLength = lower
|
this.#info.payloadLength = lower
|
||||||
this.#state = parserStates.READ_DATA
|
this.#state = parserStates.READ_DATA
|
||||||
|
|
||||||
if (!this.#validatePayloadLength()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if (this.#state === parserStates.READ_DATA) {
|
} else if (this.#state === parserStates.READ_DATA) {
|
||||||
if (this.#byteOffset < this.#info.payloadLength) {
|
if (this.#byteOffset < this.#info.payloadLength) {
|
||||||
return callback()
|
return callback()
|
||||||
@ -27114,43 +26971,27 @@ class ByteParser extends Writable {
|
|||||||
this.#state = parserStates.INFO
|
this.#state = parserStates.INFO
|
||||||
} else {
|
} else {
|
||||||
if (!this.#info.compressed) {
|
if (!this.#info.compressed) {
|
||||||
if (!this.writeFragments(body)) {
|
this.#fragments.push(body)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) {
|
|
||||||
failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the frame is not fragmented, a message has been received.
|
// If the frame is not fragmented, a message has been received.
|
||||||
// If the frame is fragmented, it will terminate with a fin bit set
|
// If the frame is fragmented, it will terminate with a fin bit set
|
||||||
// and an opcode of 0 (continuation), therefore we handle that when
|
// and an opcode of 0 (continuation), therefore we handle that when
|
||||||
// parsing continuation frames, not here.
|
// parsing continuation frames, not here.
|
||||||
if (!this.#info.fragmented && this.#info.fin) {
|
if (!this.#info.fragmented && this.#info.fin) {
|
||||||
websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments())
|
const fullMessage = Buffer.concat(this.#fragments)
|
||||||
|
websocketMessageReceived(this.ws, this.#info.binaryType, fullMessage)
|
||||||
|
this.#fragments.length = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#state = parserStates.INFO
|
this.#state = parserStates.INFO
|
||||||
} else {
|
} else {
|
||||||
this.#extensions.get('permessage-deflate').decompress(
|
this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => {
|
||||||
body,
|
|
||||||
this.#info.fin,
|
|
||||||
(error, data) => {
|
|
||||||
if (error) {
|
if (error) {
|
||||||
const code = error instanceof MessageSizeExceededError ? 1009 : 1007
|
failWebsocketConnection(this.ws, error.message)
|
||||||
failWebsocketConnectionWithCode(this.ws, code, error.message)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.writeFragments(data)) {
|
this.#fragments.push(data)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) {
|
|
||||||
failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.#info.fin) {
|
if (!this.#info.fin) {
|
||||||
this.#state = parserStates.INFO
|
this.#state = parserStates.INFO
|
||||||
@ -27159,13 +27000,13 @@ class ByteParser extends Writable {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments())
|
websocketMessageReceived(this.ws, this.#info.binaryType, Buffer.concat(this.#fragments))
|
||||||
|
|
||||||
this.#loop = true
|
this.#loop = true
|
||||||
this.#state = parserStates.INFO
|
this.#state = parserStates.INFO
|
||||||
|
this.#fragments.length = 0
|
||||||
this.run(callback)
|
this.run(callback)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
this.#loop = false
|
this.#loop = false
|
||||||
break
|
break
|
||||||
@ -27217,35 +27058,6 @@ class ByteParser extends Writable {
|
|||||||
return buffer
|
return buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFragments (fragment) {
|
|
||||||
if (
|
|
||||||
this.#maxFragments > 0 &&
|
|
||||||
this.#fragments.length === this.#maxFragments
|
|
||||||
) {
|
|
||||||
failWebsocketConnectionWithCode(this.ws, 1008, 'Too many message fragments')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#fragmentsBytes += fragment.length
|
|
||||||
this.#fragments.push(fragment)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
consumeFragments () {
|
|
||||||
const fragments = this.#fragments
|
|
||||||
|
|
||||||
if (fragments.length === 1) {
|
|
||||||
this.#fragmentsBytes = 0
|
|
||||||
return fragments.shift()
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = Buffer.concat(fragments, this.#fragmentsBytes)
|
|
||||||
this.#fragments = []
|
|
||||||
this.#fragmentsBytes = 0
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
parseCloseBody (data) {
|
parseCloseBody (data) {
|
||||||
assert(data.length !== 1)
|
assert(data.length !== 1)
|
||||||
|
|
||||||
@ -28277,14 +28089,7 @@ class WebSocket extends EventTarget {
|
|||||||
// once this happens, the connection is open
|
// once this happens, the connection is open
|
||||||
this[kResponse] = response
|
this[kResponse] = response
|
||||||
|
|
||||||
const webSocketOptions = this[kController]?.dispatcher?.webSocketOptions
|
const parser = new ByteParser(this, parsedExtensions)
|
||||||
const maxFragments = webSocketOptions?.maxFragments
|
|
||||||
const maxPayloadSize = webSocketOptions?.maxPayloadSize
|
|
||||||
|
|
||||||
const parser = new ByteParser(this, parsedExtensions, {
|
|
||||||
maxFragments,
|
|
||||||
maxPayloadSize
|
|
||||||
})
|
|
||||||
parser.on('drain', onParserDrain)
|
parser.on('drain', onParserDrain)
|
||||||
parser.on('error', onParserError.bind(this))
|
parser.on('error', onParserError.bind(this))
|
||||||
|
|
||||||
@ -40307,11 +40112,8 @@ class InputService {
|
|||||||
}) || null);
|
}) || null);
|
||||||
}
|
}
|
||||||
getServiceLogLevel() {
|
getServiceLogLevel() {
|
||||||
const configuredLevel = getInput(InputNames.ServiceLogLevel, {
|
const configuredLevel = getInput(InputNames.ServiceLogLevel, { required: false });
|
||||||
required: false,
|
if (configuredLevel && !Object.values(LogLevel).includes(configuredLevel)) {
|
||||||
});
|
|
||||||
if (configuredLevel &&
|
|
||||||
!Object.values(LogLevel).includes(configuredLevel)) {
|
|
||||||
throw new Error(`Invalid service log level "${configuredLevel}". Valid values are: ${Object.values(LogLevel).join(", ")}`);
|
throw new Error(`Invalid service log level "${configuredLevel}". Valid values are: ${Object.values(LogLevel).join(", ")}`);
|
||||||
}
|
}
|
||||||
return configuredLevel || LogLevel.Debug;
|
return configuredLevel || LogLevel.Debug;
|
||||||
@ -40393,12 +40195,12 @@ class DockerComposeService {
|
|||||||
parts.push("Docker Compose command failed");
|
parts.push("Docker Compose command failed");
|
||||||
}
|
}
|
||||||
// Add error stream output if available
|
// Add error stream output if available
|
||||||
if (error.err?.trim()) {
|
if (error.err && error.err.trim()) {
|
||||||
parts.push("\nError output:");
|
parts.push("\nError output:");
|
||||||
parts.push(error.err.trim());
|
parts.push(error.err.trim());
|
||||||
}
|
}
|
||||||
// Add standard output if available and different from error output
|
// Add standard output if available and different from error output
|
||||||
if (error.out?.trim() && error.out !== error.err) {
|
if (error.out && error.out.trim() && error.out !== error.err) {
|
||||||
parts.push("\nStandard output:");
|
parts.push("\nStandard output:");
|
||||||
parts.push(error.out.trim());
|
parts.push(error.out.trim());
|
||||||
}
|
}
|
||||||
|
|||||||
3
eslint.config.mjs
Normal file
3
eslint.config.mjs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import tsDevToolsCore from "@ts-dev-tools/core/dist/eslint-plugin-ts-dev-tools/index.js";
|
||||||
|
|
||||||
|
export default tsDevToolsCore.default;
|
||||||
10155
package-lock.json
generated
10155
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
94
package.json
94
package.json
@ -22,43 +22,105 @@
|
|||||||
".": "./dist/index.js"
|
".": "./dist/index.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=24"
|
"node": ">=20"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^3.0.1",
|
"@actions/core": "^3.0.0",
|
||||||
"@actions/github": "^9.1.1",
|
"@actions/github": "^9.1.0",
|
||||||
"@actions/tool-cache": "^4.0.0",
|
"@actions/tool-cache": "^4.0.0",
|
||||||
"@octokit/action": "^8.0.4",
|
"@octokit/action": "^8.0.4",
|
||||||
"docker-compose": "^1.4.2"
|
"docker-compose": "^1.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ts-dev-tools/core": "^1.12.4",
|
"@ts-dev-tools/core": "^1.12.0",
|
||||||
"@vercel/ncc": "^0.44.0"
|
"@vercel/ncc": "^0.38.4",
|
||||||
|
"eslint-plugin-github": "^6.0.0",
|
||||||
|
"eslint-plugin-jsonc": "^3.1.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"package": "npm run package:index && npm run package:post",
|
"package": "npm run package:index && npm run package:post",
|
||||||
"package:index": "ncc build src/index.ts -o dist --license licenses.txt",
|
"package:index": "ncc build src/index.ts -o dist --license licenses.txt",
|
||||||
"package:post": "ncc build src/post.ts -o dist/post && mv dist/post/index.js dist/post.js && rm -rf dist/post",
|
"package:post": "ncc build src/post.ts -o dist/post && mv dist/post/index.js dist/post.js && rm -rf dist/post",
|
||||||
"package:watch": "npm run package -- --watch",
|
"package:watch": "npm run package -- --watch",
|
||||||
"lint": "biome lint --error-on-warnings .",
|
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
||||||
"lint:ci": "biome lint --error-on-warnings . --reporter=sarif | tee biome-report.sarif",
|
"lint:ci": "npm run lint -- --output-file eslint-report.json --format json",
|
||||||
"all": "npm run format && npm run lint:ci && npm run test:ci && npm run package",
|
"all": "npm run format && npm run lint:ci && npm run test:ci && npm run package",
|
||||||
"build": "tsc --noEmit",
|
"build": "tsc --noEmit",
|
||||||
"format": "biome format --write .",
|
"format": "prettier --cache --write .",
|
||||||
"test": "vitest run",
|
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --detectOpenHandles --forceExit --maxWorkers=50%",
|
||||||
"test:watch": "vitest",
|
"test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch --maxWorkers=25%",
|
||||||
"test:cov": "vitest run --reporter=default --reporter=junit --outputFile=junit.xml --coverage.enabled --coverage.reporter=lcov --coverage.reporter=text",
|
"test:cov": "npm run test -- --coverage",
|
||||||
"test:ci": "npm run test:cov",
|
"test:ci": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage --runInBand --detectOpenHandles --forceExit",
|
||||||
"prepare": "ts-dev-tools install",
|
"prepare": "ts-dev-tools install"
|
||||||
"check": "biome check --error-on-warnings --write .",
|
},
|
||||||
"vitest": "vitest"
|
"jest": {
|
||||||
|
"preset": "ts-jest/presets/default-esm",
|
||||||
|
"verbose": true,
|
||||||
|
"clearMocks": true,
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"extensionsToTreatAsEsm": [
|
||||||
|
".ts"
|
||||||
|
],
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"js",
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"testMatch": [
|
||||||
|
"**/*.test.ts",
|
||||||
|
"**/__tests__/**/*.[jt]s?(x)",
|
||||||
|
"**/?(*.)+(spec|test)?(.*).+(ts|tsx|js)"
|
||||||
|
],
|
||||||
|
"testPathIgnorePatterns": [
|
||||||
|
"/node_modules/",
|
||||||
|
"/dist/"
|
||||||
|
],
|
||||||
|
"moduleNameMapper": {
|
||||||
|
"^(\\.{1,2}/.*)\\.js$": "$1"
|
||||||
|
},
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.ts$": [
|
||||||
|
"ts-jest",
|
||||||
|
{
|
||||||
|
"useESM": true,
|
||||||
|
"tsconfig": "tsconfig.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"injectGlobals": true,
|
||||||
|
"coverageReporters": [
|
||||||
|
"json-summary",
|
||||||
|
"text",
|
||||||
|
"lcov"
|
||||||
|
],
|
||||||
|
"collectCoverage": true,
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"./src/**",
|
||||||
|
"**/src/**/*.[jt]s?(x)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"semi": true,
|
||||||
|
"printWidth": 100,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"plugins": []
|
||||||
},
|
},
|
||||||
"commitlint": {
|
"commitlint": {
|
||||||
"extends": [
|
"extends": [
|
||||||
"@commitlint/config-conventional"
|
"@commitlint/config-conventional"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,ts,tsx}": [
|
||||||
|
"eslint --fix"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"importSort": {
|
||||||
|
".js, .jsx, .ts, .tsx": {
|
||||||
|
"style": "module",
|
||||||
|
"parser": "typescript"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tsDevTools": {
|
"tsDevTools": {
|
||||||
"version": "20260604100000-migrate-to-vitest"
|
"version": "20250623095600-remove-prettier-oxc"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,38 +1,38 @@
|
|||||||
import { describe, expect, it, beforeEach, vi } from "vitest";
|
import { jest, describe, it, expect, beforeEach } from "@jest/globals";
|
||||||
|
|
||||||
// Mock @actions/core
|
// Mock @actions/core
|
||||||
const setFailedMock = vi.fn();
|
const setFailedMock = jest.fn();
|
||||||
|
|
||||||
vi.doMock("@actions/core", () => ({
|
jest.unstable_mockModule("@actions/core", () => ({
|
||||||
setFailed: setFailedMock,
|
setFailed: setFailedMock,
|
||||||
getInput: vi.fn().mockReturnValue(""),
|
getInput: jest.fn().mockReturnValue(""),
|
||||||
getMultilineInput: vi.fn().mockReturnValue([]),
|
getMultilineInput: jest.fn().mockReturnValue([]),
|
||||||
debug: vi.fn(),
|
debug: jest.fn(),
|
||||||
info: vi.fn(),
|
info: jest.fn(),
|
||||||
warning: vi.fn(),
|
warning: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock docker-compose
|
// Mock docker-compose
|
||||||
vi.doMock("docker-compose", () => ({
|
jest.unstable_mockModule("docker-compose", () => ({
|
||||||
upAll: vi.fn(),
|
upAll: jest.fn(),
|
||||||
upMany: vi.fn(),
|
upMany: jest.fn(),
|
||||||
down: vi.fn(),
|
down: jest.fn(),
|
||||||
logs: vi.fn(),
|
logs: jest.fn(),
|
||||||
version: vi
|
version: jest
|
||||||
.fn<() => Promise<{ data: { version: string } }>>()
|
.fn<() => Promise<{ data: { version: string } }>>()
|
||||||
.mockResolvedValue({ data: { version: "1.2.3" } }),
|
.mockResolvedValue({ data: { version: "1.2.3" } }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock node:fs
|
// Mock node:fs
|
||||||
vi.doMock("node:fs", async () => {
|
jest.unstable_mockModule("node:fs", async () => {
|
||||||
const actualFs = await vi.importActual<typeof import("node:fs")>("node:fs");
|
const actualFs = await jest.requireActual<typeof import("node:fs")>("node:fs");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...actualFs,
|
...actualFs,
|
||||||
existsSync: vi.fn().mockReturnValue(true),
|
existsSync: jest.fn().mockReturnValue(true),
|
||||||
default: {
|
default: {
|
||||||
...actualFs,
|
...actualFs,
|
||||||
existsSync: vi.fn().mockReturnValue(true),
|
existsSync: jest.fn().mockReturnValue(true),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -40,35 +40,26 @@ vi.doMock("node:fs", async () => {
|
|||||||
// Dynamic imports after mock setup
|
// Dynamic imports after mock setup
|
||||||
const { run } = await import("./index-runner.js");
|
const { run } = await import("./index-runner.js");
|
||||||
const { InputService } = await import("./services/input.service.js");
|
const { InputService } = await import("./services/input.service.js");
|
||||||
const { LoggerService, LogLevel } = await import(
|
const { LoggerService, LogLevel } = await import("./services/logger.service.js");
|
||||||
"./services/logger.service.js"
|
const { DockerComposeInstallerService } =
|
||||||
);
|
await import("./services/docker-compose-installer.service.js");
|
||||||
const { DockerComposeInstallerService } = await import(
|
const { DockerComposeService } = await import("./services/docker-compose.service.js");
|
||||||
"./services/docker-compose-installer.service.js"
|
|
||||||
);
|
|
||||||
const { DockerComposeService } = await import(
|
|
||||||
"./services/docker-compose.service.js"
|
|
||||||
);
|
|
||||||
|
|
||||||
describe("run", () => {
|
describe("run", () => {
|
||||||
let infoMock: ReturnType<typeof vi.spyOn>;
|
let infoMock: jest.SpiedFunction<typeof LoggerService.prototype.info>;
|
||||||
let debugMock: ReturnType<typeof vi.spyOn>;
|
let debugMock: jest.SpiedFunction<typeof LoggerService.prototype.debug>;
|
||||||
let getInputsMock: ReturnType<typeof vi.spyOn>;
|
let getInputsMock: jest.SpiedFunction<typeof InputService.prototype.getInputs>;
|
||||||
let installMock: ReturnType<typeof vi.spyOn>;
|
let installMock: jest.SpiedFunction<typeof DockerComposeInstallerService.prototype.install>;
|
||||||
let upMock: ReturnType<typeof vi.spyOn>;
|
let upMock: jest.SpiedFunction<typeof DockerComposeService.prototype.up>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
|
||||||
infoMock = vi
|
infoMock = jest.spyOn(LoggerService.prototype, "info").mockImplementation(() => {});
|
||||||
.spyOn(LoggerService.prototype, "info")
|
debugMock = jest.spyOn(LoggerService.prototype, "debug").mockImplementation(() => {});
|
||||||
.mockImplementation(() => {});
|
getInputsMock = jest.spyOn(InputService.prototype, "getInputs");
|
||||||
debugMock = vi
|
installMock = jest.spyOn(DockerComposeInstallerService.prototype, "install");
|
||||||
.spyOn(LoggerService.prototype, "debug")
|
upMock = jest.spyOn(DockerComposeService.prototype, "up");
|
||||||
.mockImplementation(() => {});
|
|
||||||
getInputsMock = vi.spyOn(InputService.prototype, "getInputs");
|
|
||||||
installMock = vi.spyOn(DockerComposeInstallerService.prototype, "install");
|
|
||||||
upMock = vi.spyOn(DockerComposeService.prototype, "up");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should install docker compose with specified version", async () => {
|
it("should install docker compose with specified version", async () => {
|
||||||
@ -94,12 +85,10 @@ describe("run", () => {
|
|||||||
await run();
|
await run();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(infoMock).toHaveBeenCalledWith(
|
expect(infoMock).toHaveBeenCalledWith("Setting up docker compose version 1.29.2");
|
||||||
"Setting up docker compose version 1.29.2",
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(debugMock).toHaveBeenCalledWith(
|
expect(debugMock).toHaveBeenCalledWith(
|
||||||
'inputs: {"dockerFlags":[],"composeFiles":["docker-compose.yml"],"services":[],"composeFlags":[],"upFlags":[],"downFlags":[],"cwd":"/current/working/dir","composeVersion":"1.29.2","githubToken":null,"serviceLogLevel":"debug"}',
|
'inputs: {"dockerFlags":[],"composeFiles":["docker-compose.yml"],"services":[],"composeFlags":[],"upFlags":[],"downFlags":[],"cwd":"/current/working/dir","composeVersion":"1.29.2","githubToken":null,"serviceLogLevel":"debug"}'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(installMock).toHaveBeenCalledWith({
|
expect(installMock).toHaveBeenCalledWith({
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export async function run(): Promise<void> {
|
|||||||
const loggerService = new LoggerService();
|
const loggerService = new LoggerService();
|
||||||
const inputService = new InputService();
|
const inputService = new InputService();
|
||||||
const dockerComposeInstallerService = new DockerComposeInstallerService(
|
const dockerComposeInstallerService = new DockerComposeInstallerService(
|
||||||
new ManualInstallerAdapter(),
|
new ManualInstallerAdapter()
|
||||||
);
|
);
|
||||||
const dockerComposeService = new DockerComposeService();
|
const dockerComposeService = new DockerComposeService();
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ export async function run(): Promise<void> {
|
|||||||
|
|
||||||
loggerService.info(
|
loggerService.info(
|
||||||
"Setting up docker compose" +
|
"Setting up docker compose" +
|
||||||
(inputs.composeVersion ? ` version ${inputs.composeVersion}` : ""),
|
(inputs.composeVersion ? ` version ${inputs.composeVersion}` : "")
|
||||||
);
|
);
|
||||||
|
|
||||||
const installedVersion = await dockerComposeInstallerService.install({
|
const installedVersion = await dockerComposeInstallerService.install({
|
||||||
|
|||||||
@ -1,73 +1,64 @@
|
|||||||
import { describe, expect, it, beforeEach, vi } from "vitest";
|
import { jest, describe, it, expect, beforeEach } from "@jest/globals";
|
||||||
|
|
||||||
// Mock @actions/core
|
// Mock @actions/core
|
||||||
const setFailedMock = vi.fn();
|
const setFailedMock = jest.fn();
|
||||||
|
|
||||||
vi.doMock("@actions/core", () => ({
|
jest.unstable_mockModule("@actions/core", () => ({
|
||||||
setFailed: setFailedMock,
|
setFailed: setFailedMock,
|
||||||
getInput: vi.fn().mockReturnValue(""),
|
getInput: jest.fn().mockReturnValue(""),
|
||||||
getMultilineInput: vi.fn().mockReturnValue([]),
|
getMultilineInput: jest.fn().mockReturnValue([]),
|
||||||
debug: vi.fn(),
|
debug: jest.fn(),
|
||||||
info: vi.fn(),
|
info: jest.fn(),
|
||||||
warning: vi.fn(),
|
warning: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock docker-compose
|
// Mock docker-compose
|
||||||
vi.doMock("docker-compose", () => ({
|
jest.unstable_mockModule("docker-compose", () => ({
|
||||||
upAll: vi.fn(),
|
upAll: jest.fn(),
|
||||||
upMany: vi.fn(),
|
upMany: jest.fn(),
|
||||||
down: vi.fn(),
|
down: jest.fn(),
|
||||||
logs: vi.fn(),
|
logs: jest.fn(),
|
||||||
version: vi
|
version: jest
|
||||||
.fn<() => Promise<{ data: { version: string } }>>()
|
.fn<() => Promise<{ data: { version: string } }>>()
|
||||||
.mockResolvedValue({ data: { version: "1.2.3" } }),
|
.mockResolvedValue({ data: { version: "1.2.3" } }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock node:fs
|
// Mock node:fs
|
||||||
vi.doMock("node:fs", async () => {
|
jest.unstable_mockModule("node:fs", async () => {
|
||||||
const actualFs = await vi.importActual<typeof import("node:fs")>("node:fs");
|
const actualFs = await jest.requireActual<typeof import("node:fs")>("node:fs");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...actualFs,
|
...actualFs,
|
||||||
existsSync: vi.fn().mockReturnValue(true),
|
existsSync: jest.fn().mockReturnValue(true),
|
||||||
default: {
|
default: {
|
||||||
...actualFs,
|
...actualFs,
|
||||||
existsSync: vi.fn().mockReturnValue(true),
|
existsSync: jest.fn().mockReturnValue(true),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Dynamic imports after mock setup
|
// Dynamic imports after mock setup
|
||||||
const { InputService } = await import("./services/input.service.js");
|
const { InputService } = await import("./services/input.service.js");
|
||||||
const { LoggerService, LogLevel } = await import(
|
const { LoggerService, LogLevel } = await import("./services/logger.service.js");
|
||||||
"./services/logger.service.js"
|
const { DockerComposeInstallerService } =
|
||||||
);
|
await import("./services/docker-compose-installer.service.js");
|
||||||
const { DockerComposeInstallerService } = await import(
|
const { DockerComposeService } = await import("./services/docker-compose.service.js");
|
||||||
"./services/docker-compose-installer.service.js"
|
|
||||||
);
|
|
||||||
const { DockerComposeService } = await import(
|
|
||||||
"./services/docker-compose.service.js"
|
|
||||||
);
|
|
||||||
|
|
||||||
let getInputsMock: ReturnType<typeof vi.spyOn>;
|
let getInputsMock: jest.SpiedFunction<typeof InputService.prototype.getInputs>;
|
||||||
let debugMock: ReturnType<typeof vi.spyOn>;
|
let debugMock: jest.SpiedFunction<typeof LoggerService.prototype.debug>;
|
||||||
let infoMock: ReturnType<typeof vi.spyOn>;
|
let infoMock: jest.SpiedFunction<typeof LoggerService.prototype.info>;
|
||||||
let installMock: ReturnType<typeof vi.spyOn>;
|
let installMock: jest.SpiedFunction<typeof DockerComposeInstallerService.prototype.install>;
|
||||||
let upMock: ReturnType<typeof vi.spyOn>;
|
let upMock: jest.SpiedFunction<typeof DockerComposeService.prototype.up>;
|
||||||
|
|
||||||
describe("index", () => {
|
describe("index", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
|
||||||
infoMock = vi
|
infoMock = jest.spyOn(LoggerService.prototype, "info").mockImplementation(() => {});
|
||||||
.spyOn(LoggerService.prototype, "info")
|
debugMock = jest.spyOn(LoggerService.prototype, "debug").mockImplementation(() => {});
|
||||||
.mockImplementation(() => {});
|
getInputsMock = jest.spyOn(InputService.prototype, "getInputs");
|
||||||
debugMock = vi
|
installMock = jest.spyOn(DockerComposeInstallerService.prototype, "install");
|
||||||
.spyOn(LoggerService.prototype, "debug")
|
upMock = jest.spyOn(DockerComposeService.prototype, "up");
|
||||||
.mockImplementation(() => {});
|
|
||||||
getInputsMock = vi.spyOn(InputService.prototype, "getInputs");
|
|
||||||
installMock = vi.spyOn(DockerComposeInstallerService.prototype, "install");
|
|
||||||
upMock = vi.spyOn(DockerComposeService.prototype, "up");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("calls run when imported", async () => {
|
it("calls run when imported", async () => {
|
||||||
@ -91,21 +82,15 @@ describe("index", () => {
|
|||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
|
||||||
expect(infoMock).toHaveBeenNthCalledWith(1, "Setting up docker compose");
|
expect(infoMock).toHaveBeenNthCalledWith(1, "Setting up docker compose");
|
||||||
expect(infoMock).toHaveBeenNthCalledWith(
|
expect(infoMock).toHaveBeenNthCalledWith(2, "docker compose version: 1.2.3");
|
||||||
2,
|
|
||||||
"docker compose version: 1.2.3",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify that all of the functions were called correctly
|
// Verify that all of the functions were called correctly
|
||||||
expect(debugMock).toHaveBeenNthCalledWith(
|
expect(debugMock).toHaveBeenNthCalledWith(
|
||||||
1,
|
1,
|
||||||
'inputs: {"dockerFlags":[],"composeFiles":["docker-compose.yml"],"services":[],"composeFlags":[],"upFlags":[],"downFlags":[],"cwd":"/current/working/dir","composeVersion":null,"githubToken":null,"serviceLogLevel":"debug"}',
|
'inputs: {"dockerFlags":[],"composeFiles":["docker-compose.yml"],"services":[],"composeFlags":[],"upFlags":[],"downFlags":[],"cwd":"/current/working/dir","composeVersion":null,"githubToken":null,"serviceLogLevel":"debug"}'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(infoMock).toHaveBeenNthCalledWith(
|
expect(infoMock).toHaveBeenNthCalledWith(3, "Bringing up docker compose service(s)");
|
||||||
3,
|
|
||||||
"Bringing up docker compose service(s)",
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(upMock).toHaveBeenCalledWith({
|
expect(upMock).toHaveBeenCalledWith({
|
||||||
dockerFlags: [],
|
dockerFlags: [],
|
||||||
@ -119,9 +104,6 @@ describe("index", () => {
|
|||||||
|
|
||||||
expect(setFailedMock).not.toHaveBeenCalled();
|
expect(setFailedMock).not.toHaveBeenCalled();
|
||||||
|
|
||||||
expect(infoMock).toHaveBeenNthCalledWith(
|
expect(infoMock).toHaveBeenNthCalledWith(4, "docker compose service(s) are up");
|
||||||
4,
|
|
||||||
"docker compose service(s) are up",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,63 +1,55 @@
|
|||||||
import { describe, expect, it, beforeEach, vi } from "vitest";
|
import { jest, describe, it, expect, beforeEach } from "@jest/globals";
|
||||||
|
|
||||||
// Mock @actions/core
|
// Mock @actions/core
|
||||||
const setFailedMock = vi.fn();
|
const setFailedMock = jest.fn();
|
||||||
|
|
||||||
vi.doMock("@actions/core", () => ({
|
jest.unstable_mockModule("@actions/core", () => ({
|
||||||
setFailed: setFailedMock,
|
setFailed: setFailedMock,
|
||||||
getInput: vi.fn().mockReturnValue(""),
|
getInput: jest.fn().mockReturnValue(""),
|
||||||
getMultilineInput: vi.fn().mockReturnValue([]),
|
getMultilineInput: jest.fn().mockReturnValue([]),
|
||||||
debug: vi.fn(),
|
debug: jest.fn(),
|
||||||
info: vi.fn(),
|
info: jest.fn(),
|
||||||
warning: vi.fn(),
|
warning: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock docker-compose
|
// Mock docker-compose
|
||||||
const logsMock = vi.fn();
|
const logsMock = jest.fn();
|
||||||
const downMock = vi.fn();
|
const downMock = jest.fn();
|
||||||
|
|
||||||
vi.doMock("docker-compose", () => ({
|
jest.unstable_mockModule("docker-compose", () => ({
|
||||||
logs: logsMock,
|
logs: logsMock,
|
||||||
down: downMock,
|
down: downMock,
|
||||||
upAll: vi.fn(),
|
upAll: jest.fn(),
|
||||||
upMany: vi.fn(),
|
upMany: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock node:fs
|
// Mock node:fs
|
||||||
vi.doMock("node:fs", () => ({
|
jest.unstable_mockModule("node:fs", () => ({
|
||||||
existsSync: vi.fn().mockReturnValue(true),
|
existsSync: jest.fn().mockReturnValue(true),
|
||||||
default: { existsSync: vi.fn().mockReturnValue(true) },
|
default: { existsSync: jest.fn().mockReturnValue(true) },
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Dynamic imports after mock setup
|
// Dynamic imports after mock setup
|
||||||
const { run } = await import("./post-runner.js");
|
const { run } = await import("./post-runner.js");
|
||||||
const { InputService } = await import("./services/input.service.js");
|
const { InputService } = await import("./services/input.service.js");
|
||||||
const { LoggerService, LogLevel } = await import(
|
const { LoggerService, LogLevel } = await import("./services/logger.service.js");
|
||||||
"./services/logger.service.js"
|
const { DockerComposeService } = await import("./services/docker-compose.service.js");
|
||||||
);
|
|
||||||
const { DockerComposeService } = await import(
|
|
||||||
"./services/docker-compose.service.js"
|
|
||||||
);
|
|
||||||
|
|
||||||
describe("run", () => {
|
describe("run", () => {
|
||||||
let infoMock: ReturnType<typeof vi.spyOn>;
|
let infoMock: jest.SpiedFunction<typeof LoggerService.prototype.info>;
|
||||||
let debugMock: ReturnType<typeof vi.spyOn>;
|
let debugMock: jest.SpiedFunction<typeof LoggerService.prototype.debug>;
|
||||||
let getInputsMock: ReturnType<typeof vi.spyOn>;
|
let getInputsMock: jest.SpiedFunction<typeof InputService.prototype.getInputs>;
|
||||||
let serviceDownMock: ReturnType<typeof vi.spyOn>;
|
let serviceDownMock: jest.SpiedFunction<typeof DockerComposeService.prototype.down>;
|
||||||
let serviceLogsMock: ReturnType<typeof vi.spyOn>;
|
let serviceLogsMock: jest.SpiedFunction<typeof DockerComposeService.prototype.logs>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
|
||||||
infoMock = vi
|
infoMock = jest.spyOn(LoggerService.prototype, "info").mockImplementation(() => {});
|
||||||
.spyOn(LoggerService.prototype, "info")
|
debugMock = jest.spyOn(LoggerService.prototype, "debug").mockImplementation(() => {});
|
||||||
.mockImplementation(() => {});
|
getInputsMock = jest.spyOn(InputService.prototype, "getInputs");
|
||||||
debugMock = vi
|
serviceDownMock = jest.spyOn(DockerComposeService.prototype, "down");
|
||||||
.spyOn(LoggerService.prototype, "debug")
|
serviceLogsMock = jest.spyOn(DockerComposeService.prototype, "logs");
|
||||||
.mockImplementation(() => {});
|
|
||||||
getInputsMock = vi.spyOn(InputService.prototype, "getInputs");
|
|
||||||
serviceDownMock = vi.spyOn(DockerComposeService.prototype, "down");
|
|
||||||
serviceLogsMock = vi.spyOn(DockerComposeService.prototype, "logs");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should bring down docker compose service(s) and log output", async () => {
|
it("should bring down docker compose service(s) and log output", async () => {
|
||||||
@ -131,12 +123,8 @@ describe("run", () => {
|
|||||||
await run();
|
await run();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(debugMock).toHaveBeenCalledWith(
|
expect(debugMock).toHaveBeenCalledWith("docker compose error:\ntest logs error");
|
||||||
"docker compose error:\ntest logs error",
|
expect(debugMock).toHaveBeenCalledWith("docker compose logs:\ntest logs output");
|
||||||
);
|
|
||||||
expect(debugMock).toHaveBeenCalledWith(
|
|
||||||
"docker compose logs:\ntest logs output",
|
|
||||||
);
|
|
||||||
expect(infoMock).toHaveBeenCalledWith("docker compose is down");
|
expect(infoMock).toHaveBeenCalledWith("docker compose is down");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,62 +1,54 @@
|
|||||||
import { describe, expect, it, beforeEach, vi } from "vitest";
|
import { jest, describe, it, expect, beforeEach } from "@jest/globals";
|
||||||
|
|
||||||
// Mock @actions/core
|
// Mock @actions/core
|
||||||
const setFailedMock = vi.fn();
|
const setFailedMock = jest.fn();
|
||||||
|
|
||||||
vi.doMock("@actions/core", () => ({
|
jest.unstable_mockModule("@actions/core", () => ({
|
||||||
setFailed: setFailedMock,
|
setFailed: setFailedMock,
|
||||||
getInput: vi.fn().mockReturnValue(""),
|
getInput: jest.fn().mockReturnValue(""),
|
||||||
getMultilineInput: vi.fn().mockReturnValue([]),
|
getMultilineInput: jest.fn().mockReturnValue([]),
|
||||||
debug: vi.fn(),
|
debug: jest.fn(),
|
||||||
info: vi.fn(),
|
info: jest.fn(),
|
||||||
warning: vi.fn(),
|
warning: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock docker-compose
|
// Mock docker-compose
|
||||||
const logsMock = vi.fn();
|
const logsMock = jest.fn();
|
||||||
const downMock = vi.fn();
|
const downMock = jest.fn();
|
||||||
|
|
||||||
vi.doMock("docker-compose", () => ({
|
jest.unstable_mockModule("docker-compose", () => ({
|
||||||
logs: logsMock,
|
logs: logsMock,
|
||||||
down: downMock,
|
down: downMock,
|
||||||
upAll: vi.fn(),
|
upAll: jest.fn(),
|
||||||
upMany: vi.fn(),
|
upMany: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock node:fs
|
// Mock node:fs
|
||||||
vi.doMock("node:fs", () => ({
|
jest.unstable_mockModule("node:fs", () => ({
|
||||||
existsSync: vi.fn().mockReturnValue(true),
|
existsSync: jest.fn().mockReturnValue(true),
|
||||||
default: { existsSync: vi.fn().mockReturnValue(true) },
|
default: { existsSync: jest.fn().mockReturnValue(true) },
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Dynamic imports after mock setup
|
// Dynamic imports after mock setup
|
||||||
const { InputService } = await import("./services/input.service.js");
|
const { InputService } = await import("./services/input.service.js");
|
||||||
const { LoggerService, LogLevel } = await import(
|
const { LoggerService, LogLevel } = await import("./services/logger.service.js");
|
||||||
"./services/logger.service.js"
|
const { DockerComposeService } = await import("./services/docker-compose.service.js");
|
||||||
);
|
|
||||||
const { DockerComposeService } = await import(
|
|
||||||
"./services/docker-compose.service.js"
|
|
||||||
);
|
|
||||||
|
|
||||||
let getInputsMock: ReturnType<typeof vi.spyOn>;
|
let getInputsMock: jest.SpiedFunction<typeof InputService.prototype.getInputs>;
|
||||||
let debugMock: ReturnType<typeof vi.spyOn>;
|
let debugMock: jest.SpiedFunction<typeof LoggerService.prototype.debug>;
|
||||||
let infoMock: ReturnType<typeof vi.spyOn>;
|
let infoMock: jest.SpiedFunction<typeof LoggerService.prototype.info>;
|
||||||
let serviceLogsMock: ReturnType<typeof vi.spyOn>;
|
let serviceLogsMock: jest.SpiedFunction<typeof DockerComposeService.prototype.logs>;
|
||||||
let serviceDownMock: ReturnType<typeof vi.spyOn>;
|
let serviceDownMock: jest.SpiedFunction<typeof DockerComposeService.prototype.down>;
|
||||||
|
|
||||||
describe("post", () => {
|
describe("post", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
|
||||||
infoMock = vi
|
infoMock = jest.spyOn(LoggerService.prototype, "info").mockImplementation(() => {});
|
||||||
.spyOn(LoggerService.prototype, "info")
|
debugMock = jest.spyOn(LoggerService.prototype, "debug").mockImplementation(() => {});
|
||||||
.mockImplementation(() => {});
|
getInputsMock = jest.spyOn(InputService.prototype, "getInputs");
|
||||||
debugMock = vi
|
serviceLogsMock = jest.spyOn(DockerComposeService.prototype, "logs");
|
||||||
.spyOn(LoggerService.prototype, "debug")
|
serviceDownMock = jest.spyOn(DockerComposeService.prototype, "down");
|
||||||
.mockImplementation(() => {});
|
|
||||||
getInputsMock = vi.spyOn(InputService.prototype, "getInputs");
|
|
||||||
serviceLogsMock = vi.spyOn(DockerComposeService.prototype, "logs");
|
|
||||||
serviceDownMock = vi.spyOn(DockerComposeService.prototype, "down");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("calls run when imported", async () => {
|
it("calls run when imported", async () => {
|
||||||
@ -97,10 +89,7 @@ describe("post", () => {
|
|||||||
serviceLogger: debugMock,
|
serviceLogger: debugMock,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(debugMock).toHaveBeenNthCalledWith(
|
expect(debugMock).toHaveBeenNthCalledWith(1, "docker compose logs:\ntest logs");
|
||||||
1,
|
|
||||||
"docker compose logs:\ntest logs",
|
|
||||||
);
|
|
||||||
expect(infoMock).toHaveBeenNthCalledWith(1, "docker compose is down");
|
expect(infoMock).toHaveBeenNthCalledWith(1, "docker compose is down");
|
||||||
|
|
||||||
expect(setFailedMock).not.toHaveBeenCalled();
|
expect(setFailedMock).not.toHaveBeenCalled();
|
||||||
|
|||||||
@ -1,24 +1,21 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
import { jest, describe, it, expect, beforeEach, afterEach } from "@jest/globals";
|
||||||
import type { IDockerComposeResult } from "docker-compose";
|
import type { IDockerComposeResult } from "docker-compose";
|
||||||
import { MockAgent, setGlobalDispatcher } from "undici";
|
import { MockAgent, setGlobalDispatcher } from "undici";
|
||||||
|
|
||||||
// Mock docker-compose before importing the module under test
|
// Mock docker-compose before importing the module under test
|
||||||
const versionMock =
|
const versionMock = jest.fn<() => Promise<IDockerComposeResult & { data: { version: string } }>>();
|
||||||
vi.fn<() => Promise<IDockerComposeResult & { data: { version: string } }>>();
|
|
||||||
|
|
||||||
vi.doMock("docker-compose", () => ({
|
jest.unstable_mockModule("docker-compose", () => ({
|
||||||
version: versionMock,
|
version: versionMock,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Create manual installer adapter mock
|
// Create manual installer adapter mock
|
||||||
const manualInstallerAdapterMock = {
|
const manualInstallerAdapterMock = {
|
||||||
install: vi.fn<(version: string) => Promise<void>>(),
|
install: jest.fn<(version: string) => Promise<void>>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Dynamic import after mock setup
|
// Dynamic import after mock setup
|
||||||
const { DockerComposeInstallerService } = await import(
|
const { DockerComposeInstallerService } = await import("./docker-compose-installer.service.js");
|
||||||
"./docker-compose-installer.service.js"
|
|
||||||
);
|
|
||||||
|
|
||||||
describe("DockerComposeInstallerService", () => {
|
describe("DockerComposeInstallerService", () => {
|
||||||
let mockAgent: MockAgent;
|
let mockAgent: MockAgent;
|
||||||
@ -33,10 +30,7 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const installCompose = (
|
const installCompose = (composeVersion: string | null, githubToken: string | null) =>
|
||||||
composeVersion: string | null,
|
|
||||||
githubToken: string | null,
|
|
||||||
) =>
|
|
||||||
service.install({
|
service.install({
|
||||||
composeVersion,
|
composeVersion,
|
||||||
cwd: "/path/to/cwd",
|
cwd: "/path/to/cwd",
|
||||||
@ -65,26 +59,24 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
setGlobalDispatcher(mockClient);
|
setGlobalDispatcher(mockClient);
|
||||||
Object.defineProperty(globalThis, "fetch", {
|
Object.defineProperty(globalThis, "fetch", {
|
||||||
value: vi.fn(),
|
value: jest.fn(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
mockAgent = new MockAgent();
|
mockAgent = new MockAgent();
|
||||||
mockAgent.disableNetConnect();
|
mockAgent.disableNetConnect();
|
||||||
|
|
||||||
service = new DockerComposeInstallerService(
|
service = new DockerComposeInstallerService(manualInstallerAdapterMock as never);
|
||||||
manualInstallerAdapterMock as never,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("install", () => {
|
describe("install", () => {
|
||||||
@ -102,9 +94,7 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toBe(latestVersion);
|
expect(result).toBe(latestVersion);
|
||||||
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(
|
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(latestVersion);
|
||||||
latestVersion,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return current version when no version is provided", async () => {
|
it("should return current version when no version is provided", async () => {
|
||||||
@ -136,9 +126,7 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
versionMock.mockResolvedValueOnce(composeVersionResponse("1.2.3"));
|
versionMock.mockResolvedValueOnce(composeVersionResponse("1.2.3"));
|
||||||
|
|
||||||
const expectedVersion = "1.3.0";
|
const expectedVersion = "1.3.0";
|
||||||
versionMock.mockResolvedValueOnce(
|
versionMock.mockResolvedValueOnce(composeVersionResponse(expectedVersion));
|
||||||
composeVersionResponse(expectedVersion),
|
|
||||||
);
|
|
||||||
setPlatform("linux");
|
setPlatform("linux");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@ -146,9 +134,7 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toBe(expectedVersion);
|
expect(result).toBe(expectedVersion);
|
||||||
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(
|
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(expectedVersion);
|
||||||
expectedVersion,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should install the latest version if requested", async () => {
|
it("should install the latest version if requested", async () => {
|
||||||
@ -165,9 +151,7 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toBe(latestVersion);
|
expect(result).toBe(latestVersion);
|
||||||
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(
|
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(latestVersion);
|
||||||
latestVersion,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw an error if the latest version if requested and no Github token is provided", async () => {
|
it("should throw an error if the latest version if requested and no Github token is provided", async () => {
|
||||||
@ -176,7 +160,7 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
await expect(installCompose("latest", null)).rejects.toThrow(
|
await expect(installCompose("latest", null)).rejects.toThrow(
|
||||||
"GitHub token is required to install the latest version",
|
"GitHub token is required to install the latest version"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -185,14 +169,12 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
versionMock.mockResolvedValueOnce(composeVersionResponse("1.2.3"));
|
versionMock.mockResolvedValueOnce(composeVersionResponse("1.2.3"));
|
||||||
|
|
||||||
const expectedVersion = "1.3.0";
|
const expectedVersion = "1.3.0";
|
||||||
versionMock.mockResolvedValueOnce(
|
versionMock.mockResolvedValueOnce(composeVersionResponse(expectedVersion));
|
||||||
composeVersionResponse(expectedVersion),
|
|
||||||
);
|
|
||||||
setPlatform("win32");
|
setPlatform("win32");
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
await expect(installCompose(expectedVersion, null)).rejects.toThrow(
|
await expect(installCompose(expectedVersion, null)).rejects.toThrow(
|
||||||
`Unsupported platform: win32`,
|
`Unsupported platform: win32`
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(manualInstallerAdapterMock.install).not.toHaveBeenCalled();
|
expect(manualInstallerAdapterMock.install).not.toHaveBeenCalled();
|
||||||
@ -205,9 +187,7 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
const installedVersion = "2.0.0";
|
const installedVersion = "2.0.0";
|
||||||
|
|
||||||
// After installation, version() returns the new version
|
// After installation, version() returns the new version
|
||||||
versionMock.mockResolvedValueOnce(
|
versionMock.mockResolvedValueOnce(composeVersionResponse(installedVersion));
|
||||||
composeVersionResponse(installedVersion),
|
|
||||||
);
|
|
||||||
setPlatform("linux");
|
setPlatform("linux");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@ -215,9 +195,7 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toBe(installedVersion);
|
expect(result).toBe(installedVersion);
|
||||||
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(
|
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(installedVersion);
|
||||||
installedVersion,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should install latest version when missing or unspecified", async () => {
|
it("should install latest version when missing or unspecified", async () => {
|
||||||
@ -236,9 +214,7 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toBe(latestVersion);
|
expect(result).toBe(latestVersion);
|
||||||
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(
|
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(latestVersion);
|
||||||
latestVersion,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw if Compose is missing and no GitHub token is provided", async () => {
|
it("should throw if Compose is missing and no GitHub token is provided", async () => {
|
||||||
@ -247,7 +223,7 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
setPlatform("linux");
|
setPlatform("linux");
|
||||||
|
|
||||||
await expect(installCompose("latest", null)).rejects.toThrow(
|
await expect(installCompose("latest", null)).rejects.toThrow(
|
||||||
"GitHub token is required to install the latest version",
|
"GitHub token is required to install the latest version"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -273,11 +249,9 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
await expect(installCompose(targetVersion, "token")).rejects.toThrow(
|
await expect(installCompose(targetVersion, "token")).rejects.toThrow(
|
||||||
`Failed to install Docker Compose version "${targetVersion}", installed version is "1.3.0"`,
|
`Failed to install Docker Compose version "${targetVersion}", installed version is "1.3.0"`
|
||||||
);
|
|
||||||
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(
|
|
||||||
targetVersion,
|
|
||||||
);
|
);
|
||||||
|
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(targetVersion);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw with unknown installed version when post-install version check fails", async () => {
|
it("should throw with unknown installed version when post-install version check fails", async () => {
|
||||||
@ -285,18 +259,14 @@ describe("DockerComposeInstallerService", () => {
|
|||||||
versionMock.mockResolvedValueOnce(composeVersionResponse("1.2.3"));
|
versionMock.mockResolvedValueOnce(composeVersionResponse("1.2.3"));
|
||||||
|
|
||||||
const targetVersion = "v1.4.0";
|
const targetVersion = "v1.4.0";
|
||||||
versionMock.mockRejectedValueOnce(
|
versionMock.mockRejectedValueOnce(new Error("version check failed after install"));
|
||||||
new Error("version check failed after install"),
|
|
||||||
);
|
|
||||||
setPlatform("linux");
|
setPlatform("linux");
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
await expect(installCompose(targetVersion, "token")).rejects.toThrow(
|
await expect(installCompose(targetVersion, "token")).rejects.toThrow(
|
||||||
`Failed to install Docker Compose version "${targetVersion}", installed version is "unknown"`,
|
`Failed to install Docker Compose version "${targetVersion}", installed version is "unknown"`
|
||||||
);
|
|
||||||
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(
|
|
||||||
targetVersion,
|
|
||||||
);
|
);
|
||||||
|
expect(manualInstallerAdapterMock.install).toHaveBeenCalledWith(targetVersion);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import * as github from "@actions/github";
|
import * as github from "@actions/github";
|
||||||
import { version } from "docker-compose";
|
import { version } from "docker-compose";
|
||||||
import { COMPOSE_VERSION_LATEST, type Inputs } from "./input.service.js";
|
import { COMPOSE_VERSION_LATEST, Inputs } from "./input.service.js";
|
||||||
import type { ManualInstallerAdapter } from "./installer-adapter/manual-installer-adapter.js";
|
import { ManualInstallerAdapter } from "./installer-adapter/manual-installer-adapter.js";
|
||||||
|
|
||||||
export type InstallInputs = {
|
export type InstallInputs = {
|
||||||
composeVersion: Inputs["composeVersion"];
|
composeVersion: Inputs["composeVersion"];
|
||||||
@ -14,28 +14,19 @@ export type VersionInputs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class DockerComposeInstallerService {
|
export class DockerComposeInstallerService {
|
||||||
constructor(
|
constructor(private readonly manualInstallerAdapter: ManualInstallerAdapter) {}
|
||||||
private readonly manualInstallerAdapter: ManualInstallerAdapter,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async install({
|
async install({ composeVersion, cwd, githubToken }: InstallInputs): Promise<string> {
|
||||||
composeVersion,
|
|
||||||
cwd,
|
|
||||||
githubToken,
|
|
||||||
}: InstallInputs): Promise<string> {
|
|
||||||
const currentVersion = await this.version({ cwd });
|
const currentVersion = await this.version({ cwd });
|
||||||
|
|
||||||
const normalizedCurrentVersion = currentVersion
|
const normalizedCurrentVersion = currentVersion ? this.normalizeVersion(currentVersion) : null;
|
||||||
? this.normalizeVersion(currentVersion)
|
|
||||||
: null;
|
|
||||||
const normalizedRequestedVersion = composeVersion
|
const normalizedRequestedVersion = composeVersion
|
||||||
? this.normalizeVersion(composeVersion)
|
? this.normalizeVersion(composeVersion)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const needsInstall =
|
const needsInstall =
|
||||||
!currentVersion ||
|
!currentVersion ||
|
||||||
(composeVersion &&
|
(composeVersion && normalizedRequestedVersion !== normalizedCurrentVersion);
|
||||||
normalizedRequestedVersion !== normalizedCurrentVersion);
|
|
||||||
if (!needsInstall) {
|
if (!needsInstall) {
|
||||||
return currentVersion;
|
return currentVersion;
|
||||||
}
|
}
|
||||||
@ -44,9 +35,7 @@ export class DockerComposeInstallerService {
|
|||||||
|
|
||||||
if (targetVersion === COMPOSE_VERSION_LATEST) {
|
if (targetVersion === COMPOSE_VERSION_LATEST) {
|
||||||
if (!githubToken) {
|
if (!githubToken) {
|
||||||
throw new Error(
|
throw new Error("GitHub token is required to install the latest version");
|
||||||
"GitHub token is required to install the latest version",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
targetVersion = await this.getLatestVersion(githubToken);
|
targetVersion = await this.getLatestVersion(githubToken);
|
||||||
}
|
}
|
||||||
@ -57,11 +46,10 @@ export class DockerComposeInstallerService {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
!installedVersion ||
|
!installedVersion ||
|
||||||
this.normalizeVersion(installedVersion) !==
|
this.normalizeVersion(installedVersion) !== this.normalizeVersion(targetVersion)
|
||||||
this.normalizeVersion(targetVersion)
|
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to install Docker Compose version "${targetVersion}", installed version is "${installedVersion ?? "unknown"}"`,
|
`Failed to install Docker Compose version "${targetVersion}", installed version is "${installedVersion ?? "unknown"}"`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, it, beforeEach, vi } from "vitest";
|
import { jest, describe, it, expect, beforeEach } from "@jest/globals";
|
||||||
import type {
|
import type {
|
||||||
IDockerComposeLogOptions,
|
IDockerComposeLogOptions,
|
||||||
IDockerComposeOptions,
|
IDockerComposeOptions,
|
||||||
@ -6,26 +6,16 @@ import type {
|
|||||||
} from "docker-compose";
|
} from "docker-compose";
|
||||||
|
|
||||||
// Mock docker-compose before importing the module under test
|
// Mock docker-compose before importing the module under test
|
||||||
const upAllMock =
|
const upAllMock = jest.fn<(options: IDockerComposeOptions) => Promise<IDockerComposeResult>>();
|
||||||
vi.fn<(options: IDockerComposeOptions) => Promise<IDockerComposeResult>>();
|
|
||||||
const upManyMock =
|
const upManyMock =
|
||||||
vi.fn<
|
jest.fn<(services: string[], options: IDockerComposeOptions) => Promise<IDockerComposeResult>>();
|
||||||
(
|
const downMock = jest.fn<(options: IDockerComposeOptions) => Promise<IDockerComposeResult>>();
|
||||||
services: string[],
|
|
||||||
options: IDockerComposeOptions,
|
|
||||||
) => Promise<IDockerComposeResult>
|
|
||||||
>();
|
|
||||||
const downMock =
|
|
||||||
vi.fn<(options: IDockerComposeOptions) => Promise<IDockerComposeResult>>();
|
|
||||||
const logsMock =
|
const logsMock =
|
||||||
vi.fn<
|
jest.fn<
|
||||||
(
|
(services: string[], options: IDockerComposeLogOptions) => Promise<IDockerComposeResult>
|
||||||
services: string[],
|
|
||||||
options: IDockerComposeLogOptions,
|
|
||||||
) => Promise<IDockerComposeResult>
|
|
||||||
>();
|
>();
|
||||||
|
|
||||||
vi.doMock("docker-compose", () => ({
|
jest.unstable_mockModule("docker-compose", () => ({
|
||||||
upAll: upAllMock,
|
upAll: upAllMock,
|
||||||
upMany: upManyMock,
|
upMany: upManyMock,
|
||||||
down: downMock,
|
down: downMock,
|
||||||
@ -39,7 +29,7 @@ describe("DockerComposeService", () => {
|
|||||||
let service: InstanceType<typeof DockerComposeService>;
|
let service: InstanceType<typeof DockerComposeService>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
service = new DockerComposeService();
|
service = new DockerComposeService();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -52,7 +42,7 @@ describe("DockerComposeService", () => {
|
|||||||
composeFlags: [] as string[],
|
composeFlags: [] as string[],
|
||||||
upFlags: [] as string[],
|
upFlags: [] as string[],
|
||||||
cwd: "/current/working/dir",
|
cwd: "/current/working/dir",
|
||||||
serviceLogger: vi.fn(),
|
serviceLogger: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
upAllMock.mockResolvedValue({ exitCode: 0, err: "", out: "" });
|
upAllMock.mockResolvedValue({ exitCode: 0, err: "", out: "" });
|
||||||
@ -72,8 +62,7 @@ describe("DockerComposeService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Ensure callback is calling the service logger
|
// Ensure callback is calling the service logger
|
||||||
const callback = (upAllMock.mock.calls[0][0] as IDockerComposeOptions)
|
const callback = (upAllMock.mock.calls[0][0] as IDockerComposeOptions)?.callback;
|
||||||
?.callback;
|
|
||||||
expect(callback).toBeDefined();
|
expect(callback).toBeDefined();
|
||||||
|
|
||||||
const message = "test log output";
|
const message = "test log output";
|
||||||
@ -93,7 +82,7 @@ describe("DockerComposeService", () => {
|
|||||||
composeFlags: [] as string[],
|
composeFlags: [] as string[],
|
||||||
upFlags: [] as string[],
|
upFlags: [] as string[],
|
||||||
cwd: "/current/working/dir",
|
cwd: "/current/working/dir",
|
||||||
serviceLogger: vi.fn(),
|
serviceLogger: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
upAllMock.mockResolvedValue({ exitCode: 0, err: "", out: "" });
|
upAllMock.mockResolvedValue({ exitCode: 0, err: "", out: "" });
|
||||||
@ -121,7 +110,7 @@ describe("DockerComposeService", () => {
|
|||||||
composeFlags: [] as string[],
|
composeFlags: [] as string[],
|
||||||
upFlags: ["--build"],
|
upFlags: ["--build"],
|
||||||
cwd: "/current/working/dir",
|
cwd: "/current/working/dir",
|
||||||
serviceLogger: vi.fn(),
|
serviceLogger: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
upManyMock.mockResolvedValue({ exitCode: 0, err: "", out: "" });
|
upManyMock.mockResolvedValue({ exitCode: 0, err: "", out: "" });
|
||||||
@ -149,7 +138,7 @@ describe("DockerComposeService", () => {
|
|||||||
composeFlags: [] as string[],
|
composeFlags: [] as string[],
|
||||||
upFlags: [] as string[],
|
upFlags: [] as string[],
|
||||||
cwd: "/current/working/dir",
|
cwd: "/current/working/dir",
|
||||||
serviceLogger: vi.fn(),
|
serviceLogger: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const dockerComposeError = {
|
const dockerComposeError = {
|
||||||
@ -161,11 +150,9 @@ describe("DockerComposeService", () => {
|
|||||||
upAllMock.mockRejectedValue(dockerComposeError);
|
upAllMock.mockRejectedValue(dockerComposeError);
|
||||||
|
|
||||||
await expect(service.up(upInputs)).rejects.toThrow(
|
await expect(service.up(upInputs)).rejects.toThrow(
|
||||||
"Docker Compose command failed with exit code 1",
|
"Docker Compose command failed with exit code 1"
|
||||||
);
|
|
||||||
await expect(service.up(upInputs)).rejects.toThrow(
|
|
||||||
"unable to pull image",
|
|
||||||
);
|
);
|
||||||
|
await expect(service.up(upInputs)).rejects.toThrow("unable to pull image");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw formatted error when upMany fails with docker-compose result", async () => {
|
it("should throw formatted error when upMany fails with docker-compose result", async () => {
|
||||||
@ -176,7 +163,7 @@ describe("DockerComposeService", () => {
|
|||||||
composeFlags: [] as string[],
|
composeFlags: [] as string[],
|
||||||
upFlags: [] as string[],
|
upFlags: [] as string[],
|
||||||
cwd: "/current/working/dir",
|
cwd: "/current/working/dir",
|
||||||
serviceLogger: vi.fn(),
|
serviceLogger: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const dockerComposeError = {
|
const dockerComposeError = {
|
||||||
@ -188,11 +175,9 @@ describe("DockerComposeService", () => {
|
|||||||
upManyMock.mockRejectedValue(dockerComposeError);
|
upManyMock.mockRejectedValue(dockerComposeError);
|
||||||
|
|
||||||
await expect(service.up(upInputs)).rejects.toThrow(
|
await expect(service.up(upInputs)).rejects.toThrow(
|
||||||
"Docker Compose command failed with exit code 1",
|
"Docker Compose command failed with exit code 1"
|
||||||
);
|
|
||||||
await expect(service.up(upInputs)).rejects.toThrow(
|
|
||||||
"Service 'web' failed to start",
|
|
||||||
);
|
);
|
||||||
|
await expect(service.up(upInputs)).rejects.toThrow("Service 'web' failed to start");
|
||||||
await expect(service.up(upInputs)).rejects.toThrow("Starting web...");
|
await expect(service.up(upInputs)).rejects.toThrow("Starting web...");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -204,7 +189,7 @@ describe("DockerComposeService", () => {
|
|||||||
composeFlags: [] as string[],
|
composeFlags: [] as string[],
|
||||||
upFlags: [] as string[],
|
upFlags: [] as string[],
|
||||||
cwd: "/current/working/dir",
|
cwd: "/current/working/dir",
|
||||||
serviceLogger: vi.fn(),
|
serviceLogger: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const dockerComposeError = {
|
const dockerComposeError = {
|
||||||
@ -215,37 +200,7 @@ describe("DockerComposeService", () => {
|
|||||||
|
|
||||||
upAllMock.mockRejectedValue(dockerComposeError);
|
upAllMock.mockRejectedValue(dockerComposeError);
|
||||||
|
|
||||||
await expect(service.up(upInputs)).rejects.toThrow(
|
await expect(service.up(upInputs)).rejects.toThrow("Some error without exit code");
|
||||||
"Some error without exit code",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format docker-compose result when streams are undefined", async () => {
|
|
||||||
const upInputs = {
|
|
||||||
dockerFlags: [] as string[],
|
|
||||||
composeFiles: ["docker-compose.yml"],
|
|
||||||
services: [] as string[],
|
|
||||||
composeFlags: [] as string[],
|
|
||||||
upFlags: [] as string[],
|
|
||||||
cwd: "/current/working/dir",
|
|
||||||
serviceLogger: vi.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const dockerComposeError = {
|
|
||||||
exitCode: 1,
|
|
||||||
err: undefined,
|
|
||||||
out: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
upAllMock.mockRejectedValue(dockerComposeError);
|
|
||||||
|
|
||||||
await expect(service.up(upInputs)).rejects.toThrow(
|
|
||||||
"Docker Compose command failed with exit code 1",
|
|
||||||
);
|
|
||||||
await expect(service.up(upInputs)).rejects.not.toThrow("Error output:");
|
|
||||||
await expect(service.up(upInputs)).rejects.not.toThrow(
|
|
||||||
"Standard output:",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should pass through standard Error objects", async () => {
|
it("should pass through standard Error objects", async () => {
|
||||||
@ -256,15 +211,13 @@ describe("DockerComposeService", () => {
|
|||||||
composeFlags: [] as string[],
|
composeFlags: [] as string[],
|
||||||
upFlags: [] as string[],
|
upFlags: [] as string[],
|
||||||
cwd: "/current/working/dir",
|
cwd: "/current/working/dir",
|
||||||
serviceLogger: vi.fn(),
|
serviceLogger: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const standardError = new Error("Standard error message");
|
const standardError = new Error("Standard error message");
|
||||||
upAllMock.mockRejectedValue(standardError);
|
upAllMock.mockRejectedValue(standardError);
|
||||||
|
|
||||||
await expect(service.up(upInputs)).rejects.toThrow(
|
await expect(service.up(upInputs)).rejects.toThrow("Standard error message");
|
||||||
"Standard error message",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should pass through error strings", async () => {
|
it("should pass through error strings", async () => {
|
||||||
@ -275,7 +228,7 @@ describe("DockerComposeService", () => {
|
|||||||
composeFlags: [] as string[],
|
composeFlags: [] as string[],
|
||||||
upFlags: [] as string[],
|
upFlags: [] as string[],
|
||||||
cwd: "/current/working/dir",
|
cwd: "/current/working/dir",
|
||||||
serviceLogger: vi.fn(),
|
serviceLogger: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const unknownError = "Some unknown error";
|
const unknownError = "Some unknown error";
|
||||||
@ -292,15 +245,13 @@ describe("DockerComposeService", () => {
|
|||||||
composeFlags: [] as string[],
|
composeFlags: [] as string[],
|
||||||
upFlags: [] as string[],
|
upFlags: [] as string[],
|
||||||
cwd: "/current/working/dir",
|
cwd: "/current/working/dir",
|
||||||
serviceLogger: vi.fn(),
|
serviceLogger: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const unknownError = { unexpected: "error format" };
|
const unknownError = { unexpected: "error format" };
|
||||||
upAllMock.mockRejectedValue(unknownError);
|
upAllMock.mockRejectedValue(unknownError);
|
||||||
|
|
||||||
await expect(service.up(upInputs)).rejects.toThrow(
|
await expect(service.up(upInputs)).rejects.toThrow(JSON.stringify(unknownError));
|
||||||
JSON.stringify(unknownError),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -312,7 +263,7 @@ describe("DockerComposeService", () => {
|
|||||||
composeFlags: [] as string[],
|
composeFlags: [] as string[],
|
||||||
downFlags: ["--volumes", "--remove-orphans"],
|
downFlags: ["--volumes", "--remove-orphans"],
|
||||||
cwd: "/current/working/dir",
|
cwd: "/current/working/dir",
|
||||||
serviceLogger: vi.fn(),
|
serviceLogger: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
downMock.mockResolvedValue({ exitCode: 0, err: "", out: "" });
|
downMock.mockResolvedValue({ exitCode: 0, err: "", out: "" });
|
||||||
@ -339,7 +290,7 @@ describe("DockerComposeService", () => {
|
|||||||
composeFlags: [] as string[],
|
composeFlags: [] as string[],
|
||||||
downFlags: [] as string[],
|
downFlags: [] as string[],
|
||||||
cwd: "/current/working/dir",
|
cwd: "/current/working/dir",
|
||||||
serviceLogger: vi.fn(),
|
serviceLogger: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const dockerComposeError = {
|
const dockerComposeError = {
|
||||||
@ -351,17 +302,15 @@ describe("DockerComposeService", () => {
|
|||||||
downMock.mockRejectedValue(dockerComposeError);
|
downMock.mockRejectedValue(dockerComposeError);
|
||||||
|
|
||||||
await expect(service.down(downInputs)).rejects.toThrow(
|
await expect(service.down(downInputs)).rejects.toThrow(
|
||||||
"Docker Compose command failed with exit code 1",
|
"Docker Compose command failed with exit code 1"
|
||||||
);
|
|
||||||
await expect(service.down(downInputs)).rejects.toThrow(
|
|
||||||
"Error stopping containers",
|
|
||||||
);
|
);
|
||||||
|
await expect(service.down(downInputs)).rejects.toThrow("Error stopping containers");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("logs", () => {
|
describe("logs", () => {
|
||||||
it("should call logs with correct options", async () => {
|
it("should call logs with correct options", async () => {
|
||||||
const debugMock = vi.fn();
|
const debugMock = jest.fn();
|
||||||
const logsInputs = {
|
const logsInputs = {
|
||||||
dockerFlags: [] as string[],
|
dockerFlags: [] as string[],
|
||||||
composeFiles: ["docker-compose.yml"],
|
composeFiles: ["docker-compose.yml"],
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
down,
|
down,
|
||||||
type IDockerComposeLogOptions,
|
IDockerComposeLogOptions,
|
||||||
type IDockerComposeOptions,
|
IDockerComposeOptions,
|
||||||
type IDockerComposeResult,
|
IDockerComposeResult,
|
||||||
logs,
|
logs,
|
||||||
upAll,
|
upAll,
|
||||||
upMany,
|
upMany,
|
||||||
} from "docker-compose";
|
} from "docker-compose";
|
||||||
import type { Inputs } from "./input.service.js";
|
import { Inputs } from "./input.service.js";
|
||||||
|
|
||||||
type OptionsInputs = {
|
type OptionsInputs = {
|
||||||
dockerFlags: Inputs["dockerFlags"];
|
dockerFlags: Inputs["dockerFlags"];
|
||||||
@ -17,10 +17,7 @@ type OptionsInputs = {
|
|||||||
serviceLogger: (message: string) => void;
|
serviceLogger: (message: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpInputs = OptionsInputs & {
|
export type UpInputs = OptionsInputs & { upFlags: Inputs["upFlags"]; services: Inputs["services"] };
|
||||||
upFlags: Inputs["upFlags"];
|
|
||||||
services: Inputs["services"];
|
|
||||||
};
|
|
||||||
export type DownInputs = OptionsInputs & { downFlags: Inputs["downFlags"] };
|
export type DownInputs = OptionsInputs & { downFlags: Inputs["downFlags"] };
|
||||||
export type LogsInputs = OptionsInputs & { services: Inputs["services"] };
|
export type LogsInputs = OptionsInputs & { services: Inputs["services"] };
|
||||||
|
|
||||||
@ -107,21 +104,19 @@ export class DockerComposeService {
|
|||||||
|
|
||||||
// Add exit code information
|
// Add exit code information
|
||||||
if (error.exitCode !== null) {
|
if (error.exitCode !== null) {
|
||||||
parts.push(
|
parts.push(`Docker Compose command failed with exit code ${error.exitCode}`);
|
||||||
`Docker Compose command failed with exit code ${error.exitCode}`,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
parts.push("Docker Compose command failed");
|
parts.push("Docker Compose command failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add error stream output if available
|
// Add error stream output if available
|
||||||
if (error.err?.trim()) {
|
if (error.err && error.err.trim()) {
|
||||||
parts.push("\nError output:");
|
parts.push("\nError output:");
|
||||||
parts.push(error.err.trim());
|
parts.push(error.err.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add standard output if available and different from error output
|
// Add standard output if available and different from error output
|
||||||
if (error.out?.trim() && error.out !== error.err) {
|
if (error.out && error.out.trim() && error.out !== error.err) {
|
||||||
parts.push("\nStandard output:");
|
parts.push("\nStandard output:");
|
||||||
parts.push(error.out.trim());
|
parts.push(error.out.trim());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,22 @@
|
|||||||
import { describe, expect, it, beforeEach, vi } from "vitest";
|
import { jest, describe, it, expect, beforeEach } from "@jest/globals";
|
||||||
|
|
||||||
// Mock @actions/core before importing the module under test
|
// Mock @actions/core before importing the module under test
|
||||||
const getInputMock =
|
const getInputMock = jest.fn<(name: string, options?: { required?: boolean }) => string>();
|
||||||
vi.fn<(name: string, options?: { required?: boolean }) => string>();
|
|
||||||
const getMultilineInputMock =
|
const getMultilineInputMock =
|
||||||
vi.fn<(name: string, options?: { required?: boolean }) => string[]>();
|
jest.fn<(name: string, options?: { required?: boolean }) => string[]>();
|
||||||
|
|
||||||
vi.doMock("@actions/core", () => ({
|
jest.unstable_mockModule("@actions/core", () => ({
|
||||||
getInput: getInputMock,
|
getInput: getInputMock,
|
||||||
getMultilineInput: getMultilineInputMock,
|
getMultilineInput: getMultilineInputMock,
|
||||||
debug: vi.fn(),
|
debug: jest.fn(),
|
||||||
info: vi.fn(),
|
info: jest.fn(),
|
||||||
warning: vi.fn(),
|
warning: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock node:fs
|
// Mock node:fs
|
||||||
const existsSyncMock = vi.fn<(path: string) => boolean>();
|
const existsSyncMock = jest.fn<(path: string) => boolean>();
|
||||||
|
|
||||||
vi.doMock("node:fs", () => ({
|
jest.unstable_mockModule("node:fs", () => ({
|
||||||
existsSync: existsSyncMock,
|
existsSync: existsSyncMock,
|
||||||
default: { existsSync: existsSyncMock },
|
default: { existsSync: existsSyncMock },
|
||||||
}));
|
}));
|
||||||
@ -30,7 +29,7 @@ describe("InputService", () => {
|
|||||||
let service: InstanceType<typeof InputService>;
|
let service: InstanceType<typeof InputService>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
|
||||||
getMultilineInputMock.mockImplementation((inputName) => {
|
getMultilineInputMock.mockImplementation((inputName) => {
|
||||||
switch (inputName) {
|
switch (inputName) {
|
||||||
@ -161,9 +160,7 @@ describe("InputService", () => {
|
|||||||
|
|
||||||
const inputs = service.getInputs();
|
const inputs = service.getInputs();
|
||||||
|
|
||||||
expect(inputs.composeFiles).toEqual([
|
expect(inputs.composeFiles).toEqual(["oci://docker.io/hoverkraft/compose-app:latest"]);
|
||||||
"oci://docker.io/hoverkraft/compose-app:latest",
|
|
||||||
]);
|
|
||||||
expect(existsSyncMock).not.toHaveBeenCalled();
|
expect(existsSyncMock).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -186,12 +183,10 @@ describe("InputService", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
existsSyncMock.mockImplementation(
|
existsSyncMock.mockImplementation((file) => file === "/current/working/directory/file1");
|
||||||
(file) => file === "/current/working/directory/file1",
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(() => service.getInputs()).toThrow(
|
expect(() => service.getInputs()).toThrow(
|
||||||
'Compose file not found in "/current/working/directory/file2", "file2"',
|
'Compose file not found in "/current/working/directory/file2", "file2"'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -391,7 +386,7 @@ describe("InputService", () => {
|
|||||||
existsSyncMock.mockReturnValue(true);
|
existsSyncMock.mockReturnValue(true);
|
||||||
|
|
||||||
expect(() => service.getInputs()).toThrow(
|
expect(() => service.getInputs()).toThrow(
|
||||||
'Invalid service log level "invalid-log-level". Valid values are: debug, info',
|
'Invalid service log level "invalid-log-level". Valid values are: debug, info'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -53,8 +53,7 @@ export class InputService {
|
|||||||
|
|
||||||
private getComposeFiles(): string[] {
|
private getComposeFiles(): string[] {
|
||||||
const cwd = this.getCwd();
|
const cwd = this.getCwd();
|
||||||
const composeFiles = getMultilineInput(InputNames.ComposeFile).filter(
|
const composeFiles = getMultilineInput(InputNames.ComposeFile).filter((composeFile: string) => {
|
||||||
(composeFile: string) => {
|
|
||||||
const trimmedComposeFile = composeFile.trim();
|
const trimmedComposeFile = composeFile.trim();
|
||||||
|
|
||||||
if (!trimmedComposeFile.length) {
|
if (!trimmedComposeFile.length) {
|
||||||
@ -73,11 +72,8 @@ export class InputService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(`Compose file not found in "${possiblePaths.join('", "')}"`);
|
||||||
`Compose file not found in "${possiblePaths.join('", "')}"`,
|
});
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!composeFiles.length) {
|
if (!composeFiles.length) {
|
||||||
throw new Error("No compose files found");
|
throw new Error("No compose files found");
|
||||||
@ -131,15 +127,10 @@ export class InputService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getServiceLogLevel(): LogLevel {
|
private getServiceLogLevel(): LogLevel {
|
||||||
const configuredLevel = getInput(InputNames.ServiceLogLevel, {
|
const configuredLevel = getInput(InputNames.ServiceLogLevel, { required: false });
|
||||||
required: false,
|
if (configuredLevel && !Object.values(LogLevel).includes(configuredLevel as LogLevel)) {
|
||||||
});
|
|
||||||
if (
|
|
||||||
configuredLevel &&
|
|
||||||
!Object.values(LogLevel).includes(configuredLevel as LogLevel)
|
|
||||||
) {
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Invalid service log level "${configuredLevel}". Valid values are: ${Object.values(LogLevel).join(", ")}`,
|
`Invalid service log level "${configuredLevel}". Valid values are: ${Object.values(LogLevel).join(", ")}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (configuredLevel as LogLevel) || LogLevel.Debug;
|
return (configuredLevel as LogLevel) || LogLevel.Debug;
|
||||||
|
|||||||
@ -1,73 +1,52 @@
|
|||||||
import { describe, expect, it, beforeEach, vi } from "vitest";
|
import { jest, describe, it, expect, beforeEach } from "@jest/globals";
|
||||||
import type { ExecOptions } from "@actions/exec";
|
import type { ExecOptions } from "@actions/exec";
|
||||||
import type { OutgoingHttpHeaders } from "node:http";
|
import type { OutgoingHttpHeaders } from "node:http";
|
||||||
|
|
||||||
// Mock @actions/exec
|
// Mock @actions/exec
|
||||||
const execMock =
|
const execMock =
|
||||||
vi.fn<
|
jest.fn<(command: string, args?: string[], options?: ExecOptions) => Promise<number>>();
|
||||||
(command: string, args?: string[], options?: ExecOptions) => Promise<number>
|
|
||||||
>();
|
|
||||||
|
|
||||||
vi.doMock("@actions/exec", () => ({
|
jest.unstable_mockModule("@actions/exec", () => ({
|
||||||
exec: execMock,
|
exec: execMock,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock @actions/io
|
// Mock @actions/io
|
||||||
const mkdirPMock = vi.fn<(fsPath: string) => Promise<void>>();
|
const mkdirPMock = jest.fn<(fsPath: string) => Promise<void>>();
|
||||||
|
|
||||||
vi.doMock("@actions/io", () => ({
|
jest.unstable_mockModule("@actions/io", () => ({
|
||||||
mkdirP: mkdirPMock,
|
mkdirP: mkdirPMock,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock @actions/tool-cache
|
// Mock @actions/tool-cache
|
||||||
const cacheFileMock =
|
const cacheFileMock =
|
||||||
vi.fn<
|
jest.fn<
|
||||||
(
|
(
|
||||||
sourceFile: string,
|
sourceFile: string,
|
||||||
targetFile: string,
|
targetFile: string,
|
||||||
tool: string,
|
tool: string,
|
||||||
version: string,
|
version: string,
|
||||||
arch?: string,
|
arch?: string
|
||||||
) => Promise<string>
|
) => Promise<string>
|
||||||
>();
|
>();
|
||||||
const downloadToolMock =
|
const downloadToolMock =
|
||||||
vi.fn<
|
jest.fn<
|
||||||
(
|
(url: string, dest?: string, auth?: string, headers?: OutgoingHttpHeaders) => Promise<string>
|
||||||
url: string,
|
|
||||||
dest?: string,
|
|
||||||
auth?: string,
|
|
||||||
headers?: OutgoingHttpHeaders,
|
|
||||||
) => Promise<string>
|
|
||||||
>();
|
>();
|
||||||
|
|
||||||
vi.doMock("@actions/tool-cache", () => ({
|
jest.unstable_mockModule("@actions/tool-cache", () => ({
|
||||||
cacheFile: cacheFileMock,
|
cacheFile: cacheFileMock,
|
||||||
downloadTool: downloadToolMock,
|
downloadTool: downloadToolMock,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Dynamic import after mock setup
|
// Dynamic import after mock setup
|
||||||
const { ManualInstallerAdapter } = await import(
|
const { ManualInstallerAdapter } = await import("./manual-installer-adapter.js");
|
||||||
"./manual-installer-adapter.js"
|
|
||||||
);
|
|
||||||
|
|
||||||
const originalHome = process.env.HOME;
|
|
||||||
const originalDockerConfig = process.env.DOCKER_CONFIG;
|
|
||||||
|
|
||||||
describe("ManualInstallerAdapter", () => {
|
describe("ManualInstallerAdapter", () => {
|
||||||
let adapter: InstanceType<typeof ManualInstallerAdapter>;
|
let adapter: InstanceType<typeof ManualInstallerAdapter>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.resetAllMocks();
|
jest.clearAllMocks();
|
||||||
if (originalHome === undefined) {
|
|
||||||
delete process.env.HOME;
|
|
||||||
} else {
|
|
||||||
process.env.HOME = originalHome;
|
|
||||||
}
|
|
||||||
if (originalDockerConfig === undefined) {
|
|
||||||
delete process.env.DOCKER_CONFIG;
|
delete process.env.DOCKER_CONFIG;
|
||||||
} else {
|
|
||||||
process.env.DOCKER_CONFIG = originalDockerConfig;
|
|
||||||
}
|
|
||||||
adapter = new ManualInstallerAdapter();
|
adapter = new ManualInstallerAdapter();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -76,17 +55,15 @@ describe("ManualInstallerAdapter", () => {
|
|||||||
// Arrange
|
// Arrange
|
||||||
const version = "v2.29.0";
|
const version = "v2.29.0";
|
||||||
|
|
||||||
execMock.mockImplementationOnce(async (_command, _args, options) => {
|
// Uname -s
|
||||||
options?.listeners?.stdout?.(Buffer.from("Linux\n"));
|
execMock.mockResolvedValueOnce(0);
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
execMock.mockImplementationOnce(async (_command, _args, options) => {
|
// Uname -m
|
||||||
options?.listeners?.stdout?.(Buffer.from("x86_64\n"));
|
execMock.mockResolvedValueOnce(0);
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
process.env.HOME = "/home/test";
|
Object.defineProperty(process.env, "HOME", {
|
||||||
|
value: "/home/test",
|
||||||
|
});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await adapter.install(version);
|
await adapter.install(version);
|
||||||
@ -101,15 +78,15 @@ describe("ManualInstallerAdapter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(downloadToolMock).toHaveBeenCalledWith(
|
expect(downloadToolMock).toHaveBeenCalledWith(
|
||||||
"https://github.com/docker/compose/releases/download/v2.29.0/docker-compose-Linux-x86_64",
|
"https://github.com/docker/compose/releases/download/v2.29.0/docker-compose--",
|
||||||
"/home/test/.docker/cli-plugins/docker-compose",
|
"/home/test/.docker/cli-plugins/docker-compose"
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(cacheFileMock).toHaveBeenCalledWith(
|
expect(cacheFileMock).toHaveBeenCalledWith(
|
||||||
"/home/test/.docker/cli-plugins/docker-compose",
|
"/home/test/.docker/cli-plugins/docker-compose",
|
||||||
"docker-compose",
|
"docker-compose",
|
||||||
"docker-compose",
|
"docker-compose",
|
||||||
version,
|
version
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -135,7 +112,7 @@ describe("ManualInstallerAdapter", () => {
|
|||||||
// Assert
|
// Assert
|
||||||
expect(downloadToolMock).toHaveBeenCalledWith(
|
expect(downloadToolMock).toHaveBeenCalledWith(
|
||||||
"https://github.com/docker/compose/releases/download/v2.29.0/docker-compose-Linux-x86_64",
|
"https://github.com/docker/compose/releases/download/v2.29.0/docker-compose-Linux-x86_64",
|
||||||
"/custom/docker/cli-plugins/docker-compose",
|
"/custom/docker/cli-plugins/docker-compose"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -143,17 +120,15 @@ describe("ManualInstallerAdapter", () => {
|
|||||||
// Arrange
|
// Arrange
|
||||||
const version = "2.29.0";
|
const version = "2.29.0";
|
||||||
|
|
||||||
execMock.mockImplementationOnce(async (_command, _args, options) => {
|
// Uname -s
|
||||||
options?.listeners?.stdout?.(Buffer.from("Linux\n"));
|
execMock.mockResolvedValueOnce(0);
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
execMock.mockImplementationOnce(async (_command, _args, options) => {
|
// Uname -m
|
||||||
options?.listeners?.stdout?.(Buffer.from("x86_64\n"));
|
execMock.mockResolvedValueOnce(0);
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
process.env.HOME = "/home/test";
|
Object.defineProperty(process.env, "HOME", {
|
||||||
|
value: "/home/test",
|
||||||
|
});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await adapter.install(version);
|
await adapter.install(version);
|
||||||
@ -168,8 +143,8 @@ describe("ManualInstallerAdapter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(downloadToolMock).toHaveBeenCalledWith(
|
expect(downloadToolMock).toHaveBeenCalledWith(
|
||||||
"https://github.com/docker/compose/releases/download/v2.29.0/docker-compose-Linux-x86_64",
|
"https://github.com/docker/compose/releases/download/v2.29.0/docker-compose--",
|
||||||
"/home/test/.docker/cli-plugins/docker-compose",
|
"/home/test/.docker/cli-plugins/docker-compose"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -188,7 +163,9 @@ describe("ManualInstallerAdapter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
delete process.env.DOCKER_CONFIG;
|
delete process.env.DOCKER_CONFIG;
|
||||||
process.env.HOME = "/home/test";
|
Object.defineProperty(process.env, "HOME", {
|
||||||
|
value: "/home/test",
|
||||||
|
});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await adapter.install(version);
|
await adapter.install(version);
|
||||||
@ -196,7 +173,7 @@ describe("ManualInstallerAdapter", () => {
|
|||||||
// Assert
|
// Assert
|
||||||
expect(downloadToolMock).toHaveBeenCalledWith(
|
expect(downloadToolMock).toHaveBeenCalledWith(
|
||||||
"https://github.com/docker/compose/releases/download/1.29.0/docker-compose-Linux-x86_64",
|
"https://github.com/docker/compose/releases/download/1.29.0/docker-compose-Linux-x86_64",
|
||||||
"/home/test/.docker/cli-plugins/docker-compose",
|
"/home/test/.docker/cli-plugins/docker-compose"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -208,9 +185,7 @@ describe("ManualInstallerAdapter", () => {
|
|||||||
execMock.mockResolvedValueOnce(1);
|
execMock.mockResolvedValueOnce(1);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await expect(adapter.install(version)).rejects.toThrow(
|
await expect(adapter.install(version)).rejects.toThrow("Failed to run command: uname -s");
|
||||||
"Failed to run command: uname -s",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(execMock).toHaveBeenNthCalledWith(1, "uname -s", [], {
|
expect(execMock).toHaveBeenNthCalledWith(1, "uname -s", [], {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { exec } from "@actions/exec";
|
|||||||
import { mkdirP } from "@actions/io";
|
import { mkdirP } from "@actions/io";
|
||||||
import { basename } from "node:path";
|
import { basename } from "node:path";
|
||||||
import { cacheFile, downloadTool } from "@actions/tool-cache";
|
import { cacheFile, downloadTool } from "@actions/tool-cache";
|
||||||
import type { DockerComposeInstallerAdapter } from "./docker-compose-installer-adapter.js";
|
import { DockerComposeInstallerAdapter } from "./docker-compose-installer-adapter.js";
|
||||||
|
|
||||||
export class ManualInstallerAdapter implements DockerComposeInstallerAdapter {
|
export class ManualInstallerAdapter implements DockerComposeInstallerAdapter {
|
||||||
async install(version: string): Promise<void> {
|
async install(version: string): Promise<void> {
|
||||||
@ -13,26 +13,17 @@ export class ManualInstallerAdapter implements DockerComposeInstallerAdapter {
|
|||||||
|
|
||||||
await this.downloadFile(version, dockerComposePluginPath);
|
await this.downloadFile(version, dockerComposePluginPath);
|
||||||
await exec(`chmod +x ${dockerComposePluginPath}`);
|
await exec(`chmod +x ${dockerComposePluginPath}`);
|
||||||
await cacheFile(
|
await cacheFile(dockerComposePluginPath, "docker-compose", "docker-compose", version);
|
||||||
dockerComposePluginPath,
|
|
||||||
"docker-compose",
|
|
||||||
"docker-compose",
|
|
||||||
version,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getDockerComposePluginPath(): Promise<string> {
|
private async getDockerComposePluginPath(): Promise<string> {
|
||||||
const dockerConfig =
|
const dockerConfig = process.env.DOCKER_CONFIG || `${process.env.HOME}/.docker`;
|
||||||
process.env.DOCKER_CONFIG || `${process.env.HOME}/.docker`;
|
|
||||||
|
|
||||||
const dockerComposePluginPath = `${dockerConfig}/cli-plugins/docker-compose`;
|
const dockerComposePluginPath = `${dockerConfig}/cli-plugins/docker-compose`;
|
||||||
return dockerComposePluginPath;
|
return dockerComposePluginPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async downloadFile(
|
private async downloadFile(version: string, installerPath: string): Promise<void> {
|
||||||
version: string,
|
|
||||||
installerPath: string,
|
|
||||||
): Promise<void> {
|
|
||||||
if (!version.startsWith("v") && parseInt(version.split(".")[0], 10) >= 2) {
|
if (!version.startsWith("v") && parseInt(version.split(".")[0], 10) >= 2) {
|
||||||
version = `v${version}`;
|
version = `v${version}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { describe, expect, it, beforeEach, vi } from "vitest";
|
import { jest, describe, it, expect, beforeEach } from "@jest/globals";
|
||||||
|
|
||||||
// Import types directly from the module
|
// Import types directly from the module
|
||||||
import type { LogLevel as LogLevelType } from "./logger.service.js";
|
import type { LogLevel as LogLevelType } from "./logger.service.js";
|
||||||
|
|
||||||
// Mock @actions/core before importing the module under test
|
// Mock @actions/core before importing the module under test
|
||||||
const warningMock = vi.fn();
|
const warningMock = jest.fn();
|
||||||
const infoMock = vi.fn();
|
const infoMock = jest.fn();
|
||||||
const debugMock = vi.fn();
|
const debugMock = jest.fn();
|
||||||
|
|
||||||
vi.doMock("@actions/core", () => ({
|
jest.unstable_mockModule("@actions/core", () => ({
|
||||||
warning: warningMock,
|
warning: warningMock,
|
||||||
info: infoMock,
|
info: infoMock,
|
||||||
debug: debugMock,
|
debug: debugMock,
|
||||||
@ -21,7 +21,7 @@ describe("LoggerService", () => {
|
|||||||
let loggerService: InstanceType<typeof LoggerService>;
|
let loggerService: InstanceType<typeof LoggerService>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
loggerService = new LoggerService();
|
loggerService = new LoggerService();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,6 @@ WORKDIR /app
|
|||||||
COPY entrypoint.sh .
|
COPY entrypoint.sh .
|
||||||
RUN chmod +x entrypoint.sh
|
RUN chmod +x entrypoint.sh
|
||||||
|
|
||||||
HEALTHCHECK CMD grep -qa "entrypoint.sh" /proc/1/cmdline || exit 1
|
|
||||||
|
|
||||||
CMD ["/bin/sh", "entrypoint.sh"]
|
CMD ["/bin/sh", "entrypoint.sh"]
|
||||||
|
|
||||||
USER 1000:1000
|
USER 1000:1000
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/tsconfig",
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2024",
|
"target": "ES2022",
|
||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
import { defineConfig } from "vitest/config";
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
test: {
|
|
||||||
coverage: {
|
|
||||||
include: ["src/**/*.{ts,tsx,js,jsx}"],
|
|
||||||
provider: "v8",
|
|
||||||
},
|
|
||||||
environment: "node",
|
|
||||||
globals: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Loading…
x
Reference in New Issue
Block a user