mirror of
https://github.com/TheBinaryNinja/tvapp2.git
synced 2026-06-04 12:45:41 -04:00
build: push tvapp v2 docker files
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"projectName": "thetvapp-docker",
|
"projectName": "tvapp2",
|
||||||
"projectOwner": "Aetherinox",
|
"projectOwner": "Aetherinox",
|
||||||
"repoType": "github",
|
"repoType": "github",
|
||||||
"repoHost": "https://github.com",
|
"repoHost": "https://github.com",
|
||||||
@@ -12,15 +12,22 @@
|
|||||||
"login": "Aetherinox",
|
"login": "Aetherinox",
|
||||||
"name": "Aetherinox",
|
"name": "Aetherinox",
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/118329232?v=4",
|
"avatar_url": "https://avatars.githubusercontent.com/u/118329232?v=4",
|
||||||
"profile": "https://gitlab.com/Aetherinox",
|
"profile": "https://github.com/Aetherinox",
|
||||||
"contributions": ["code", "projectManagement"]
|
"contributions": ["code"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"login": "dtankdempse",
|
"login": "iFlip721",
|
||||||
"name": "dtankdempse",
|
"name": "iFlip721",
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/175421607?v=4",
|
"avatar_url": "https://avatars.githubusercontent.com/u/28721588?v=4",
|
||||||
"profile": "https://gitlab.com/dtankdempse",
|
"profile": "https://github.com/iFlip721",
|
||||||
"contributions": ["tools"]
|
"contributions": ["code"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Optx",
|
||||||
|
"name": "Optx",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/32874812?v=4",
|
||||||
|
"profile": "https://github.com/Nvmdfth",
|
||||||
|
"contributions": ["code"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
.git
|
|
||||||
.gitignore
|
|
||||||
.github
|
|
||||||
.gitattributes
|
|
||||||
READMETEMPLATE.md
|
|
||||||
README.md
|
|
||||||
@@ -1,9 +1,20 @@
|
|||||||
# http://editorconfig.org
|
# #
|
||||||
|
# @file .editorconfig
|
||||||
|
# @author Aetherinox https://github.com/Aetherinox
|
||||||
|
# https://git.binaryninja.net/Aetherinox
|
||||||
|
# @ref http://editorconfig.org
|
||||||
|
# #
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Is top-most EditorConfig file
|
||||||
|
# #
|
||||||
|
|
||||||
# is top-most EditorConfig file
|
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
# All Files
|
# #
|
||||||
|
# All Files
|
||||||
|
# #
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
@@ -12,11 +23,17 @@ charset = utf-8
|
|||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
# Markdown Files
|
# #
|
||||||
|
# Markdown Files
|
||||||
|
# #
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
# Other
|
# #
|
||||||
|
# Other
|
||||||
|
# #
|
||||||
|
|
||||||
[{*.nsh,*.yml,*.yaml,*.json}]
|
[{*.nsh,*.yml,*.yaml,*.json}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|||||||
41
.gitattributes
vendored
41
.gitattributes
vendored
@@ -1,17 +1,32 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
# #
|
||||||
|
# @file .gitattritutes
|
||||||
|
# @author Aetherinox https://github.com/Aetherinox
|
||||||
|
# https://git.binaryninja.net/Aetherinox
|
||||||
|
# #
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Auto detect text files and set LF
|
||||||
|
# #
|
||||||
|
|
||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
# Custom for Visual Studio
|
# #
|
||||||
|
# Visual Studio
|
||||||
|
# #
|
||||||
|
|
||||||
*.cs diff=csharp
|
*.cs diff=csharp
|
||||||
|
|
||||||
# Standard to msysgit
|
# #
|
||||||
*.doc diff=astextplain
|
# msysgit
|
||||||
*.DOC diff=astextplain
|
# #
|
||||||
*.docx diff=astextplain
|
|
||||||
*.DOCX diff=astextplain
|
*.doc diff=astextplain
|
||||||
*.dot diff=astextplain
|
*.DOC diff=astextplain
|
||||||
*.DOT diff=astextplain
|
*.docx diff=astextplain
|
||||||
*.pdf diff=astextplain
|
*.DOCX diff=astextplain
|
||||||
*.PDF diff=astextplain
|
*.dot diff=astextplain
|
||||||
*.rtf diff=astextplain
|
*.DOT diff=astextplain
|
||||||
*.RTF diff=astextplain
|
*.pdf diff=astextplain
|
||||||
|
*.PDF diff=astextplain
|
||||||
|
*.rtf diff=astextplain
|
||||||
|
*.RTF diff=astextplain
|
||||||
|
|||||||
10
.github/FUNDING.yml
vendored
10
.github/FUNDING.yml
vendored
@@ -1,10 +0,0 @@
|
|||||||
custom: ["https://buymeacoffee.com/aetherinox"]
|
|
||||||
github: # [repo-name, aetherinox]
|
|
||||||
patreon: # Replace with a single Patreon username
|
|
||||||
open_collective: # name
|
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
||||||
liberapay: # Replace with a single Liberapay username
|
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
|
||||||
otechie: # Replace with a single Otechie username
|
|
||||||
109
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
109
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -1,109 +0,0 @@
|
|||||||
name: "🐛 Bug Report"
|
|
||||||
description: Found something you weren't expecting? Report it here!
|
|
||||||
title: "🐛 Bug: <title>"
|
|
||||||
labels: [
|
|
||||||
"Type ◦ Bug"
|
|
||||||
]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
1. Please speak `English`.
|
|
||||||
2. Make sure you are using the latest version and take a moment to check that your issue hasn't been reported before.
|
|
||||||
3. It's really important to provide pertinent details and logs,
|
|
||||||
incomplete details will be handled as an invalid report.
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: |
|
|
||||||
Please provide a description of your issue here.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: steps-reproduce
|
|
||||||
attributes:
|
|
||||||
label: Steps To Reproduce
|
|
||||||
description: |
|
|
||||||
Describe the steps that need taken by the developer to get the error / issue you're experiencing.
|
|
||||||
value: |
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: version-thetvapp
|
|
||||||
attributes:
|
|
||||||
label: "Version - Tag"
|
|
||||||
description: |
|
|
||||||
Version / tag you are pulling for `thetvapp`
|
|
||||||
placeholder: "Ex: 1.0.0"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: version-docker
|
|
||||||
attributes:
|
|
||||||
label: "Version - Docker"
|
|
||||||
description: "Version of docker you are running. Use command `docker --version`."
|
|
||||||
placeholder: "Ex: 27.2.0, build 3ab4256"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: image-source
|
|
||||||
attributes:
|
|
||||||
label: Docker Image Source
|
|
||||||
description: |
|
|
||||||
Select which docker image you are pulling from
|
|
||||||
options:
|
|
||||||
- "Github"
|
|
||||||
- "Dockerhub"
|
|
||||||
- "Custom Built"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: priority-type
|
|
||||||
attributes:
|
|
||||||
label: Priority
|
|
||||||
description: |
|
|
||||||
How critical is the issue?
|
|
||||||
Do not abuse this. Issues that completely break the utility would be classified as critical
|
|
||||||
options:
|
|
||||||
- "Low"
|
|
||||||
- "Normal"
|
|
||||||
- "High"
|
|
||||||
- "Urgent"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: docker-compose
|
|
||||||
attributes:
|
|
||||||
label: docker-compose.yml
|
|
||||||
description: |
|
|
||||||
Copy / paste your `docker-compose.yml` file here
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: logs
|
|
||||||
attributes:
|
|
||||||
label: Logs
|
|
||||||
description: |
|
|
||||||
Paste your docker logs here.
|
|
||||||
Paste logs from inside mounted volume `/config/log/*`
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: screenshots
|
|
||||||
attributes:
|
|
||||||
label: Screenshots
|
|
||||||
description: |
|
|
||||||
Please provide screenshots of any errors or the issue you're having.
|
|
||||||
Gifs are even better.
|
|
||||||
33
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
33
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -1,33 +0,0 @@
|
|||||||
name: "💡 Feature Request"
|
|
||||||
description: Got a suggestion? Submit your request here.
|
|
||||||
title: "💡 Feature: <title>"
|
|
||||||
labels: [
|
|
||||||
"Type ◦ Feature"
|
|
||||||
]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
1. Please speak English.
|
|
||||||
2. Please take a moment to check that your feature hasn't already been suggested.
|
|
||||||
3. Be detailed but to the point.
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Feature Description
|
|
||||||
placeholder: |
|
|
||||||
I would like to request ...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: screenshots
|
|
||||||
attributes:
|
|
||||||
label: Screenshots
|
|
||||||
description: |
|
|
||||||
If possible, provide screenshots.
|
|
||||||
Want a feature placed in a specific location? Mark it in a screenshot.
|
|
||||||
Want something modified? Try creating a mockup.
|
|
||||||
The more details about how it should look, the better.
|
|
||||||
Not required, but appreciated.
|
|
||||||
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -7,15 +7,16 @@
|
|||||||
This text will remain hidden when you submit your pull request.
|
This text will remain hidden when you submit your pull request.
|
||||||
|
|
||||||
For your pull request title, use the format:
|
For your pull request title, use the format:
|
||||||
[BUG]: Brief title of the bug being fixed
|
[BUG]: Brief title of the bug being fixed
|
||||||
[FEATURE]: Brief title of the feature being added
|
[FEATURE]: Brief title of the feature being added
|
||||||
|
[DOCS]: Brief title of the feature being added
|
||||||
|
|
||||||
Failure to follow the above title format will result in your PR being ignored.
|
Failure to follow the above title format will result in your PR being ignored.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# Pull Request
|
# Pull Request
|
||||||
<small>Checkmark which topic best describes your contribution:</small>
|
<small>Select which topic best describes your contribution:</small>
|
||||||
|
|
||||||
- [ ] Feature
|
- [ ] Feature
|
||||||
- [ ] Bug
|
- [ ] Bug
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
### Before You Submit
|
### Before You Submit
|
||||||
<small>Please ensure you check the following items to indicate that you've read this section and completed each task</small>
|
<small>Please ensure you check the following items to indicate that you've read this section and completed each task</small>
|
||||||
|
|
||||||
- [ ] My code follows the [Contribution Guidelines](https://github.com/Aetherinox/thetvapp-docker/blob/main/CONTRIBUTING.md)
|
- [ ] My code follows the [Contribution Guidelines](https://github.com/https://github.com/iFlip721/tvapp2/blob/main/CONTRIBUTING.md)
|
||||||
- [ ] I give expressed consent for my work to be used in this repo
|
- [ ] I give expressed consent for my work to be used in this repo
|
||||||
- [ ] I have tested my work and it functions as intended
|
- [ ] I have tested my work and it functions as intended
|
||||||
- [ ] I have included documentation if the change requires such
|
- [ ] I have included documentation if the change requires such
|
||||||
|
|||||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
# #
|
# #
|
||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2025 Aetherinox
|
# Copyright (c) 2024-2025 Aetherinox
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
26
.github/labeler.yml
vendored
26
.github/labeler.yml
vendored
@@ -1,3 +1,27 @@
|
|||||||
|
# #
|
||||||
|
# MIT License
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024-2025 Aetherinox
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
# #
|
||||||
|
|
||||||
# Number of labels to fetch (optional). Defaults to 100
|
# Number of labels to fetch (optional). Defaults to 100
|
||||||
numLabels: 40
|
numLabels: 40
|
||||||
# These labels will not be used even if the issue contains them (optional).
|
# These labels will not be used even if the issue contains them (optional).
|
||||||
@@ -42,4 +66,4 @@ custom:
|
|||||||
keywords:
|
keywords:
|
||||||
- '[request]'
|
- '[request]'
|
||||||
labels:
|
labels:
|
||||||
- Type ◦ Feature
|
- Type ◦ Feature
|
||||||
|
|||||||
157
.github/workflows/deploy-clean.yml
vendored
157
.github/workflows/deploy-clean.yml
vendored
@@ -1,8 +1,15 @@
|
|||||||
# #
|
# #
|
||||||
# @type github workflow
|
# @type github workflow
|
||||||
# @desc cleans up the list of deployments in the environment history
|
# @desc cleans up the list of deployments in the environment history
|
||||||
|
# edit the 'environment:' to determine which deployment to keep clean
|
||||||
|
# - can be ran manually
|
||||||
# @author Aetherinox
|
# @author Aetherinox
|
||||||
# @url https://github.com/Aetherinox
|
# @url https://github.com/Aetherinox
|
||||||
|
#
|
||||||
|
# @secrets secrets.SELF_TOKEN_CL Github Access Token (Classic)
|
||||||
|
# secrets.DISCORD_WEBHOOK_CHAN_TVAPP2_WORKFLOWS Discord Webbhook URL; right-click on channel, click "Integrations"
|
||||||
|
# #
|
||||||
|
|
||||||
# #
|
# #
|
||||||
|
|
||||||
name: "⚙️ Deploy › Clean"
|
name: "⚙️ Deploy › Clean"
|
||||||
@@ -14,15 +21,40 @@ run-name: "⚙️ Deploy › Clean"
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Deployment Environment Name
|
||||||
|
#
|
||||||
|
# this is the name of the deployment item
|
||||||
|
# #
|
||||||
|
|
||||||
|
DEPLOYMENT_ENV:
|
||||||
|
description: '📦 Deployment Environment'
|
||||||
|
required: true
|
||||||
|
default: 'orion'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Delay
|
||||||
|
#
|
||||||
|
# Milliseconds to wait between cleaning up each action in history. Avoids secondary rate limit. Default: 500
|
||||||
|
# #
|
||||||
|
|
||||||
|
DEPLOYMENT_DELAY:
|
||||||
|
description: '🕛 Delete Delay'
|
||||||
|
required: true
|
||||||
|
default: '1000'
|
||||||
|
type: string
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# environment variables
|
# environment variables
|
||||||
# #
|
# #
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BOT_NAME_1: AdminServ
|
DEPLOYMENT_ENV: ${{ github.event.inputs.DEPLOYMENT_ENV || 'orion' }}
|
||||||
BOT_NAME_2: AdminServX
|
DEPLOYMENT_DELAY: ${{ github.event.inputs.DEPLOYMENT_DELAY || '1000' }}
|
||||||
BOT_NAME_3: EuropaServ
|
BOT_NAME_1: EuropaServ
|
||||||
BOT_NAME_DEPENDABOT: dependabot[bot]
|
BOT_NAME_DEPENDABOT: dependabot[bot]
|
||||||
LABELS_JSON: |
|
LABELS_JSON: |
|
||||||
[
|
[
|
||||||
@@ -94,13 +126,126 @@ jobs:
|
|||||||
cleanup:
|
cleanup:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions: write-all
|
permissions: write-all
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Cleanup › Set Env Variables
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: >-
|
||||||
|
🕛 Get Timestamp
|
||||||
|
id: task_cleanup_set_timestamp
|
||||||
|
run: |
|
||||||
|
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV
|
||||||
|
echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV
|
||||||
|
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV
|
||||||
|
echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Checkout › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: >-
|
||||||
|
✅ Checkout
|
||||||
|
id: task_cleanup_gh_checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Cleanup › Start
|
||||||
|
# #
|
||||||
|
|
||||||
- name: >-
|
- name: >-
|
||||||
⚙️ Deployments › Clean
|
⚙️ Deployments › Clean
|
||||||
|
id: task_cleanup_start
|
||||||
uses: Aetherinox/delete-deploy-env-action@v3
|
uses: Aetherinox/delete-deploy-env-action@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.SELF_TOKEN_CL }}
|
token: ${{ secrets.SELF_TOKEN_CL }}
|
||||||
environment: orion
|
environment: '${{ env.DEPLOYMENT_ENV }}'
|
||||||
onlyRemoveDeployments: true
|
onlyRemoveDeployments: true
|
||||||
delay: "1000"
|
delay: "${{ env.DEPLOYMENT_DELAY }}"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Cleanup › Get Weekly Commits
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: >-
|
||||||
|
🕛 Get Weekly Commit List
|
||||||
|
id: task_cleanup_set_weekly_commit_list
|
||||||
|
run: |
|
||||||
|
echo 'WEEKLY_COMMITS<<EOF' >> $GITHUB_ENV
|
||||||
|
git log --format="[\`%h\`](${{ github.server_url }}/${{ github.repository }}/commit/%H) %s - %an" --since=7.days >> $GITHUB_ENV
|
||||||
|
echo 'EOF' >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Cleanup › Notify Github › Success
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: >-
|
||||||
|
🔔 Send Discord Webhook Message (Success)
|
||||||
|
id: task_cleanup_notify_discord_success
|
||||||
|
uses: tsickert/discord-webhook@v6.0.0
|
||||||
|
if: success()
|
||||||
|
with:
|
||||||
|
username: 'Io'
|
||||||
|
avatar-url: 'https://i.imgur.com/8BVDkla.jpg'
|
||||||
|
webhook-url: ${{ secrets.DISCORD_WEBHOOK_CHAN_TVAPP2_WORKFLOWS }}
|
||||||
|
embed-title: "**Deployment Cleanup Workflow Ran**"
|
||||||
|
embed-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||||
|
embed-thumbnail-url: 'https://i.imgur.com/zDIzE8T.jpg'
|
||||||
|
embed-description: |
|
||||||
|
## 📦 Deployment Cleanup ${{ job.status == 'success' && '✅' || '❌' }}
|
||||||
|
|
||||||
|
A **successful** deployment cleanup was triggered on your repository. The history for this environment has been wiped
|
||||||
|
and will no longer list previous deployments you've made.
|
||||||
|
|
||||||
|
- Environment: `${{ env.DEPLOYMENT_ENV }}`
|
||||||
|
- Cleanup Delay: `${{ env.DEPLOYMENT_DELAY }}`
|
||||||
|
- Workflow: `${{ github.workflow }} (#${{github.run_number}})`
|
||||||
|
- Triggered By: ${{ github.actor }}
|
||||||
|
- Status: `${{ job.status == 'success' && '✅ Successful' || '❌ Failed' }}`
|
||||||
|
|
||||||
|
embed-color: ${{ job.status == 'success' && '5763719' || '15418782' }}
|
||||||
|
embed-footer-text: "Completed at ${{ env.NOW }} UTC"
|
||||||
|
embed-timestamp: "${{ env.NOW_LONG }}"
|
||||||
|
embed-author-name: "${{steps.embed.outputs.EMBED_AUTHOR_NAME}}"
|
||||||
|
embed-author-url: "${{ github.event.release.author.html_url }}"
|
||||||
|
embed-author-icon-url: "${{ github.event.release.author.avatar_url }}"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Cleanup › Notify Github › Failure
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: >-
|
||||||
|
🔔 Send Discord Webhook Message (Failure)
|
||||||
|
id: task_cleanup_notify_discord_failure
|
||||||
|
uses: tsickert/discord-webhook@v6.0.0
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
username: 'Io'
|
||||||
|
avatar-url: 'https://i.imgur.com/8BVDkla.jpg'
|
||||||
|
webhook-url: ${{ secrets.DISCORD_WEBHOOK_CHAN_TVAPP2_WORKFLOWS }}
|
||||||
|
embed-title: "**Deployment Cleanup Workflow Ran**"
|
||||||
|
embed-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||||
|
embed-thumbnail-url: 'https://i.imgur.com/zDIzE8T.jpg'
|
||||||
|
embed-description: |
|
||||||
|
## 📦 Deployment Cleanup ${{ job.status == 'success' && '✅' || '❌' }}
|
||||||
|
|
||||||
|
A **failed** deployment cleanup was triggered on your repository. Since the action failed; no entries of your repo's
|
||||||
|
deployment history have been removed.
|
||||||
|
|
||||||
|
- Environment: `${{ env.DEPLOYMENT_ENV }}`
|
||||||
|
- Cleanup Delay: `${{ env.DEPLOYMENT_DELAY }}`
|
||||||
|
- Workflow: `${{ github.workflow }} (#${{github.run_number}})`
|
||||||
|
- Triggered By: ${{ github.actor }}
|
||||||
|
- Status: `${{ job.status == 'success' && '✅ Successful' || '❌ Failed' }}`
|
||||||
|
|
||||||
|
embed-color: ${{ env.STATUS == 'success' && '5763719' || '15418782' }}
|
||||||
|
embed-footer-text: "Completed at ${{ env.NOW }} UTC"
|
||||||
|
embed-timestamp: "${{ env.NOW_LONG }}"
|
||||||
|
embed-author-name: "${{steps.embed.outputs.EMBED_AUTHOR_NAME}}"
|
||||||
|
embed-author-url: "${{ github.event.release.author.html_url }}"
|
||||||
|
embed-author-icon-url: "${{ github.event.release.author.avatar_url }}"
|
||||||
|
|
||||||
|
|||||||
523
.github/workflows/deploy-docker-dockerhub.yml
vendored
Normal file
523
.github/workflows/deploy-docker-dockerhub.yml
vendored
Normal file
@@ -0,0 +1,523 @@
|
|||||||
|
# #
|
||||||
|
# @type github workflow
|
||||||
|
# @author Aetherinox
|
||||||
|
# @url https://github.com/Aetherinox
|
||||||
|
# @usage deploys docker container to Dockerhub
|
||||||
|
# @secrets secrets.ADMINSERV_GPG_KEY_ASC gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK
|
||||||
|
# secrets.ADMINSERV_GPG_PASSPHRASE gpg private key passphrase
|
||||||
|
# secrets.IMAGE_DOCKERHUB_TOKEN hub.docker.com access token
|
||||||
|
# #
|
||||||
|
|
||||||
|
name: "📦 Deploy › Docker › Dockerhub"
|
||||||
|
run-name: "📦 Deploy › Docker › Dockerhub"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Triggers
|
||||||
|
# #
|
||||||
|
|
||||||
|
on:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Trigger › Workflow Dispatch
|
||||||
|
#
|
||||||
|
# If any values are not provided, will use fallback env variable
|
||||||
|
# #
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Image Name
|
||||||
|
#
|
||||||
|
# used in github image path
|
||||||
|
# ${{ env.IMAGE_AUTHOR }}/${{ env.IMAGE_NAME }}
|
||||||
|
# #
|
||||||
|
|
||||||
|
IMAGE_NAME:
|
||||||
|
description: '📦 Image Name'
|
||||||
|
required: true
|
||||||
|
default: 'keeweb'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Image Author
|
||||||
|
#
|
||||||
|
# used in github image path
|
||||||
|
# ${{ env.IMAGE_AUTHOR }}/${{ env.IMAGE_NAME }}
|
||||||
|
# #
|
||||||
|
|
||||||
|
IMAGE_AUTHOR:
|
||||||
|
description: '🪪 Image Author'
|
||||||
|
required: true
|
||||||
|
default: 'antelle'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Image Version
|
||||||
|
#
|
||||||
|
# used to create new release tag, and add version to docker image name
|
||||||
|
# #
|
||||||
|
|
||||||
|
IMAGE_VERSION:
|
||||||
|
description: '🏷️ Image Version'
|
||||||
|
required: true
|
||||||
|
default: '1.19.0'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Image Dockerhub username
|
||||||
|
#
|
||||||
|
# this is the user to sign into Dockerhub as.
|
||||||
|
# #
|
||||||
|
|
||||||
|
IMAGE_DOCKERHUB_USERNAME:
|
||||||
|
description: '🪪 Dockerhub Username'
|
||||||
|
required: true
|
||||||
|
default: 'antelle'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
# #
|
||||||
|
# true no changes to the repo will be made
|
||||||
|
# false workflow will behave normally, and push any changes detected to the files
|
||||||
|
# #
|
||||||
|
|
||||||
|
DRY_RUN:
|
||||||
|
description: '🐛 Dry Run (Debug)'
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
# #
|
||||||
|
# true released version will be marked as a development build and will have the v1.x.x-development tag instead of -latest
|
||||||
|
# false release version will be marked with -latest docker tag
|
||||||
|
# #
|
||||||
|
|
||||||
|
DEV_RELEASE:
|
||||||
|
description: '🧪 Development Release'
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Trigger › Push
|
||||||
|
# #
|
||||||
|
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Environment Vars
|
||||||
|
# #
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE_NAME: ${{ github.event.inputs.IMAGE_NAME || 'keeweb' }}
|
||||||
|
IMAGE_AUTHOR: ${{ github.event.inputs.IMAGE_AUTHOR || 'antelle' }}
|
||||||
|
IMAGE_VERSION: ${{ github.event.inputs.IMAGE_VERSION || '1.19.0' }}
|
||||||
|
IMAGE_DOCKERHUB_USERNAME: ${{ github.event.inputs.IMAGE_DOCKERHUB_USERNAME || 'antelle' }}
|
||||||
|
BOT_NAME_1: EuropaServ
|
||||||
|
BOT_NAME_DEPENDABOT: dependabot[bot]
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Jobs
|
||||||
|
#
|
||||||
|
# The way pushed docker containers on Dockerhub work, the most recent image built goes at the top.
|
||||||
|
# We will use the order below which builds the :latest image last so that it appears at the very
|
||||||
|
# top of the packages page.
|
||||||
|
# #
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Job › Create Tag
|
||||||
|
# #
|
||||||
|
|
||||||
|
job-docker-release-tags-create:
|
||||||
|
name: >-
|
||||||
|
📦 Release › Create Tag
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Tags › Start
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🏳️ Start'
|
||||||
|
id: task_release_tags_start
|
||||||
|
run: |
|
||||||
|
echo "Creating Tag"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Tags › Checkout
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '✅ Checkout'
|
||||||
|
id: task_release_tags_checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Tags › Fix Permissions
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '#️⃣ Manage Permissions'
|
||||||
|
id: task_release_tags_permissions
|
||||||
|
run: |
|
||||||
|
find ./ -name 'run' -exec chmod 755 {} \;
|
||||||
|
WRONG_PERM=$(find ./ -path "./.git" -prune -o \( -name "run" -o -name "finish" -o -name "check" \) -not -perm -u=x,g=x,o=x -print)
|
||||||
|
if [ -n "${WRONG_PERM}" ]; then
|
||||||
|
echo "⚠️⚠️⚠️ Permissions are invalid ⚠️⚠️⚠️"
|
||||||
|
for i in ${WRONG_PERM}; do
|
||||||
|
echo "::error file=${i},line=1,title=Missing Executable Bit::This file needs to be set as executable!"
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "✅✅✅ Executable permissions are OK ✅✅✅"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Tags › Create Tag
|
||||||
|
#
|
||||||
|
# only called in dispatch mode
|
||||||
|
# #
|
||||||
|
|
||||||
|
- uses: rickstaa/action-create-tag@v1
|
||||||
|
id: task_release_tags_create
|
||||||
|
if: ( github.event_name != 'workflow_dispatch' && inputs.DRY_RUN == false )
|
||||||
|
with:
|
||||||
|
tag: "${{ env.IMAGE_VERSION }}"
|
||||||
|
tag_exists_error: false
|
||||||
|
message: '${{ env.IMAGE_NAME }}-${{ env.IMAGE_VERSION }}'
|
||||||
|
gpg_private_key: ${{ secrets.ADMINSERV_GPG_KEY_ASC }}
|
||||||
|
gpg_passphrase: ${{ secrets.ADMINSERV_GPG_PASSPHRASE }}
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Job › Docker Release › Dockerhub › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
job-docker-release-dockerhub-arm64:
|
||||||
|
name: >-
|
||||||
|
📦 Release › Dockerhub › Arm64
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [ job-docker-release-tags-create ]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Start › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🏳️ Start'
|
||||||
|
id: task_release_dh_start
|
||||||
|
run: |
|
||||||
|
echo "Starting Dockerhub arm64"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Checkout › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '✅ Checkout'
|
||||||
|
id: task_release_dh_checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Install Dependencies
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '📦 Install Dependencies'
|
||||||
|
id: task_release_dh_dependencies
|
||||||
|
run:
|
||||||
|
sudo apt-get install -qq dos2unix
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Execute dos2unix
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🔐 Apply dos2unix'
|
||||||
|
id: task_release_dh_dos2unix
|
||||||
|
run: |
|
||||||
|
echo "⚠️⚠️⚠️ Running DOS2UNIX ⚠️⚠️⚠️"
|
||||||
|
find ./ \( -path "./.git" -o -path "./docs" -o -path "./.github" -o -path "*.png" -o -path "*.jpg" \) -prune -o -name '*' -print | xargs dos2unix --
|
||||||
|
echo "✅✅✅ Completed DOS2UNIX ✅✅✅"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Fix Permissions
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '#️⃣ Manage Permissions'
|
||||||
|
id: task_release_dh_permissions
|
||||||
|
run: |
|
||||||
|
find ./ -name 'run' -exec chmod 755 {} \;
|
||||||
|
WRONG_PERM=$(find ./ -path "./.git" -prune -o \( -name "run" -o -name "finish" -o -name "check" \) -not -perm -u=x,g=x,o=x -print)
|
||||||
|
if [ -n "${WRONG_PERM}" ]; then
|
||||||
|
echo "⚠️⚠️⚠️ Permissions are invalid ⚠️⚠️⚠️"
|
||||||
|
for i in ${WRONG_PERM}; do
|
||||||
|
echo "::error file=${i},line=1,title=Missing Executable Bit::This file needs to be set as executable!"
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "✅✅✅ Executable permissions are OK ✅✅✅"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › QEMU › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚙️ Set up QEMU'
|
||||||
|
id: task_release_dh_qemu
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Setup BuildX › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚙️ Setup Buildx'
|
||||||
|
id: task_release_dh_buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
driver-opts: 'image=moby/buildkit:latest'
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Registry Login › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚙️ Login to Dockerhub'
|
||||||
|
id: task_release_dh_registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ env.IMAGE_DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.IMAGE_DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Meta › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🔨 Dockerhub: Meta - Arm64'
|
||||||
|
id: task_release_dh_meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ env.IMAGE_AUTHOR }}/${{ env.IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
# latest no
|
||||||
|
type=raw,value=latest,enable=false
|
||||||
|
|
||||||
|
# dispatch add x1.x.x-arm64
|
||||||
|
type=raw,enable=${{ github.event_name == 'workflow_dispatch' && inputs.DEV_RELEASE == false }},priority=300,prefix=,suffix=-arm64,value=${{ env.IMAGE_VERSION }}
|
||||||
|
|
||||||
|
# dispatch add arm64-development
|
||||||
|
type=raw,enable=${{ github.event_name == 'workflow_dispatch' && inputs.DEV_RELEASE == true }},priority=300,prefix=,suffix=-development,value=arm64
|
||||||
|
|
||||||
|
# tag add tag-arm64
|
||||||
|
type=ref,enable=${{ github.event_name == 'pull_request' || github.event_name == 'push' }},priority=600,prefix=,suffix=-arm64,event=tag
|
||||||
|
flavor: |
|
||||||
|
latest=false
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Checkpoint › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚠️ Checkpoint'
|
||||||
|
id: task_release_dh_checkpoint
|
||||||
|
run: |
|
||||||
|
echo "registry ............. Github"
|
||||||
|
echo "github.actor.......... ${{ github.actor }}"
|
||||||
|
echo "github.ref ........... ${{ github.ref }}"
|
||||||
|
echo "github.ref_name ...... ${{ github.ref_name }}"
|
||||||
|
echo "github.event_name .... ${{ github.event_name }}"
|
||||||
|
echo "inputs.DRY_RUN ....... ${{ inputs.DRY_RUN }}"
|
||||||
|
echo "env.AUTHOR ........... ${{ env.IMAGE_AUTHOR }}"
|
||||||
|
echo "tags ................. ${{ steps.task_release_dh_meta.outputs.tags }}"
|
||||||
|
echo "labels ............... ${{ steps.task_release_dh_meta.outputs.labels }}"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Build and Push › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '📦 Build & Push (linux/arm64)'
|
||||||
|
id: task_release_dh_push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
if: ( github.event_name == 'workflow_dispatch' && inputs.DRY_RUN == false ) || ( github.event_name == 'push' )
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: Dockerfile.aarch64
|
||||||
|
platforms: linux/arm64
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.task_release_dh_meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.task_release_dh_meta.outputs.labels }}
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Job › Docker Release › Dockerhub › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
job-docker-release-dockerhub-amd64:
|
||||||
|
name: >-
|
||||||
|
📦 Release › Dockerhub › Amd64
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
|
needs: [ job-docker-release-tags-create, job-docker-release-dockerhub-arm64 ]
|
||||||
|
steps:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Start › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🏳️ Start'
|
||||||
|
id: task_release_dh_start
|
||||||
|
run: |
|
||||||
|
echo "Starting Dockerhub docker release"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Checkout › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '✅ Checkout'
|
||||||
|
id: task_release_dh_checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Install Dependencies
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '📦 Install Dependencies'
|
||||||
|
id: task_release_dh_dependencies
|
||||||
|
run:
|
||||||
|
sudo apt-get install -qq dos2unix
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Execute dos2unix
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🔐 Apply dos2unix'
|
||||||
|
id: task_release_dh_dos2unix
|
||||||
|
run: |
|
||||||
|
find ./ \( -path "./.git" -o -path "./docs" -o -path "./.github" -o -path "*.png" -o -path "*.jpg" \) -prune -o -name '*' -print | xargs dos2unix --
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Fix Permissions
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '#️⃣ Manage Permissions'
|
||||||
|
id: task_release_dh_permissions
|
||||||
|
run: |
|
||||||
|
find ./ -name 'run' -exec chmod 755 {} \;
|
||||||
|
WRONG_PERM=$(find ./ -path "./.git" -prune -o \( -name "run" -o -name "finish" -o -name "check" \) -not -perm -u=x,g=x,o=x -print)
|
||||||
|
if [ -n "${WRONG_PERM}" ]; then
|
||||||
|
echo "⚠️⚠️⚠️ Permissions are invalid ⚠️⚠️⚠️"
|
||||||
|
for i in ${WRONG_PERM}; do
|
||||||
|
echo "::error file=${i},line=1,title=Missing Executable Bit::This file needs to be set as executable!"
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "✅✅✅ Executable permissions are OK ✅✅✅"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › QEMU › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚙️ Set up QEMU'
|
||||||
|
id: task_release_dh_qemu
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Setup BuildX › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚙️ Setup Buildx'
|
||||||
|
id: task_release_dh_buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
driver-opts: 'image=moby/buildkit:latest'
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Registry Login › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚙️ Login to Dockerhub'
|
||||||
|
id: task_release_dh_registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ env.IMAGE_DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.IMAGE_DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Meta › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🔨 Dockerhub: Meta - Amd64'
|
||||||
|
id: task_release_dh_meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ env.IMAGE_AUTHOR }}/${{ env.IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
# latest yes
|
||||||
|
type=raw,value=latest,enable=${{ !inputs.DEV_RELEASE }}
|
||||||
|
|
||||||
|
# dispatch add x1.x.x-amd64
|
||||||
|
type=raw,enable=${{ github.event_name == 'workflow_dispatch' && inputs.DEV_RELEASE == false }},priority=300,prefix=,suffix=-amd64,value=${{ env.IMAGE_VERSION }}
|
||||||
|
|
||||||
|
# dispatch add amd64-development
|
||||||
|
type=raw,enable=${{ github.event_name == 'workflow_dispatch' && inputs.DEV_RELEASE == true }},priority=300,prefix=,suffix=-development,value=amd64
|
||||||
|
|
||||||
|
# tag add tag-arm64
|
||||||
|
type=ref,enable=${{ github.event_name == 'pull_request' || github.event_name == 'push'}},priority=600,prefix=,suffix=-amd64,event=tag
|
||||||
|
|
||||||
|
# add development tag
|
||||||
|
type=raw,enable=${{ inputs.DEV_RELEASE }},priority=400,prefix=,suffix=,value=development
|
||||||
|
flavor: |
|
||||||
|
latest=${{ !inputs.DEV_RELEASE }}
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Checkpoint › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚠️ Checkpoint'
|
||||||
|
id: task_release_dh_checkpoint
|
||||||
|
run: |
|
||||||
|
echo "registry ............. Github"
|
||||||
|
echo "github.actor.......... ${{ github.actor }}"
|
||||||
|
echo "github.ref ........... ${{ github.ref }}"
|
||||||
|
echo "github.ref_name ...... ${{ github.ref_name }}"
|
||||||
|
echo "github.event_name .... ${{ github.event_name }}"
|
||||||
|
echo "inputs.DRY_RUN ....... ${{ inputs.DRY_RUN }}"
|
||||||
|
echo "env.AUTHOR ........... ${{ env.IMAGE_AUTHOR }}"
|
||||||
|
echo "tags ................. ${{ steps.task_release_dh_meta.outputs.tags }}"
|
||||||
|
echo "labels ............... ${{ steps.task_release_dh_meta.outputs.labels }}"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Dockerhub › Build and Push › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '📦 Build & Push (linux/amd64)'
|
||||||
|
id: task_release_dh_push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
if: ( github.event_name == 'workflow_dispatch' && inputs.DRY_RUN == false ) || ( github.event_name == 'push' )
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: Dockerfile
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.task_release_dh_meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.task_release_dh_meta.outputs.labels }}
|
||||||
674
.github/workflows/deploy-docker-github.yml
vendored
Normal file
674
.github/workflows/deploy-docker-github.yml
vendored
Normal file
@@ -0,0 +1,674 @@
|
|||||||
|
# #
|
||||||
|
# @type github workflow
|
||||||
|
# @author Aetherinox
|
||||||
|
# @url https://github.com/Aetherinox
|
||||||
|
# @usage deploys docker container to github and send message to discord
|
||||||
|
# upload this workflow to both the `main` branch of the tvapp repository
|
||||||
|
# @secrets secrets.ADMINSERV_GPG_KEY_ASC gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK
|
||||||
|
# secrets.ADMINSERV_GPG_PASSPHRASE gpg private key passphrase
|
||||||
|
# secrets.IMAGE_GHCR_TOKEN github personal access token (classic) with package:write permission
|
||||||
|
# secrets.DISCORD_WEBHOOK_CHAN_TVAPP2_RELEASES Discord webhook to report releases from github to discord
|
||||||
|
# #
|
||||||
|
|
||||||
|
name: "📦 Deploy › Docker › Github"
|
||||||
|
run-name: "📦 Deploy › Docker › Github"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Triggers
|
||||||
|
# #
|
||||||
|
|
||||||
|
on:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Trigger › Workflow Dispatch
|
||||||
|
#
|
||||||
|
# If any values are not provided, will use fallback env variable
|
||||||
|
# #
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Image Name
|
||||||
|
#
|
||||||
|
# used in github image path
|
||||||
|
# ghcr.io/${{ env.IMAGE_AUTHOR }}/${{ env.IMAGE_NAME }}
|
||||||
|
# #
|
||||||
|
|
||||||
|
IMAGE_NAME:
|
||||||
|
description: '📦 Image Name'
|
||||||
|
required: true
|
||||||
|
default: 'tvapp2'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Image Author
|
||||||
|
#
|
||||||
|
# used in github image path
|
||||||
|
# ghcr.io/${{ env.IMAGE_AUTHOR }}/${{ env.IMAGE_NAME }}
|
||||||
|
# #
|
||||||
|
|
||||||
|
IMAGE_AUTHOR:
|
||||||
|
description: '🪪 Image Author'
|
||||||
|
required: true
|
||||||
|
default: 'Aetherinox'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Image Version
|
||||||
|
#
|
||||||
|
# used to create new release tag, and add version to docker image name
|
||||||
|
# #
|
||||||
|
|
||||||
|
IMAGE_VERSION:
|
||||||
|
description: '🏷️ Image Version'
|
||||||
|
required: true
|
||||||
|
default: '1.0.0'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Image ghcr username
|
||||||
|
#
|
||||||
|
# this is the user to sign into ghcr as.
|
||||||
|
# #
|
||||||
|
|
||||||
|
IMAGE_GHCR_USERNAME:
|
||||||
|
description: '🪪 ghcr.io Username'
|
||||||
|
required: true
|
||||||
|
default: 'Aetherinox'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
# #
|
||||||
|
# true no changes to the repo will be made
|
||||||
|
# false workflow will behave normally, and push any changes detected to the files
|
||||||
|
# #
|
||||||
|
|
||||||
|
DRY_RUN:
|
||||||
|
description: '🐛 Dry Run (Debug)'
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
# #
|
||||||
|
# true released version will be marked as a development build and will have the v1.x.x-development tag instead of -latest
|
||||||
|
# false release version will be marked with -latest docker tag
|
||||||
|
# #
|
||||||
|
|
||||||
|
DEV_RELEASE:
|
||||||
|
description: '🧪 Development Release'
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Trigger › Push
|
||||||
|
# #
|
||||||
|
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Environment Vars
|
||||||
|
# #
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE_NAME: ${{ github.event.inputs.IMAGE_NAME || 'tvapp2' }}
|
||||||
|
IMAGE_AUTHOR: ${{ github.event.inputs.IMAGE_AUTHOR || 'Aetherinox' }}
|
||||||
|
IMAGE_VERSION: ${{ github.event.inputs.IMAGE_VERSION || '1.0.0' }}
|
||||||
|
IMAGE_GHCR_USERNAME: ${{ github.event.inputs.IMAGE_GHCR_USERNAME || 'Aetherinox' }}
|
||||||
|
BOT_NAME_1: EuropaServ
|
||||||
|
BOT_NAME_DEPENDABOT: dependabot[bot]
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Jobs
|
||||||
|
#
|
||||||
|
# The way pushed docker containers on Github work, the most recent image built goes at the top.
|
||||||
|
# We will use the order below which builds the :latest image last so that it appears at the very
|
||||||
|
# top of the packages page.
|
||||||
|
# #
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Job › Create Tag
|
||||||
|
# #
|
||||||
|
|
||||||
|
job-docker-release-tags-create:
|
||||||
|
name: >-
|
||||||
|
📦 Release › Create Tag
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Tags › Start
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🏳️ Start'
|
||||||
|
id: task_release_tags_start
|
||||||
|
run: |
|
||||||
|
echo "Creating Tag"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Tags › Checkout
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '✅ Checkout'
|
||||||
|
id: task_release_tags_checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Tags › Fix Permissions
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '#️⃣ Manage Permissions'
|
||||||
|
id: task_release_tags_permissions
|
||||||
|
run: |
|
||||||
|
find ./ -name 'run' -exec chmod 755 {} \;
|
||||||
|
WRONG_PERM=$(find ./ -path "./.git" -prune -o \( -name "run" -o -name "finish" -o -name "check" \) -not -perm -u=x,g=x,o=x -print)
|
||||||
|
if [ -n "${WRONG_PERM}" ]; then
|
||||||
|
echo "⚠️⚠️⚠️ Permissions are invalid ⚠️⚠️⚠️"
|
||||||
|
for i in ${WRONG_PERM}; do
|
||||||
|
echo "::error file=${i},line=1,title=Missing Executable Bit::This file needs to be set as executable!"
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "✅✅✅ Executable permissions are OK ✅✅✅"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Tags › Create Tag
|
||||||
|
#
|
||||||
|
# only called in dispatch mode
|
||||||
|
# #
|
||||||
|
|
||||||
|
- uses: rickstaa/action-create-tag@v1
|
||||||
|
id: task_release_tags_create
|
||||||
|
if: ( github.event_name != 'workflow_dispatch' && inputs.DRY_RUN == false )
|
||||||
|
with:
|
||||||
|
tag: "${{ env.IMAGE_VERSION }}"
|
||||||
|
tag_exists_error: false
|
||||||
|
message: '${{ env.IMAGE_NAME }}-${{ env.IMAGE_VERSION }}'
|
||||||
|
gpg_private_key: ${{ secrets.ADMINSERV_GPG_KEY_ASC }}
|
||||||
|
gpg_passphrase: ${{ secrets.ADMINSERV_GPG_PASSPHRASE }}
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Job › Docker Release › Github › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
job-docker-release-github-arm64:
|
||||||
|
name: >-
|
||||||
|
📦 Release › Github › Arm64
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [ job-docker-release-tags-create ]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Start › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🏳️ Start'
|
||||||
|
id: task_release_gh_start
|
||||||
|
run: |
|
||||||
|
echo "Starting Github Docker arm64"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Get Timestamp
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🕛 Get Timestamp'
|
||||||
|
id: task_release_set_timestamp
|
||||||
|
run: |
|
||||||
|
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV
|
||||||
|
echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV
|
||||||
|
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV
|
||||||
|
echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Checkout › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '✅ Checkout'
|
||||||
|
id: task_release_gh_checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Install Dependencies
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '📦 Install Dependencies'
|
||||||
|
id: task_release_gh_dependencies
|
||||||
|
run:
|
||||||
|
sudo apt-get install -qq dos2unix
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Execute dos2unix
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🔐 Apply dos2unix'
|
||||||
|
id: task_release_gh_dos2unix
|
||||||
|
run: |
|
||||||
|
echo "⚠️⚠️⚠️ Running DOS2UNIX ⚠️⚠️⚠️"
|
||||||
|
find ./ \( -path "./.git" -o -path "./docs" -o -path "./.github" -o -path "*.png" -o -path "*.jpg" \) -prune -o -name '*' -print | xargs dos2unix --
|
||||||
|
echo "✅✅✅ Completed DOS2UNIX ✅✅✅"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Fix Permissions
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '#️⃣ Manage Permissions'
|
||||||
|
id: task_release_gh_permissions
|
||||||
|
run: |
|
||||||
|
find ./ -name 'run' -exec chmod 755 {} \;
|
||||||
|
WRONG_PERM=$(find ./ -path "./.git" -prune -o \( -name "run" -o -name "finish" -o -name "check" \) -not -perm -u=x,g=x,o=x -print)
|
||||||
|
if [ -n "${WRONG_PERM}" ]; then
|
||||||
|
echo "⚠️⚠️⚠️ Permissions are invalid ⚠️⚠️⚠️"
|
||||||
|
for i in ${WRONG_PERM}; do
|
||||||
|
echo "::error file=${i},line=1,title=Missing Executable Bit::This file needs to be set as executable!"
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "✅✅✅ Executable permissions are OK ✅✅✅"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › QEMU › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚙️ Set up QEMU'
|
||||||
|
id: task_release_gh_qemu
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Setup BuildX › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚙️ Setup Buildx'
|
||||||
|
id: task_release_gh_buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
driver-opts: 'image=moby/buildkit:latest'
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Registry Login › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚙️ Login to Github'
|
||||||
|
id: task_release_gh_registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ env.IMAGE_GHCR_USERNAME }}
|
||||||
|
password: ${{ secrets.IMAGE_GHCR_TOKEN }}
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Meta › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🔨 Github: Meta - Arm64'
|
||||||
|
id: task_release_gh_meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
ghcr.io/${{ env.IMAGE_AUTHOR }}/${{ env.IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
# latest no
|
||||||
|
type=raw,value=latest,enable=false
|
||||||
|
# dispatch add x1.x.x-arm64
|
||||||
|
type=raw,enable=${{ github.event_name == 'workflow_dispatch' && inputs.DEV_RELEASE == false }},priority=300,prefix=,suffix=-arm64,value=${{ env.IMAGE_VERSION }}
|
||||||
|
# dispatch add arm64-development
|
||||||
|
type=raw,enable=${{ github.event_name == 'workflow_dispatch' && inputs.DEV_RELEASE == true }},priority=300,prefix=,suffix=-development,value=arm64
|
||||||
|
# tag add tag-arm64
|
||||||
|
type=ref,enable=${{ github.event_name == 'pull_request' || github.event_name == 'push' }},priority=600,prefix=,suffix=-arm64,event=tag
|
||||||
|
flavor: |
|
||||||
|
latest=false
|
||||||
|
labels: |
|
||||||
|
org.opencontainers.image.VERSION=${{ env.IMAGE_VERSION }}
|
||||||
|
org.opencontainers.image.BUILDDATE=${{ env.NOW_DOCKER_LABEL }}
|
||||||
|
org.opencontainers.image.licenses=MIT
|
||||||
|
org.opencontainers.image.revision=${{ github.sha }}
|
||||||
|
org.opencontainers.image.vendor=${{ env.IMAGE_AUTHOR }}
|
||||||
|
org.opencontainers.image.ref.name=${{ env.GIT_REF }}
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Checkpoint › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚠️ Checkpoint'
|
||||||
|
id: task_release_gh_checkpoint
|
||||||
|
run: |
|
||||||
|
echo "registry ............. Github"
|
||||||
|
echo "github.actor.......... ${{ github.actor }}"
|
||||||
|
echo "github.ref ........... ${{ github.ref }}"
|
||||||
|
echo "github.ref_name ...... ${{ github.ref_name }}"
|
||||||
|
echo "github.event_name .... ${{ github.event_name }}"
|
||||||
|
echo "inputs.DRY_RUN ....... ${{ inputs.DRY_RUN }}"
|
||||||
|
echo "env.AUTHOR ........... ${{ env.IMAGE_AUTHOR }}"
|
||||||
|
echo "tags ................. ${{ steps.task_release_gh_meta.outputs.tags }}"
|
||||||
|
echo "labels ............... ${{ steps.task_release_gh_meta.outputs.labels }}"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Build and Push › Arm64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '📦 Build & Push (linux/arm64)'
|
||||||
|
id: task_release_gh_push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
if: ( github.event_name == 'workflow_dispatch' && inputs.DRY_RUN == false ) || ( github.event_name == 'push' )
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: Dockerfile.aarch64
|
||||||
|
platforms: linux/arm64
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.task_release_gh_meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.task_release_gh_meta.outputs.labels }}
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Get Weekly Commits
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🕛 Get Weekly Commit List'
|
||||||
|
id: task_release_set_weekly_commit_list
|
||||||
|
run: |
|
||||||
|
echo 'WEEKLY_COMMITS<<EOF' >> $GITHUB_ENV
|
||||||
|
git log --format="[\`%h\`](${{ github.server_url }}/${{ github.repository }}/commit/%H) %s - %an" --since=7.days >> $GITHUB_ENV
|
||||||
|
echo 'EOF' >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Notify Github
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🔔 Send Discord Webhook Message'
|
||||||
|
uses: tsickert/discord-webhook@v6.0.0
|
||||||
|
if: success()
|
||||||
|
with:
|
||||||
|
username: 'Io'
|
||||||
|
avatar-url: 'https://i.imgur.com/8BVDkla.jpg'
|
||||||
|
webhook-url: ${{ secrets.DISCORD_WEBHOOK_CHAN_TVAPP2_RELEASES }}
|
||||||
|
embed-title: "📦 **Deploy › Docker › Github Workflow Ran**"
|
||||||
|
embed-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||||
|
embed-thumbnail-url: 'https://i.imgur.com/zDIzE8T.jpg'
|
||||||
|
embed-description: |
|
||||||
|
## 📦 Docker › Deploy ${{ job.status == 'success' && '✅' || '❌' }} › `${{ env.IMAGE_NAME }}-${{ env.IMAGE_VERSION }}`
|
||||||
|
|
||||||
|
A new version of the docker container `${{ env.IMAGE_NAME }}` has been released from Github. The image is available at:
|
||||||
|
- https://github.com/${{ github.repository }}/pkgs/container/${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
|
- Docker Image: `${{ env.IMAGE_NAME }}-${{ env.IMAGE_VERSION }}`
|
||||||
|
- Version: `${{ env.IMAGE_VERSION }}`
|
||||||
|
- Pull URL: https://ghcr.io/${{ env.IMAGE_AUTHOR }}/${{ env.IMAGE_NAME }}
|
||||||
|
- Branch: `${{ github.ref_name }}`
|
||||||
|
- Workflow: `${{ github.workflow }} (#${{github.run_number}})`
|
||||||
|
- Triggered By: `${{ github.actor }}`
|
||||||
|
|
||||||
|
### Tags
|
||||||
|
-# This docker image will use the following tags:
|
||||||
|
|
||||||
|
```
|
||||||
|
${{ steps.task_release_gh_meta.outputs.tags }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Labels
|
||||||
|
-# This docker image embeds the following labels:
|
||||||
|
|
||||||
|
```
|
||||||
|
${{ steps.task_release_gh_meta.outputs.labels }}
|
||||||
|
```
|
||||||
|
embed-color: ${{ job.status == 'success' && '5763719' || '15418782' }}
|
||||||
|
embed-footer-text: "Completed at ${{ env.NOW }} UTC"
|
||||||
|
embed-timestamp: "${{ env.NOW_LONG }}"
|
||||||
|
embed-author-name: "${{ github.event.release.author.name }}"
|
||||||
|
embed-author-url: "${{ github.event.release.author.html_url }}"
|
||||||
|
embed-author-icon-url: "${{ github.event.release.author.avatar_url }}"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Job › Docker Release › Github › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
job-docker-release-github-amd64:
|
||||||
|
name: >-
|
||||||
|
📦 Release › Github › Amd64
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
|
needs: [ job-docker-release-tags-create, job-docker-release-github-arm64 ]
|
||||||
|
steps:
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Start › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🏳️ Start'
|
||||||
|
id: task_release_gh_start
|
||||||
|
run: |
|
||||||
|
echo "Starting Github docker release"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Get Timestamp
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🕛 Get Timestamp'
|
||||||
|
id: task_release_set_timestamp
|
||||||
|
run: |
|
||||||
|
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV
|
||||||
|
echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV
|
||||||
|
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV
|
||||||
|
echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Checkout › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '✅ Checkout'
|
||||||
|
id: task_release_gh_checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Install Dependencies
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '📦 Install Dependencies'
|
||||||
|
id: task_release_gh_dependencies
|
||||||
|
run:
|
||||||
|
sudo apt-get install -qq dos2unix
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Execute dos2unix
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🔐 Apply dos2unix'
|
||||||
|
id: task_release_gh_dos2unix
|
||||||
|
run: |
|
||||||
|
find ./ \( -path "./.git" -o -path "./docs" -o -path "./.github" -o -path "*.png" -o -path "*.jpg" \) -prune -o -name '*' -print | xargs dos2unix --
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Fix Permissions
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '#️⃣ Manage Permissions'
|
||||||
|
id: task_release_gh_permissions
|
||||||
|
run: |
|
||||||
|
find ./ -name 'run' -exec chmod 755 {} \;
|
||||||
|
WRONG_PERM=$(find ./ -path "./.git" -prune -o \( -name "run" -o -name "finish" -o -name "check" \) -not -perm -u=x,g=x,o=x -print)
|
||||||
|
if [ -n "${WRONG_PERM}" ]; then
|
||||||
|
echo "⚠️⚠️⚠️ Permissions are invalid ⚠️⚠️⚠️"
|
||||||
|
for i in ${WRONG_PERM}; do
|
||||||
|
echo "::error file=${i},line=1,title=Missing Executable Bit::This file needs to be set as executable!"
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "✅✅✅ Executable permissions are OK ✅✅✅"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › QEMU › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚙️ Set up QEMU'
|
||||||
|
id: task_release_gh_qemu
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Setup BuildX › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚙️ Setup Buildx'
|
||||||
|
id: task_release_gh_buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
driver-opts: 'image=moby/buildkit:latest'
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Registry Login › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚙️ Login to Github'
|
||||||
|
id: task_release_gh_registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ env.IMAGE_GHCR_USERNAME }}
|
||||||
|
password: ${{ secrets.IMAGE_GHCR_TOKEN }}
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Meta › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🔨 Github: Meta - Amd64'
|
||||||
|
id: task_release_gh_meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
ghcr.io/${{ env.IMAGE_AUTHOR }}/${{ env.IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
# latest yes
|
||||||
|
type=raw,value=latest,enable=${{ !inputs.DEV_RELEASE }}
|
||||||
|
# dispatch add x1.x.x-amd64
|
||||||
|
type=raw,enable=${{ github.event_name == 'workflow_dispatch' && inputs.DEV_RELEASE == false }},priority=300,prefix=,suffix=-amd64,value=${{ env.IMAGE_VERSION }}
|
||||||
|
# dispatch add amd64-development
|
||||||
|
type=raw,enable=${{ github.event_name == 'workflow_dispatch' && inputs.DEV_RELEASE == true }},priority=300,prefix=,suffix=-development,value=amd64
|
||||||
|
# tag add tag-arm64
|
||||||
|
type=ref,enable=${{ github.event_name == 'pull_request' || github.event_name == 'push'}},priority=600,prefix=,suffix=-amd64,event=tag
|
||||||
|
# add development tag
|
||||||
|
type=raw,enable=${{ inputs.DEV_RELEASE }},priority=400,prefix=,suffix=,value=development
|
||||||
|
flavor: |
|
||||||
|
latest=${{ !inputs.DEV_RELEASE }}
|
||||||
|
labels: |
|
||||||
|
org.opencontainers.image.VERSION=${{ env.IMAGE_VERSION }}
|
||||||
|
org.opencontainers.image.BUILDDATE=${{ env.NOW_DOCKER_LABEL }}
|
||||||
|
org.opencontainers.image.licenses=MIT
|
||||||
|
org.opencontainers.image.revision=${{ github.sha }}
|
||||||
|
org.opencontainers.image.vendor=${{ env.IMAGE_AUTHOR }}
|
||||||
|
org.opencontainers.image.ref.name=${{ env.GIT_REF }}
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Checkpoint › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '⚠️ Checkpoint'
|
||||||
|
id: task_release_gh_checkpoint
|
||||||
|
run: |
|
||||||
|
echo "registry ............. Github"
|
||||||
|
echo "github.actor.......... ${{ github.actor }}"
|
||||||
|
echo "github.ref ........... ${{ github.ref }}"
|
||||||
|
echo "github.ref_name ...... ${{ github.ref_name }}"
|
||||||
|
echo "github.event_name .... ${{ github.event_name }}"
|
||||||
|
echo "inputs.DRY_RUN ....... ${{ inputs.DRY_RUN }}"
|
||||||
|
echo "env.AUTHOR ........... ${{ env.IMAGE_AUTHOR }}"
|
||||||
|
echo "tags ................. ${{ steps.task_release_gh_meta.outputs.tags }}"
|
||||||
|
echo "labels ............... ${{ steps.task_release_gh_meta.outputs.labels }}"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Github › Build and Push › Amd64
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '📦 Build & Push (linux/amd64)'
|
||||||
|
id: task_release_gh_push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
if: ( github.event_name == 'workflow_dispatch' && inputs.DRY_RUN == false ) || ( github.event_name == 'push' )
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: Dockerfile
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.task_release_gh_meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.task_release_gh_meta.outputs.labels }}
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Get Weekly Commits
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🕛 Get Weekly Commit List'
|
||||||
|
id: task_release_set_weekly_commit_list
|
||||||
|
run: |
|
||||||
|
echo 'WEEKLY_COMMITS<<EOF' >> $GITHUB_ENV
|
||||||
|
git log --format="[\`%h\`](${{ github.server_url }}/${{ github.repository }}/commit/%H) %s - %an" --since=7.days >> $GITHUB_ENV
|
||||||
|
echo 'EOF' >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Release › Notify Github
|
||||||
|
# #
|
||||||
|
|
||||||
|
- name: '🔔 Send Discord Webhook Message'
|
||||||
|
uses: tsickert/discord-webhook@v6.0.0
|
||||||
|
if: success()
|
||||||
|
with:
|
||||||
|
username: 'Io'
|
||||||
|
avatar-url: 'https://i.imgur.com/8BVDkla.jpg'
|
||||||
|
webhook-url: ${{ secrets.DISCORD_WEBHOOK_CHAN_TVAPP2_RELEASES }}
|
||||||
|
embed-title: "📦 **Deploy › Docker › Github Workflow Ran**"
|
||||||
|
embed-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||||
|
embed-thumbnail-url: 'https://i.imgur.com/zDIzE8T.jpg'
|
||||||
|
embed-description: |
|
||||||
|
## 📦 Docker › Deploy ${{ job.status == 'success' && '✅' || '❌' }} › `${{ env.IMAGE_NAME }}-${{ env.IMAGE_VERSION }}`
|
||||||
|
|
||||||
|
A new version of the docker container `${{ env.IMAGE_NAME }}` has been released from Github. The image is available at:
|
||||||
|
- https://github.com/${{ github.repository }}/pkgs/container/${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
|
- Docker Image: `${{ env.IMAGE_NAME }}-${{ env.IMAGE_VERSION }}`
|
||||||
|
- Version: `${{ env.IMAGE_VERSION }}`
|
||||||
|
- Pull URL: https://ghcr.io/${{ env.IMAGE_AUTHOR }}/${{ env.IMAGE_NAME }}
|
||||||
|
- Branch: `${{ github.ref_name }}`
|
||||||
|
- Workflow: `${{ github.workflow }} (#${{github.run_number}})`
|
||||||
|
- Triggered By: `${{ github.actor }}`
|
||||||
|
|
||||||
|
### Tags
|
||||||
|
-# This docker image will use the following tags:
|
||||||
|
|
||||||
|
```
|
||||||
|
${{ steps.task_release_gh_meta.outputs.tags }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Labels
|
||||||
|
-# This docker image embeds the following labels:
|
||||||
|
|
||||||
|
```
|
||||||
|
${{ steps.task_release_gh_meta.outputs.labels }}
|
||||||
|
```
|
||||||
|
embed-color: ${{ job.status == 'success' && '5763719' || '15418782' }}
|
||||||
|
embed-footer-text: "Completed at ${{ env.NOW }} UTC"
|
||||||
|
embed-timestamp: "${{ env.NOW_LONG }}"
|
||||||
|
embed-author-name: "${{ github.event.release.author.name }}"
|
||||||
|
embed-author-url: "${{ github.event.release.author.html_url }}"
|
||||||
|
embed-author-icon-url: "${{ github.event.release.author.avatar_url }}"
|
||||||
516
.github/workflows/deploy-docker.yml
vendored
516
.github/workflows/deploy-docker.yml
vendored
@@ -1,516 +0,0 @@
|
|||||||
# #
|
|
||||||
# @type github workflow
|
|
||||||
# @desc deploys docker container
|
|
||||||
# @author Aetherinox
|
|
||||||
# @url https://github.com/Aetherinox
|
|
||||||
# #
|
|
||||||
|
|
||||||
name: "⚙️ Deploy › Docker › Main"
|
|
||||||
run-name: "⚙️ Deploy › Docker › Main"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# triggers
|
|
||||||
# #
|
|
||||||
|
|
||||||
on:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Trigger > Workflow Dispatch
|
|
||||||
# #
|
|
||||||
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
|
|
||||||
IMAGE_NAME:
|
|
||||||
description: "📦 Image Name"
|
|
||||||
required: true
|
|
||||||
default: 'thetvapp-docker'
|
|
||||||
type: string
|
|
||||||
|
|
||||||
IMAGE_AUTHOR:
|
|
||||||
description: "📦 Image Author"
|
|
||||||
required: true
|
|
||||||
default: 'aetherinox'
|
|
||||||
type: string
|
|
||||||
|
|
||||||
# #
|
|
||||||
# true: runs all actions, even ones not scheduled
|
|
||||||
# false: only scheduled tasks will run
|
|
||||||
# #
|
|
||||||
|
|
||||||
PRINT_ONLY:
|
|
||||||
description: "📑 Print Debugs Only"
|
|
||||||
required: true
|
|
||||||
default: false
|
|
||||||
type: boolean
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Trigger > Push
|
|
||||||
# #
|
|
||||||
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- '*'
|
|
||||||
|
|
||||||
# #
|
|
||||||
# environment variables
|
|
||||||
# #
|
|
||||||
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: alpine-base
|
|
||||||
IMAGE_AUTHOR: Aetherinox
|
|
||||||
BOT_NAME_1: EuropaServ
|
|
||||||
BOT_NAME_DEPENDABOT: dependabot[bot]
|
|
||||||
|
|
||||||
# #
|
|
||||||
# jobs
|
|
||||||
#
|
|
||||||
# The way pushed docker containers on Github work, the most recent image built goes at the top.
|
|
||||||
# We will use the order below which builds the :latest image last so that it appears at the very
|
|
||||||
# top of the packages page.
|
|
||||||
# #
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job > Docker Release > Github
|
|
||||||
# #
|
|
||||||
|
|
||||||
job-docker-release-github-php:
|
|
||||||
name: >-
|
|
||||||
📦 Release › Github › PHP
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
attestations: write
|
|
||||||
id-token: write
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Start
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "✅ Start"
|
|
||||||
id: task_release_gh_start
|
|
||||||
run: |
|
|
||||||
echo "Starting Github docker release for image PHP"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Checkout
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "☑️ Checkout"
|
|
||||||
id: task_release_gh_checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > QEMU
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "⚙️ Set up QEMU"
|
|
||||||
id: task_release_gh_qemu
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Setup BuildX
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "⚙️ Setup Buildx"
|
|
||||||
id: task_release_gh_buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
driver-opts: 'image=moby/buildkit:v0.10.5'
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Registry Login
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "⚙️ Login to Github"
|
|
||||||
id: task_release_gh_registry
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.SELF_TOKEN_CL }}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Meta
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "🔨 Docker meta"
|
|
||||||
id: task_release_gh_meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
ghcr.io/${{ inputs.IMAGE_AUTHOR || env.IMAGE_AUTHOR }}/docker-${{ inputs.IMAGE_NAME || env.IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=ref,enable=true,priority=600,prefix=,suffix=-php,event=tag
|
|
||||||
flavor: |
|
|
||||||
latest=false
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Debug
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "🪪 Debug › Print"
|
|
||||||
id: task_release_gh_print
|
|
||||||
run: |
|
|
||||||
echo "registry ............. Github"
|
|
||||||
echo "github.actor.......... ${{ github.actor }}"
|
|
||||||
echo "github.ref ........... ${{ github.ref }}"
|
|
||||||
echo "github.event_name .... ${{ github.event_name }}"
|
|
||||||
echo "tags ................. ${{ steps.task_release_gh_meta.outputs.tags }}"
|
|
||||||
echo "labels ............... ${{ steps.task_release_gh_meta.outputs.labels }}"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Build and Push
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "📦 Build and push"
|
|
||||||
id: task_release_gh_push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
if: ( github.event_name == 'workflow_dispatch' && inputs.PRINT_ONLY == 'false' ) || ( github.event_name == 'push' )
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: Dockerfile-php.template
|
|
||||||
platforms: linux/amd64
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.task_release_gh_meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.task_release_gh_meta.outputs.labels }}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job > Docker Release > Github
|
|
||||||
# #
|
|
||||||
|
|
||||||
job-docker-release-dockerhub-php:
|
|
||||||
name: >-
|
|
||||||
📦 Release › Dockerhub › PHP
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
attestations: write
|
|
||||||
id-token: write
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Start
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "✅ Start"
|
|
||||||
id: task_release_dh_start
|
|
||||||
run: |
|
|
||||||
echo "Starting Dockerhub Release"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Checkout
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "☑️ Checkout"
|
|
||||||
id: task_release_dh_checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > QEMU
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "⚙️ Set up QEMU"
|
|
||||||
id: task_release_dh_qemu
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Setup BuildX
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "⚙️ Setup Buildx"
|
|
||||||
id: task_release_dh_buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
driver-opts: 'image=moby/buildkit:v0.10.5'
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Registry Login
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "⚙️ Login to DockerHub"
|
|
||||||
id: task_release_dh_registry
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ inputs.IMAGE_AUTHOR || env.IMAGE_AUTHOR }}
|
|
||||||
password: ${{ secrets.SELF_DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Meta
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "🔨 Docker meta"
|
|
||||||
id: task_release_dh_meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
${{ inputs.IMAGE_AUTHOR || env.IMAGE_AUTHOR }}/${{ inputs.IMAGE_NAME || env.IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=raw,value=latest,enable=false
|
|
||||||
type=ref,enable=true,priority=600,prefix=,suffix=-php,event=tag
|
|
||||||
flavor: |
|
|
||||||
latest=false
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Debug
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "🪪 Debug › Print"
|
|
||||||
id: task_release_dh_print
|
|
||||||
run: |
|
|
||||||
echo "registry ............. Dockerhub"
|
|
||||||
echo "github.actor.......... ${{ github.actor }}"
|
|
||||||
echo "github.ref ........... ${{ github.ref }}"
|
|
||||||
echo "github.event_name .... ${{ github.event_name }}"
|
|
||||||
echo "tags ................. ${{ steps.task_release_dh_meta.outputs.tags }}"
|
|
||||||
echo "labels ............... ${{ steps.task_release_dh_meta.outputs.labels }}"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Build and Push
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "📦 Build and push"
|
|
||||||
id: task_release_dh_push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
if: ( github.event_name == 'workflow_dispatch' && inputs.PRINT_ONLY == 'false' ) || ( github.event_name == 'push' )
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: Dockerfile-php.template
|
|
||||||
platforms: linux/amd64
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.task_release_dh_meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.task_release_dh_meta.outputs.labels }}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job > Docker Release > Github
|
|
||||||
# #
|
|
||||||
|
|
||||||
job-docker-release-github-main:
|
|
||||||
name: >-
|
|
||||||
📦 Release › Github › Main
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
attestations: write
|
|
||||||
id-token: write
|
|
||||||
needs: [ job-docker-release-github-php, job-docker-release-dockerhub-php ]
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Start
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "✅ Start"
|
|
||||||
id: task_release_gh_start
|
|
||||||
run: |
|
|
||||||
echo "Starting Github docker release"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Checkout
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "☑️ Checkout"
|
|
||||||
id: task_release_gh_checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > QEMU
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "⚙️ Set up QEMU"
|
|
||||||
id: task_release_gh_qemu
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Setup BuildX
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "⚙️ Setup Buildx"
|
|
||||||
id: task_release_gh_buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
driver-opts: 'image=moby/buildkit:v0.10.5'
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Registry Login
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "⚙️ Login to Github"
|
|
||||||
id: task_release_gh_registry
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.SELF_TOKEN_CL }}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Meta
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "🔨 Docker meta"
|
|
||||||
id: task_release_gh_meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
ghcr.io/${{ inputs.IMAGE_AUTHOR || env.IMAGE_AUTHOR }}/docker-${{ inputs.IMAGE_NAME || env.IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=raw,value=latest,enable=${{ endsWith(github.ref, 'main') }}
|
|
||||||
type=ref,event=tag
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Debug
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "🪪 Debug › Print"
|
|
||||||
id: task_release_gh_print
|
|
||||||
run: |
|
|
||||||
echo "registry ............. Github"
|
|
||||||
echo "github.actor.......... ${{ github.actor }}"
|
|
||||||
echo "github.ref ........... ${{ github.ref }}"
|
|
||||||
echo "github.event_name .... ${{ github.event_name }}"
|
|
||||||
echo "tags ................. ${{ steps.task_release_gh_meta.outputs.tags }}"
|
|
||||||
echo "labels ............... ${{ steps.task_release_gh_meta.outputs.labels }}"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Github > Build and Push
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "📦 Build and push"
|
|
||||||
id: task_release_gh_push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
if: ( github.event_name == 'workflow_dispatch' && inputs.PRINT_ONLY == 'false' ) || ( github.event_name == 'push' )
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: Dockerfile
|
|
||||||
platforms: linux/amd64
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.task_release_gh_meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.task_release_gh_meta.outputs.labels }}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job > Docker Release > Github
|
|
||||||
# #
|
|
||||||
|
|
||||||
job-docker-release-dockerhub-main:
|
|
||||||
name: >-
|
|
||||||
📦 Release › Dockerhub › Main
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
attestations: write
|
|
||||||
id-token: write
|
|
||||||
needs: [ job-docker-release-github-php, job-docker-release-dockerhub-php ]
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Start
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "✅ Start"
|
|
||||||
id: task_release_dh_start
|
|
||||||
run: |
|
|
||||||
echo "Starting Github docker release"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Checkout
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "☑️ Checkout"
|
|
||||||
id: task_release_dh_checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > QEMU
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "⚙️ Set up QEMU"
|
|
||||||
id: task_release_dh_qemu
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Setup BuildX
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "⚙️ Setup Buildx"
|
|
||||||
id: task_release_dh_buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
driver-opts: 'image=moby/buildkit:v0.10.5'
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Registry Login
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "⚙️ Login to DockerHub"
|
|
||||||
id: task_release_dh_registry
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ inputs.IMAGE_AUTHOR || env.IMAGE_AUTHOR }}
|
|
||||||
password: ${{ secrets.SELF_DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Meta
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "🔨 Docker meta"
|
|
||||||
id: task_release_dh_meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
${{ inputs.IMAGE_AUTHOR || env.IMAGE_AUTHOR }}/${{ inputs.IMAGE_NAME || env.IMAGE_NAME }}
|
|
||||||
tags: |
|
|
||||||
type=raw,value=latest,enable=${{ endsWith(github.ref, 'main') }}
|
|
||||||
type=ref,event=tag
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Debug
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "🪪 Debug › Print"
|
|
||||||
id: task_release_dh_print
|
|
||||||
run: |
|
|
||||||
echo "registry ............. Dockerhub"
|
|
||||||
echo "github.actor.......... ${{ github.actor }}"
|
|
||||||
echo "github.ref ........... ${{ github.ref }}"
|
|
||||||
echo "github.event_name .... ${{ github.event_name }}"
|
|
||||||
echo "tags ................. ${{ steps.task_release_dh_meta.outputs.tags }}"
|
|
||||||
echo "labels ............... ${{ steps.task_release_dh_meta.outputs.labels }}"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Release > Dockerhub > Build and Push
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "📦 Build & Push"
|
|
||||||
id: task_release_dh_push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
if: ( github.event_name == 'workflow_dispatch' && inputs.PRINT_ONLY == 'false' ) || ( github.event_name == 'push' )
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: Dockerfile
|
|
||||||
platforms: linux/amd64
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.task_release_dh_meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.task_release_dh_meta.outputs.labels }}
|
|
||||||
72
.github/workflows/issues-accept.yml
vendored
72
.github/workflows/issues-accept.yml
vendored
@@ -1,72 +0,0 @@
|
|||||||
# #
|
|
||||||
# @type github workflow
|
|
||||||
# @desc adds a label to a PR when the command "/accept" is typed in the issue comments
|
|
||||||
# do not attempt to use env variables in if condition.
|
|
||||||
# do not accept to change GITHUB_TOKEN.
|
|
||||||
# @author Aetherinox
|
|
||||||
# @url https://github.com/Aetherinox
|
|
||||||
# #
|
|
||||||
|
|
||||||
name: "🎫 Issue › Accept"
|
|
||||||
run-name: "🎫 Issue › Accept"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# triggers
|
|
||||||
# #
|
|
||||||
|
|
||||||
on:
|
|
||||||
issue_comment:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
# #
|
|
||||||
# environment variables
|
|
||||||
# #
|
|
||||||
|
|
||||||
env:
|
|
||||||
LABEL_ACCEPT: "Status 𐄂 Accepted"
|
|
||||||
|
|
||||||
BOT_NAME_1: AdminServ
|
|
||||||
BOT_NAME_2: AdminServX
|
|
||||||
BOT_NAME_3: EuropaServ
|
|
||||||
BOT_NAME_DEPENDABOT: dependabot[bot]
|
|
||||||
|
|
||||||
# #
|
|
||||||
# jobs
|
|
||||||
# #
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job [ Deploy ]
|
|
||||||
# #
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
if: contains(github.event.comment.body, '/accept') && github.event.comment.user.login == 'Aetherinox'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Add Label to accepted PR
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
🏷️ Assign Label › ${{ env.LABEL_ACCEPT }}
|
|
||||||
run: gh issue edit "$NUMBER" --add-label "$LABELS"
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
GH_REPO: ${{ github.repository }}
|
|
||||||
NUMBER: ${{ github.event.issue.number }}
|
|
||||||
LABELS: ${{ env.LABEL_ACCEPT }}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Add assignee to accepted PR
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
🏷️ Assign Assignee › ${{ github.repository_owner }}
|
|
||||||
run: gh issue edit "$NUMBER" --add-assignee "$ASSIGNEE"
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
GH_REPO: ${{ github.repository }}
|
|
||||||
NUMBER: ${{ github.event.issue.number }}
|
|
||||||
ASSIGNEE: ${{ github.repository_owner }}
|
|
||||||
891
.github/workflows/issues-new.yml
vendored
891
.github/workflows/issues-new.yml
vendored
@@ -1,891 +0,0 @@
|
|||||||
# #
|
|
||||||
# @type github workflow
|
|
||||||
# @desc searches a new issues title and body for certain keywords and assigns a label
|
|
||||||
# sets the assignee for the issue to the repository owner
|
|
||||||
# @author Aetherinox
|
|
||||||
# @url https://github.com/Aetherinox
|
|
||||||
#
|
|
||||||
# requires the following labels to be created in your repo:
|
|
||||||
# - bug
|
|
||||||
# - feature
|
|
||||||
# - urgent
|
|
||||||
# - roadmap
|
|
||||||
# #
|
|
||||||
|
|
||||||
name: "🎫 Issue › New"
|
|
||||||
run-name: "🎫 Issue › New › ${{ github.event.issue.number }}: ${{ github.event.issue.title }}"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# triggers
|
|
||||||
# #
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types:
|
|
||||||
- reopened
|
|
||||||
- opened
|
|
||||||
|
|
||||||
# #
|
|
||||||
# environment variables
|
|
||||||
# #
|
|
||||||
|
|
||||||
env:
|
|
||||||
PREFIX_BUG: "Bug"
|
|
||||||
PREFIX_DEPENDENCY: "Dependency"
|
|
||||||
PREFIX_DOCS: "Docs"
|
|
||||||
PREFIX_FEATURE: "Feature"
|
|
||||||
PREFIX_GIT: "Git Action"
|
|
||||||
PREFIX_PR: "PR"
|
|
||||||
PREFIX_ROADMAP: "Roadmap"
|
|
||||||
PREFIX_INTERNAL: "Internal"
|
|
||||||
PREFIX_URGENT: "Urgent"
|
|
||||||
|
|
||||||
LABEL_BUG: "Type ◦ Bug"
|
|
||||||
LABEL_DEPENDENCY: "Type ◦ Dependency"
|
|
||||||
LABEL_DOCS: "Type ◦ Docs"
|
|
||||||
LABEL_FEATURE: "Type ◦ Feature"
|
|
||||||
LABEL_GIT: "Type ◦ Git Action"
|
|
||||||
LABEL_PR: "Type ◦ Pull Request"
|
|
||||||
LABEL_ROADMAP: "Type ◦ Roadmap"
|
|
||||||
LABEL_INTERNAL: "Type ◦ Git Action"
|
|
||||||
LABEL_URGENT: "⚠ Urgent"
|
|
||||||
|
|
||||||
BOT_NAME_1: AdminServ
|
|
||||||
BOT_NAME_2: AdminServX
|
|
||||||
BOT_NAME_3: EuropaServ
|
|
||||||
BOT_NAME_DEPENDABOT: dependabot[bot]
|
|
||||||
|
|
||||||
LABELS_JSON: |
|
|
||||||
[
|
|
||||||
{ "name": "AC › Changes Made", "color": "8F1784", "description": "Requested changes have been made and are pending a re-scan" },
|
|
||||||
{ "name": "AC › Changes Required", "color": "8F1784", "description": "Requires changes to be made to the package before being accepted" },
|
|
||||||
{ "name": "AC › Failed", "color": "a61f2d", "description": "Autocheck failed to run through a complete cycle, requires investigation" },
|
|
||||||
{ "name": "AC › Needs Rebase", "color": "8F1784", "description": "Due to the permissions on the requesting repo, this pull request must be rebased by the author" },
|
|
||||||
{ "name": "AC › Passed", "color": "146b4a", "description": "Ready to be reviewed" },
|
|
||||||
{ "name": "AC › Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" },
|
|
||||||
{ "name": "AC › Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" },
|
|
||||||
{ "name": "AC › Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" },
|
|
||||||
{ "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" },
|
|
||||||
{ "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" },
|
|
||||||
{ "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" },
|
|
||||||
{ "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" },
|
|
||||||
{ "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" },
|
|
||||||
{ "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" },
|
|
||||||
{ "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" },
|
|
||||||
{ "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" },
|
|
||||||
{ "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" },
|
|
||||||
{ "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" },
|
|
||||||
{ "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" },
|
|
||||||
{ "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" },
|
|
||||||
{ "name": "Type ◦ Bug", "color": "9a2c2c", "description": "Something isn't working" },
|
|
||||||
{ "name": "Type ◦ Dependency", "color": "243759", "description": "Item is associated to dependency" },
|
|
||||||
{ "name": "Type ◦ Docs", "color": "0e588d", "description": "Improvements or modifications to docs" },
|
|
||||||
{ "name": "Type ◦ Feature", "color": "3c4e93", "description": "Feature request" },
|
|
||||||
{ "name": "Type ◦ Git Action", "color": "030406", "description": "GitHub Action / workflow" },
|
|
||||||
{ "name": "Type ◦ Pull Request", "color": "8F1784", "description": "Normal pull request" },
|
|
||||||
{ "name": "Type ◦ Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" },
|
|
||||||
{ "name": "Type ◦ Internal", "color": "A51994", "description": "Assigned items are for internal developer use" },
|
|
||||||
{ "name": "Build ◦ Desktop", "color": "c7ca4a", "description": "Specific to desktop" },
|
|
||||||
{ "name": "Build ◦ Linux", "color": "c7ca4a", "description": "Specific to Linux" },
|
|
||||||
{ "name": "Build ◦ MacOS", "color": "c7ca4a", "description": "Specific to MacOS" },
|
|
||||||
{ "name": "Build ◦ Mobile", "color": "c7ca4a", "description": "Specific to mobile" },
|
|
||||||
{ "name": "Build ◦ Web", "color": "c7ca4a", "description": "Specific to web" },
|
|
||||||
{ "name": "Build ◦ Windows", "color": "c7ca4a", "description": "Specific to Windows" },
|
|
||||||
{ "name": "› API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" },
|
|
||||||
{ "name": "› Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" },
|
|
||||||
{ "name": "› Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" },
|
|
||||||
{ "name": "› Customization", "color": "E3F0FC", "description": "Customizations: plugins, themes, configs" },
|
|
||||||
{ "name": "› Design", "color": "FA70DE", "description": "Design related queries" },
|
|
||||||
{ "name": "› Dist", "color": "FA70DE", "description": "Installers and other forms of software distribution" },
|
|
||||||
{ "name": "› Enterprise", "color": "11447a", "description": "Issues about collaboration, administration, and so on" },
|
|
||||||
{ "name": "› Hardware", "color": "5a7503", "description": "YubiKey, other tokens, biometrics" },
|
|
||||||
{ "name": "› Import/Export", "color": "F5FFCC", "description": "Import from and export to different file formats" },
|
|
||||||
{ "name": "› Improvement", "color": "185c98", "description": "Enhance an existing feature" },
|
|
||||||
{ "name": "› Performance", "color": "006b75", "description": "Web and desktop performance issues" },
|
|
||||||
{ "name": "› Plugin Request", "color": "FCE9CA", "description": "Requested changes should be implemented as a plugin" },
|
|
||||||
{ "name": "› Security", "color": "F75D39", "description": "Security issues" },
|
|
||||||
{ "name": "› Self-Hosting", "color": "fad8c7", "description": "Self-hosting installations and configs" },
|
|
||||||
{ "name": "› Storage", "color": "5319e7", "description": "Storage providers: Dropbox, Google, WebDAV, etc." },
|
|
||||||
{ "name": "› Updater", "color": "1BADDE", "description": "Auto-updater issues" },
|
|
||||||
{ "name": "› UX", "color": "1BADDE", "description": "UX and usability" },
|
|
||||||
{ "name": "› Website", "color": "fef2c0", "description": "Website related issues" },
|
|
||||||
{ "name": "⚠ Urgent", "color": "a8740e", "description": "Requires urgent attention" },
|
|
||||||
{ "name": "⚠ Announcement", "color": "DB4712", "description": "Announcements" },
|
|
||||||
{ "name": "📰 Progress Report", "color": "392297", "description": "Development updates" },
|
|
||||||
{ "name": "📦 Release", "color": "277542", "description": "Release announcements" },
|
|
||||||
{ "name": "✔️ Poll", "color": "972255", "description": "Community polls" },
|
|
||||||
{ "name": "❔ Question", "color": "FFFFFF", "description": "All questions" }
|
|
||||||
]
|
|
||||||
|
|
||||||
# #
|
|
||||||
# jobs
|
|
||||||
# #
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job [ Verify / Create Labels ]
|
|
||||||
#
|
|
||||||
# This job will ensure you have labels already created in your repo.
|
|
||||||
# All labels come from the JSON table LABELS_JSON.
|
|
||||||
# #
|
|
||||||
|
|
||||||
job-labels-create:
|
|
||||||
name: >-
|
|
||||||
🎫 Labels › Verify Existing
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Create Labels ] Start
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
✅ Start
|
|
||||||
id: task_label_create_start
|
|
||||||
run: |
|
|
||||||
echo "Assigning labels and assignees"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Create Labels ] Checkout
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
☑️ Checkout
|
|
||||||
id: task_label_create_checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Create Labels ] Verify Existing Labels
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
🏷️ Verify Existing Labels
|
|
||||||
id: task_label_create_verify
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
script: |
|
|
||||||
const labels = JSON.parse( process.env.LABELS_JSON );
|
|
||||||
for ( const label of labels )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await github.rest.issues.createLabel(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
name: label.name,
|
|
||||||
description: label.description || '',
|
|
||||||
color: label.color
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch ( err )
|
|
||||||
{
|
|
||||||
if ( err.status === 422 )
|
|
||||||
{
|
|
||||||
console.log( `Label '${label.name}' already exists. Skipping.` );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
console.error( `Error creating label '${label.name}': ${err}` );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job [ Assign Labels ]
|
|
||||||
# #
|
|
||||||
|
|
||||||
job-assign-labels:
|
|
||||||
name: >-
|
|
||||||
🏷️ Labels › Assign
|
|
||||||
needs:
|
|
||||||
- job-labels-create
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: 'read'
|
|
||||||
id-token: 'write'
|
|
||||||
issues: 'write'
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Assign > Get Issue Title
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
🏷️ Get Issue Title
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
id: task_get_title
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
script: |
|
|
||||||
let iss_title = `${ context.payload.issue.title }`;
|
|
||||||
|
|
||||||
core.setOutput( 'issue_title', iss_title )
|
|
||||||
core.info( `Setting env issue title: ${ iss_title }` )
|
|
||||||
|
|
||||||
console.log( "\n\n" )
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Labels > Bugs
|
|
||||||
#
|
|
||||||
# Title of issue is carried over from the previous step.
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
🏷️ ${{ env.PREFIX_BUG }} › Assignment
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
id: task_issues_bugs
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
script: |
|
|
||||||
|
|
||||||
const issueLabels = await github.rest.issues.listLabelsOnIssue(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number
|
|
||||||
});
|
|
||||||
|
|
||||||
let add_labels = issueLabels.data.map( label => label.name );
|
|
||||||
|
|
||||||
let iss_title = `${{ steps.task_get_title.outputs.issue_title }}` || `${ context.payload.issue.title }`;
|
|
||||||
let iss_body = `${ context.payload.issue.body }`;
|
|
||||||
let iss_author = `${ context.payload.issue.user.login }`;
|
|
||||||
|
|
||||||
const iss_title_lc = iss_title.toLowerCase( );
|
|
||||||
|
|
||||||
console.log( "Bug Title ..................... " + iss_title )
|
|
||||||
console.log( "Bug Output .................... " + `${{ steps.task_get_title.outputs.issue_title }}` )
|
|
||||||
console.log( "Bug Payload ................... " + `${ context.payload.issue.title }` )
|
|
||||||
|
|
||||||
/*
|
|
||||||
Tags
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bug_tag = `${{ env.PREFIX_BUG }}:`;
|
|
||||||
const bug_lbl = `${{ env.LABEL_BUG }}`;
|
|
||||||
const feat_tag = `${{ env.PREFIX_FEATURE }}:`;
|
|
||||||
const feat_lbl = `${{ env.LABEL_FEATURE }}`;
|
|
||||||
const urgn_tag = `${{ env.PREFIX_URGENT }}:`;
|
|
||||||
const urgn_lbl = `${{ env.LABEL_URGENT }}`;
|
|
||||||
const road_tag = `${{ env.PREFIX_ROADMAP }}:`;
|
|
||||||
const road_lbl = `${{ env.LABEL_ROADMAP }}`;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Bugs
|
|
||||||
*/
|
|
||||||
|
|
||||||
const words = [ "bug", "broke", "issue", "fail" ];
|
|
||||||
const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Find regex based phrases
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/Z99Gnq/2
|
|
||||||
*/
|
|
||||||
|
|
||||||
const findWordList = /^\b(?:I?\s*have\s*(?:a|an)\s*(?:issue|problem|bug))|(?:will\s*not\s*work)|(?:it\s*is\s*(?:broken|broke|stuck))|(?:found\s*(?:an?|the)\s*(?:bug|issue))|(?:can\s*I\s*fix\s*the\s*(?:bug|issue))|(?:(?:does not|doesn'?t|don'?t|won'?t|can'?t|can\s?not|will\s*not)\s*(?:work|load|function))|(?:it\s*(?:will\s?not|won'?t|can\s?not|can'?t))\s*(?:get|find)\s*the\s*(?:website|site|webpage|page)|(?:the\s*(?:window|frame)\s*is\s*(?:blank|white|empty|missing))\b$/igm;
|
|
||||||
const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) );
|
|
||||||
const bFoundMatchBody = Boolean( findWordList.test( iss_body ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Do not change a title if the item starts with a PR: #
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/JOrqbN/1
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bug_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm;
|
|
||||||
const bug_bFoundPRTitle = Boolean( bug_findPRTitle.test( iss_title ) );
|
|
||||||
|
|
||||||
console.log( "Title Lowercase ............... " + iss_title_lc )
|
|
||||||
console.log( "Startswith " + bug_tag.toLowerCase( ) + "................ " + iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Title Includes Keyword ........ " + bTriggerWordInTitle )
|
|
||||||
console.log( "Title Includes Regex .......... " + bFoundMatchTitle )
|
|
||||||
console.log( "Body Includes Regex ........... " + bFoundMatchBody )
|
|
||||||
console.log( "\n" )
|
|
||||||
|
|
||||||
/*
|
|
||||||
- Check if issue title matches the issue label "Bug:"
|
|
||||||
- Check if title contains word in words
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ( iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) || bTriggerWordInTitle || bFoundMatchTitle || bFoundMatchBody )
|
|
||||||
{
|
|
||||||
|
|
||||||
console.log( "⚠️ " + bug_tag + " ---------------------------------------" )
|
|
||||||
console.log( "Already starts with " + bug_tag + " ......... " + iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Already starts with " + feat_tag + " ..... " + iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Already starts with " + urgn_tag + " ...... " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Already starts with " + road_tag + " ..... " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
|
|
||||||
add_labels.push( `${ bug_lbl }` );
|
|
||||||
|
|
||||||
console.log( `Adding Tag ....................... ${ bug_lbl }` )
|
|
||||||
console.log( "\n" )
|
|
||||||
|
|
||||||
if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` )
|
|
||||||
core.info( `Skipping: Detected ${ iss_author }` )
|
|
||||||
|
|
||||||
// Rename title to contain Bug:
|
|
||||||
// Make sure issue / pr title doesnt already contain a beginning title tag
|
|
||||||
|
|
||||||
if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !bug_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
{
|
|
||||||
console.log( "Renaming Title" )
|
|
||||||
console.log( `Old Title: .................. ${ iss_title }` )
|
|
||||||
|
|
||||||
const title = context.payload.issue.title
|
|
||||||
let title_new = title.replace( /^\s?bug\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?fail\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?issue\s*(.*?)\b/gi, '' );
|
|
||||||
iss_title = `${ bug_tag } ${ title_new }`;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log( `New Title: ...................... ${ iss_title }` )
|
|
||||||
|
|
||||||
await github.rest.issues.update(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number,
|
|
||||||
title: `${ iss_title }`, labels: add_labels
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
core.setOutput( 'issue_title', iss_title )
|
|
||||||
console.log( "\n\n" )
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Labels > Features
|
|
||||||
#
|
|
||||||
# Title of issue is carried over from the previous step.
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
🏷️ ${{ env.PREFIX_FEATURE }} › Assignment
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
id: task_issues_features
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
script: |
|
|
||||||
|
|
||||||
const issueLabels = await github.rest.issues.listLabelsOnIssue(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number
|
|
||||||
});
|
|
||||||
|
|
||||||
let add_labels = issueLabels.data.map( label => label.name );
|
|
||||||
|
|
||||||
let iss_title = `${{ steps.task_issues_bugs.outputs.issue_title }}` || `${ context.payload.issue.title }`;
|
|
||||||
let iss_body = `${ context.payload.issue.body }`;
|
|
||||||
|
|
||||||
const iss_title_lc = iss_title.toLowerCase( );
|
|
||||||
|
|
||||||
console.log( "Feat Title .................... " + iss_title )
|
|
||||||
console.log( "Feat Output ................... " + `${{ steps.task_issues_bugs.outputs.issue_title }}` )
|
|
||||||
console.log( "Feat Payload .................. " + `${ context.payload.issue.title }` )
|
|
||||||
|
|
||||||
/*
|
|
||||||
Tags
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bug_tag = `${{ env.PREFIX_BUG }}:`;
|
|
||||||
const bug_lbl = `${{ env.LABEL_BUG }}`;
|
|
||||||
const feat_tag = `${{ env.PREFIX_FEATURE }}:`;
|
|
||||||
const feat_lbl = `${{ env.LABEL_FEATURE }}`;
|
|
||||||
const urgn_tag = `${{ env.PREFIX_URGENT }}:`;
|
|
||||||
const urgn_lbl = `${{ env.LABEL_URGENT }}`;
|
|
||||||
const road_tag = `${{ env.PREFIX_ROADMAP }}:`;
|
|
||||||
const road_lbl = `${{ env.LABEL_ROADMAP }}`;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Features
|
|
||||||
*/
|
|
||||||
|
|
||||||
const words = [ "feature", "request", "add support" ];
|
|
||||||
const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Find regex based phrases
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/fR1Hm6/1
|
|
||||||
*/
|
|
||||||
|
|
||||||
const findWordList = /^(?:(?:request|include|see)\s*(?:an?|the?)\s*(?:feature|addon|addition|plugin))|(?:(?:add|see|get)\s*support\s*(?:for|with|of))|(?:can\s*we\s*get\s*(?:the|a)\s*(?:ability|feature))|(?:💡 Feature:)$/igm;
|
|
||||||
const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) );
|
|
||||||
const bFoundMatchBody = Boolean( findWordList.test( iss_body ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Do not change a title if the item starts with a PR: #
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/JOrqbN/1
|
|
||||||
*/
|
|
||||||
|
|
||||||
const feat_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm;
|
|
||||||
const feat_bFoundPRTitle = Boolean( feat_findPRTitle.test( iss_title ) );
|
|
||||||
|
|
||||||
console.log( "Title Lowercase ............... " + iss_title_lc )
|
|
||||||
console.log( "Startswith " + feat_tag.toLowerCase( ) + "............ " + iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Title Includes Keyword ........ " + bTriggerWordInTitle )
|
|
||||||
console.log( "Title Includes Regex .......... " + bFoundMatchTitle )
|
|
||||||
console.log( "Body Includes Regex ........... " + bFoundMatchBody )
|
|
||||||
console.log( "\n" )
|
|
||||||
|
|
||||||
/*
|
|
||||||
- Check if issue title matches the issue label "Feature:"
|
|
||||||
- Check if title contains word in words
|
|
||||||
*/
|
|
||||||
|
|
||||||
// change TAG per category
|
|
||||||
if ( iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) || bTriggerWordInTitle || bFoundMatchTitle || bFoundMatchBody )
|
|
||||||
{
|
|
||||||
|
|
||||||
console.log( "⚠️ " + feat_tag + " ---------------------------------------" )
|
|
||||||
console.log( "Already starts with " + bug_tag + " ......... " + iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Already starts with " + feat_tag + " ..... " + iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Already starts with " + urgn_tag + " ...... " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Already starts with " + road_tag + " ..... " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
|
|
||||||
// change LBL per category
|
|
||||||
add_labels.push( `${ feat_lbl }` );
|
|
||||||
|
|
||||||
console.log( `Adding Tag ....................... ${ feat_lbl }` )
|
|
||||||
console.log( "\n" )
|
|
||||||
|
|
||||||
if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` )
|
|
||||||
core.info( `Skipping: Detected ${ iss_author }` )
|
|
||||||
|
|
||||||
// Rename title to contain Feature:
|
|
||||||
// Make sure issue / pr title doesnt already contain a beginning title tag
|
|
||||||
|
|
||||||
if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !feat_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
{
|
|
||||||
console.log( "Renaming Title" )
|
|
||||||
console.log( `Old Title: .................. ${ iss_title }` )
|
|
||||||
|
|
||||||
const title = context.payload.issue.title
|
|
||||||
let title_new = title.replace( /^\s?feature\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?request\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?add(.*?)\s?feature\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?add(.*?)\s?support\s*(.*?)\b/gi, '' );
|
|
||||||
iss_title = `${ feat_tag } ${ title_new }`; // change TAG per category
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log( `New Title: ...................... ${ iss_title }` )
|
|
||||||
|
|
||||||
await github.rest.issues.update(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number,
|
|
||||||
title: `${ iss_title }`, labels: add_labels
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
core.setOutput( 'issue_title', iss_title )
|
|
||||||
console.log( "\n\n" )
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Labels > Urgent
|
|
||||||
#
|
|
||||||
# Title of issue is carried over from the previous step.
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
🏷️ ${{ env.PREFIX_URGENT }} › Assignment
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
id: task_issues_urgent
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
script: |
|
|
||||||
const issueLabels = await github.rest.issues.listLabelsOnIssue(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number
|
|
||||||
});
|
|
||||||
|
|
||||||
let add_labels = issueLabels.data.map( label => label.name );
|
|
||||||
|
|
||||||
let iss_title = `${{ steps.task_issues_features.outputs.issue_title }}` || `${ context.payload.issue.title }`;
|
|
||||||
let iss_body = `${ context.payload.issue.body }`;
|
|
||||||
|
|
||||||
const iss_title_lc = iss_title.toLowerCase( );
|
|
||||||
|
|
||||||
console.log( "Urgn Title .................... " + iss_title )
|
|
||||||
console.log( "Urgn Output ................... " + `${{ steps.task_issues_features.outputs.issue_title }}` )
|
|
||||||
console.log( "Urgn Payload .................. " + `${ context.payload.issue.title }` )
|
|
||||||
|
|
||||||
/*
|
|
||||||
Tags
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bug_tag = `${{ env.PREFIX_BUG }}:`;
|
|
||||||
const bug_lbl = `${{ env.LABEL_BUG }}`;
|
|
||||||
const feat_tag = `${{ env.PREFIX_FEATURE }}:`;
|
|
||||||
const feat_lbl = `${{ env.LABEL_FEATURE }}`;
|
|
||||||
const urgn_tag = `${{ env.PREFIX_URGENT }}:`;
|
|
||||||
const urgn_lbl = `${{ env.LABEL_URGENT }}`;
|
|
||||||
const road_tag = `${{ env.PREFIX_ROADMAP }}:`;
|
|
||||||
const road_lbl = `${{ env.LABEL_ROADMAP }}`;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Urgent
|
|
||||||
*/
|
|
||||||
|
|
||||||
const words = [ "urgent", "urgency", "emergency", "important", "critical" ];
|
|
||||||
const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Find regex based phrases
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/eE9tJX/2
|
|
||||||
*/
|
|
||||||
|
|
||||||
const findWordList = /(?:(?:this)?is\s*a?n?\s*?(?:emergency|urgent|important|vital|acute|crucial|grave|pressing|serious|top.?priority|high.?priority))|(?:reply|respond|answer|write|address)\s*(?:immediate|quick|asap|urgent|now|fast|(?:as)?\s*(?:soon|quick|immediate|fast))(?:ly)?|(?:need\s*(?:help|support|fixed|answer|reply|response)!)|(?:emergency|critical|urgen(?:t|cy)|high.?priority)/igm;
|
|
||||||
const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) );
|
|
||||||
const bFoundMatchBody = Boolean( findWordList.test( iss_body ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Do not change a title if the item starts with a PR: #
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/JOrqbN/1
|
|
||||||
*/
|
|
||||||
|
|
||||||
const urgn_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm;
|
|
||||||
const urgn_bFoundPRTitle = Boolean( urgn_findPRTitle.test( iss_title ) );
|
|
||||||
|
|
||||||
console.log( "Title Lowercase ............... " + iss_title_lc )
|
|
||||||
console.log( "Startswith " + urgn_tag.toLowerCase( ) + "............. " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Title Includes Keyword ........ " + bTriggerWordInTitle )
|
|
||||||
console.log( "Title Includes Regex .......... " + bFoundMatchTitle )
|
|
||||||
console.log( "Body Includes Regex ........... " + bFoundMatchBody )
|
|
||||||
console.log( "\n" )
|
|
||||||
|
|
||||||
/*
|
|
||||||
- Check if issue title matches the issue label "Urgent:"
|
|
||||||
- Check if title contains word in words
|
|
||||||
*/
|
|
||||||
|
|
||||||
// change TAG per category
|
|
||||||
if ( iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) || bTriggerWordInTitle || bFoundMatchTitle || bFoundMatchBody )
|
|
||||||
{
|
|
||||||
|
|
||||||
console.log( "⚠️ " + urgn_tag + " ---------------------------------------" )
|
|
||||||
console.log( "Already starts with " + bug_tag + " ......... " + iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Already starts with " + feat_tag + " ..... " + iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Already starts with " + urgn_tag + " ...... " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Already starts with " + road_tag + " ..... " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
|
|
||||||
// change LBL per category
|
|
||||||
add_labels.push( `${ urgn_lbl }` );
|
|
||||||
|
|
||||||
console.log( `Adding Tag ....................... ${ urgn_lbl }` )
|
|
||||||
console.log( "\n" )
|
|
||||||
|
|
||||||
if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` )
|
|
||||||
core.info( `Skipping: Detected ${ iss_author }` )
|
|
||||||
|
|
||||||
// Rename title to contain Urgent:
|
|
||||||
// Make sure issue / pr title doesnt already contain a beginning title tag
|
|
||||||
|
|
||||||
if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !urgn_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
{
|
|
||||||
console.log( "Renaming Title" )
|
|
||||||
console.log( `Old Title: .................. ${ iss_title }` )
|
|
||||||
|
|
||||||
const title = context.payload.issue.title
|
|
||||||
let title_new = title.replace( /^\s?emergency\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?urgent\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?urgency\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?important\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?critical\s*(.*?)\b/gi, '' );
|
|
||||||
iss_title = `${ urgn_tag } ${ title_new }`; // change TAG per category
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log( `New Title: ...................... ${ iss_title }` )
|
|
||||||
|
|
||||||
await github.rest.issues.update(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number,
|
|
||||||
title: `${ iss_title }`, labels: add_labels
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
core.setOutput( 'issue_title', iss_title )
|
|
||||||
console.log( "\n\n" )
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Labels > Roadmap
|
|
||||||
#
|
|
||||||
# Title of issue is carried over from the previous step.
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
🏷️ ${{ env.PREFIX_ROADMAP }} › Assignment
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
id: task_issues_roadmap
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
script: |
|
|
||||||
const issueLabels = await github.rest.issues.listLabelsOnIssue(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number
|
|
||||||
});
|
|
||||||
|
|
||||||
let add_labels = issueLabels.data.map( label => label.name );
|
|
||||||
|
|
||||||
let iss_title = `${{ steps.task_issues_urgent.outputs.issue_title }}` || `${ context.payload.issue.title }`;
|
|
||||||
let iss_body = `${ context.payload.issue.body }`;
|
|
||||||
|
|
||||||
const iss_title_lc = iss_title.toLowerCase( );
|
|
||||||
|
|
||||||
console.log( "Road Title .................... " + iss_title )
|
|
||||||
console.log( "Road Output ................... " + `${{ steps.task_issues_urgent.outputs.issue_title }}` )
|
|
||||||
console.log( "Road Payload .................. " + `${ context.payload.issue.title }` )
|
|
||||||
|
|
||||||
/*
|
|
||||||
Tags
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bug_tag = `${{ env.PREFIX_BUG }}:`;
|
|
||||||
const bug_lbl = `${{ env.LABEL_BUG }}`;
|
|
||||||
const feat_tag = `${{ env.PREFIX_FEATURE }}:`;
|
|
||||||
const feat_lbl = `${{ env.LABEL_FEATURE }}`;
|
|
||||||
const urgn_tag = `${{ env.PREFIX_URGENT }}:`;
|
|
||||||
const urgn_lbl = `${{ env.LABEL_URGENT }}`;
|
|
||||||
const road_tag = `${{ env.PREFIX_ROADMAP }}:`;
|
|
||||||
const road_lbl = `${{ env.LABEL_ROADMAP }}`;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Roadmap
|
|
||||||
*/
|
|
||||||
|
|
||||||
const words = [ "roadmap", "road map", "planned" ];
|
|
||||||
const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Find regex based phrases
|
|
||||||
Roadmap requires headers #Summary and #Proposal | #Objective
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/ucajBZ/1
|
|
||||||
*/
|
|
||||||
|
|
||||||
const findWordList = /#\s*Summary[\S\s]+#\s*(?:Proposal|Objective)[^\]]+/igm;
|
|
||||||
const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) );
|
|
||||||
const bFoundMatchBody = Boolean( findWordList.test( iss_body ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Do not change a title if the item starts with a PR: #
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/JOrqbN/1
|
|
||||||
*/
|
|
||||||
|
|
||||||
const road_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm;
|
|
||||||
const road_bFoundPRTitle = Boolean( road_findPRTitle.test( iss_title ) );
|
|
||||||
|
|
||||||
console.log( "Title Lowercase ............... " + iss_title_lc )
|
|
||||||
console.log( "Startswith " + road_tag.toLowerCase( ) + "............ " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Title Includes Keyword ........ " + bTriggerWordInTitle )
|
|
||||||
console.log( "Title Includes Regex .......... " + bFoundMatchTitle )
|
|
||||||
console.log( "Body Includes Regex ........... " + bFoundMatchBody )
|
|
||||||
console.log( "\n" )
|
|
||||||
|
|
||||||
/*
|
|
||||||
- Check if issue title matches the issue label "Roadmap:"
|
|
||||||
- Check if title contains word in words
|
|
||||||
*/
|
|
||||||
|
|
||||||
// change TAG per category
|
|
||||||
if ( iss_title_lc.startsWith( road_tag.toLowerCase( ) ) || bTriggerWordInTitle || bFoundMatchTitle || bFoundMatchBody )
|
|
||||||
{
|
|
||||||
|
|
||||||
console.log( "⚠️ " + road_tag + " ---------------------------------------" )
|
|
||||||
console.log( "Already starts with " + bug_tag + " ...... " + iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Already starts with " + feat_tag + " .. " + iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Already starts with " + urgn_tag + " ... " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) )
|
|
||||||
console.log( "Already starts with " + road_tag + " .. " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
|
|
||||||
// change LBL per category
|
|
||||||
add_labels.push( `${ road_lbl }` );
|
|
||||||
|
|
||||||
console.log( `Adding Tag .................... ${ road_lbl }` )
|
|
||||||
console.log( "\n" )
|
|
||||||
|
|
||||||
if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` )
|
|
||||||
core.info( `Skipping: Detected ${ iss_author }` )
|
|
||||||
|
|
||||||
// Rename title to contain Roadmap:
|
|
||||||
// Make sure issue / pr title doesnt already contain a beginning title tag
|
|
||||||
|
|
||||||
if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !road_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
{
|
|
||||||
console.log( "Renaming Title" )
|
|
||||||
console.log( `Old Title: .................. ${ iss_title }` )
|
|
||||||
|
|
||||||
const title = context.payload.issue.title
|
|
||||||
let title_new = title.replace( /^\s?broad(.*?)\s?map\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?planned\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?broadmap\s*(.*?)\b/gi, '' );
|
|
||||||
iss_title = `${ road_tag } ${ title_new }`; // change TAG per category
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log( `New Title: .................... ${ iss_title }` )
|
|
||||||
|
|
||||||
await github.rest.issues.update(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number,
|
|
||||||
title: `${ iss_title }`, labels: add_labels
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
core.setOutput( 'issue_title', iss_title )
|
|
||||||
console.log( "\n\n" )
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job > Phrase Search
|
|
||||||
#
|
|
||||||
# Checks a message for certain keywords and then responds to the user as a reply / comment
|
|
||||||
# #
|
|
||||||
|
|
||||||
job-phrase-search:
|
|
||||||
name: >-
|
|
||||||
🏷️ Labels › Phrase Search
|
|
||||||
needs:
|
|
||||||
- job-labels-create
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: 'read'
|
|
||||||
id-token: 'write'
|
|
||||||
issues: 'write'
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Search Phrase ] Checkout
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
☑️ Prepare
|
|
||||||
id: issues-labels-check-checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Search Phrase ] Search
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
👄 Search Phrases
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
script: |
|
|
||||||
const fs = require( 'fs' );
|
|
||||||
const iss_title = `${ context.payload.issue.title }`;
|
|
||||||
const iss_body = `${ context.payload.issue.body }`;
|
|
||||||
let message = [ "\n<br />\n" ]
|
|
||||||
let bHasMessage = false
|
|
||||||
|
|
||||||
/*********************************************
|
|
||||||
Keyword > Help
|
|
||||||
**********************************************/
|
|
||||||
|
|
||||||
let HE_message =
|
|
||||||
`
|
|
||||||
💡 It appears you might need help, please check the resources below for documentation that might assist with your issue:
|
|
||||||
- [Documentation](${{github.event.repository.url}})
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<sub>I am a bot reaching out to you with an automated response. If the above info doesn't apply to you, please ignore it.</sub>
|
|
||||||
`;
|
|
||||||
|
|
||||||
/*
|
|
||||||
found searched word "for help"
|
|
||||||
append / prepare message for bot to send
|
|
||||||
*/
|
|
||||||
|
|
||||||
const HEfindWordList = /^\b(?:have\s*(?:a|some)?\s*question*s?)|(?:can\s*you\s*(?:tell|help)\s*me)|(?:need\s*(?:some)?\s*(?:help|assistance|guidance))|(?:how\s*can\s*I\s*find)|(?:point\s*me\s*in\s*the\s*direction)|(?:where\s*can\s*I\s*find)|(?:where\s*(?:\N*)\s*(?:\N*)\s*find)|(?:please\s*help)|(?:where\s*\N*\s*(?:located|at))|(?:documentation)\b$/igm;
|
|
||||||
const HEbFoundMatchTitle = Boolean( HEfindWordList.test( iss_title ) );
|
|
||||||
const HEbFoundMatchBody = Boolean( HEfindWordList.test( iss_body ) );
|
|
||||||
|
|
||||||
if ( HEbFoundMatchTitle || HEbFoundMatchBody )
|
|
||||||
{
|
|
||||||
message.push ( HE_message );
|
|
||||||
bHasMessage = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Bot has message to send
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ( bHasMessage == true )
|
|
||||||
{
|
|
||||||
await github.rest.issues.createComment(
|
|
||||||
{
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
body: message.join('\n'),
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job > Add Assignees
|
|
||||||
# #
|
|
||||||
|
|
||||||
job-assign-assignees:
|
|
||||||
name: >-
|
|
||||||
✍️ Issue › Assignees
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [ job-assign-labels ]
|
|
||||||
if: |
|
|
||||||
always()
|
|
||||||
&& contains( needs.*.result, 'success' )
|
|
||||||
&& !contains( needs.*.result, 'failure' )
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Assignees] Assign
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
✍️ Set Assignees
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
script: |
|
|
||||||
const assignees = [ `${{ github.repository_owner }}` ];
|
|
||||||
|
|
||||||
if ( assignees.length > 0 )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await github.rest.issues.addAssignees(
|
|
||||||
{
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
assignees
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch ( error )
|
|
||||||
{
|
|
||||||
core.setFailed( error.message );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1361
.github/workflows/issues-scan.yml
vendored
1361
.github/workflows/issues-scan.yml
vendored
File diff suppressed because it is too large
Load Diff
667
.github/workflows/issues-stale.yml
vendored
667
.github/workflows/issues-stale.yml
vendored
@@ -1,667 +0,0 @@
|
|||||||
# #
|
|
||||||
# @type github workflow
|
|
||||||
# @desc creates repository labels if they are not yet installed
|
|
||||||
# issues marked as stale after 30 days, given tag Status 𐄂 Stale
|
|
||||||
# inactive issues closed after 180 days, given tag Status 𐄂 Locked
|
|
||||||
# inactive pr closed after 365 days, given tag Status 𐄂 Locked
|
|
||||||
# issues marked stale after 30 days, given tag Status 𐄂 Stale
|
|
||||||
# issues marked closed 7 days after being marked stale, given tag Status 𐄂 Autoclosed
|
|
||||||
# @author Aetherinox
|
|
||||||
# @url https://github.com/Aetherinox
|
|
||||||
#
|
|
||||||
# This Github action must be activated manually. This workflow script will do the
|
|
||||||
# following:
|
|
||||||
#
|
|
||||||
# - Scan issues / pull requests and make sure they have properly assigned labels:
|
|
||||||
# - `Bug`
|
|
||||||
# - `Feature`
|
|
||||||
# - `Urgent`
|
|
||||||
# - `Roadmap`
|
|
||||||
#
|
|
||||||
# - Workflow script will then scan each pr or issue and mark them as `Stale`
|
|
||||||
# if they haven't had any replies in 30 days.
|
|
||||||
#
|
|
||||||
# - Workflow will `autoclose` pr or issues which haven't had action in `365 days`.
|
|
||||||
# #
|
|
||||||
|
|
||||||
name: "🎫 Issues › Stale"
|
|
||||||
run-name: "🎫 Issues › Stale"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# triggers
|
|
||||||
# #
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 0 * * *"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# environment variables
|
|
||||||
# #
|
|
||||||
|
|
||||||
env:
|
|
||||||
PREFIX_BUG: "Bug"
|
|
||||||
PREFIX_DEPENDENCY: "Dependency"
|
|
||||||
PREFIX_DOCS: "Docs"
|
|
||||||
PREFIX_FEATURE: "Feature"
|
|
||||||
PREFIX_GIT: "Git Action"
|
|
||||||
PREFIX_PR: "PR"
|
|
||||||
PREFIX_ROADMAP: "Roadmap"
|
|
||||||
PREFIX_INTERNAL: "Internal"
|
|
||||||
PREFIX_URGENT: "Urgent"
|
|
||||||
|
|
||||||
LABEL_BUG: "Type ◦ Bug"
|
|
||||||
LABEL_DEPENDENCY: "Type ◦ Dependency"
|
|
||||||
LABEL_DOCS: "Type ◦ Docs"
|
|
||||||
LABEL_FEATURE: "Type ◦ Feature"
|
|
||||||
LABEL_GIT: "Type ◦ Git Action"
|
|
||||||
LABEL_PR: "Type ◦ Pull Request"
|
|
||||||
LABEL_ROADMAP: "Type ◦ Roadmap"
|
|
||||||
LABEL_INTERNAL: "Type ◦ Internal"
|
|
||||||
LABEL_URGENT: "⚠ Urgent"
|
|
||||||
|
|
||||||
BOT_NAME_1: EuropaServ
|
|
||||||
BOT_NAME_DEPENDABOT: dependabot[bot]
|
|
||||||
LABELS_JSON: |
|
|
||||||
[
|
|
||||||
{ "name": "AC › Changes Made", "color": "8F1784", "description": "Requested changes have been made and are pending a re-scan" },
|
|
||||||
{ "name": "AC › Changes Required", "color": "8F1784", "description": "Requires changes to be made to the package before being accepted" },
|
|
||||||
{ "name": "AC › Failed", "color": "a61f2d", "description": "Autocheck failed to run through a complete cycle, requires investigation" },
|
|
||||||
{ "name": "AC › Needs Rebase", "color": "8F1784", "description": "Due to the permissions on the requesting repo, this pull request must be rebased by the author" },
|
|
||||||
{ "name": "AC › Passed", "color": "146b4a", "description": "Ready to be reviewed" },
|
|
||||||
{ "name": "AC › Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" },
|
|
||||||
{ "name": "AC › Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" },
|
|
||||||
{ "name": "AC › Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" },
|
|
||||||
{ "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" },
|
|
||||||
{ "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" },
|
|
||||||
{ "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" },
|
|
||||||
{ "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" },
|
|
||||||
{ "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" },
|
|
||||||
{ "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" },
|
|
||||||
{ "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" },
|
|
||||||
{ "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" },
|
|
||||||
{ "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" },
|
|
||||||
{ "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" },
|
|
||||||
{ "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" },
|
|
||||||
{ "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" },
|
|
||||||
{ "name": "Type ◦ Bug", "color": "9a2c2c", "description": "Something isn't working" },
|
|
||||||
{ "name": "Type ◦ Dependency", "color": "243759", "description": "Item is associated to dependency" },
|
|
||||||
{ "name": "Type ◦ Docs", "color": "0e588d", "description": "Improvements or modifications to docs" },
|
|
||||||
{ "name": "Type ◦ Feature", "color": "3c4e93", "description": "Feature request" },
|
|
||||||
{ "name": "Type ◦ Git Action", "color": "030406", "description": "GitHub Action / workflow" },
|
|
||||||
{ "name": "Type ◦ Pull Request", "color": "8F1784", "description": "Normal pull request" },
|
|
||||||
{ "name": "Type ◦ Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" },
|
|
||||||
{ "name": "Type ◦ Internal", "color": "A51994", "description": "Assigned items are for internal developer use" },
|
|
||||||
{ "name": "Build ◦ Desktop", "color": "c7ca4a", "description": "Specific to desktop" },
|
|
||||||
{ "name": "Build ◦ Linux", "color": "c7ca4a", "description": "Specific to Linux" },
|
|
||||||
{ "name": "Build ◦ MacOS", "color": "c7ca4a", "description": "Specific to MacOS" },
|
|
||||||
{ "name": "Build ◦ Mobile", "color": "c7ca4a", "description": "Specific to mobile" },
|
|
||||||
{ "name": "Build ◦ Web", "color": "c7ca4a", "description": "Specific to web" },
|
|
||||||
{ "name": "Build ◦ Windows", "color": "c7ca4a", "description": "Specific to Windows" },
|
|
||||||
{ "name": "› API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" },
|
|
||||||
{ "name": "› Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" },
|
|
||||||
{ "name": "› Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" },
|
|
||||||
{ "name": "› Customization", "color": "E3F0FC", "description": "Customizations: plugins, themes, configs" },
|
|
||||||
{ "name": "› Design", "color": "FA70DE", "description": "Design related queries" },
|
|
||||||
{ "name": "› Dist", "color": "FA70DE", "description": "Installers and other forms of software distribution" },
|
|
||||||
{ "name": "› Enterprise", "color": "11447a", "description": "Issues about collaboration, administration, and so on" },
|
|
||||||
{ "name": "› Hardware", "color": "5a7503", "description": "YubiKey, other tokens, biometrics" },
|
|
||||||
{ "name": "› Import/Export", "color": "F5FFCC", "description": "Import from and export to different file formats" },
|
|
||||||
{ "name": "› Improvement", "color": "185c98", "description": "Enhance an existing feature" },
|
|
||||||
{ "name": "› Performance", "color": "006b75", "description": "Web and desktop performance issues" },
|
|
||||||
{ "name": "› Plugin Request", "color": "FCE9CA", "description": "Requested changes should be implemented as a plugin" },
|
|
||||||
{ "name": "› Security", "color": "F75D39", "description": "Security issues" },
|
|
||||||
{ "name": "› Self-Hosting", "color": "fad8c7", "description": "Self-hosting installations and configs" },
|
|
||||||
{ "name": "› Storage", "color": "5319e7", "description": "Storage providers: Dropbox, Google, WebDAV, etc." },
|
|
||||||
{ "name": "› Updater", "color": "1BADDE", "description": "Auto-updater issues" },
|
|
||||||
{ "name": "› UX", "color": "1BADDE", "description": "UX and usability" },
|
|
||||||
{ "name": "› Website", "color": "fef2c0", "description": "Website related issues" },
|
|
||||||
{ "name": "⚠ Urgent", "color": "a8740e", "description": "Requires urgent attention" },
|
|
||||||
{ "name": "⚠ Announcement", "color": "DB4712", "description": "Announcements" },
|
|
||||||
{ "name": "📰 Progress Report", "color": "392297", "description": "Development updates" },
|
|
||||||
{ "name": "📦 Release", "color": "277542", "description": "Release announcements" },
|
|
||||||
{ "name": "✔️ Poll", "color": "972255", "description": "Community polls" },
|
|
||||||
{ "name": "❔ Question", "color": "FFFFFF", "description": "All questions" }
|
|
||||||
]
|
|
||||||
|
|
||||||
# #
|
|
||||||
# jobs
|
|
||||||
# #
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job [ Verify / Create Labels ]
|
|
||||||
#
|
|
||||||
# This job will ensure you have labels already created in your repo.
|
|
||||||
# All labels come from the JSON table LABELS_JSON.
|
|
||||||
# #
|
|
||||||
|
|
||||||
job-labels-create:
|
|
||||||
name: >-
|
|
||||||
🎫 Labels › Verify Existing
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Create Labels ] Start
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
✅ Start
|
|
||||||
id: task_label_create_start
|
|
||||||
run: |
|
|
||||||
echo "Assigning labels and assignees"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Create Labels ] Checkout
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
☑️ Checkout
|
|
||||||
id: task_label_create_checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Create Labels ] Verify Existing Labels
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
🏷️ Verify Existing Labels
|
|
||||||
id: task_label_create_verify
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
script: |
|
|
||||||
const labels = JSON.parse( process.env.LABELS_JSON );
|
|
||||||
for ( const label of labels )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await github.rest.issues.createLabel(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
name: label.name,
|
|
||||||
description: label.description || '',
|
|
||||||
color: label.color
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch ( err )
|
|
||||||
{
|
|
||||||
if ( err.status === 422 )
|
|
||||||
{
|
|
||||||
console.log( `Label '${label.name}' already exists. Skipping.` );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
console.error( `Error creating label '${label.name}': ${err}` );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job [ Check Labels ]
|
|
||||||
#
|
|
||||||
# Runs through all submissions to check for ones that have not been properly labeled
|
|
||||||
# - Bug
|
|
||||||
# - Feature
|
|
||||||
# - Urgent
|
|
||||||
# - Roadmap
|
|
||||||
# #
|
|
||||||
|
|
||||||
job-issues-nolabel:
|
|
||||||
name: >-
|
|
||||||
🎫 Labels › Assign Missing
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: job-labels-create
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Check Labels ] Checkout
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "☑️ Prepare"
|
|
||||||
id: task_issues_nolabel_prepare
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Check Labels ] Check
|
|
||||||
# Check if repo has labels currently added to issues
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: 🏷️ Checking Issues
|
|
||||||
id: task_issues_nolabel_run
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
script: |
|
|
||||||
|
|
||||||
/*
|
|
||||||
Date/Time
|
|
||||||
*/
|
|
||||||
|
|
||||||
const dateTimeformat = ( date ) =>
|
|
||||||
{
|
|
||||||
let month = date.getMonth( ) + 1;
|
|
||||||
month = month.toString( ).padStart( 2, '0' );
|
|
||||||
let day = date.getDate( ).toString( ).padStart( 2, '0' );
|
|
||||||
let year = date.getFullYear( ).toString( ).padStart( 2, '0' );
|
|
||||||
|
|
||||||
let hours = date.getHours();
|
|
||||||
let minutes = date.getMinutes();
|
|
||||||
let x = hours >= 12 ? 'PM' : 'AM';
|
|
||||||
hours = hours % 12;
|
|
||||||
hours = hours ? hours : 12;
|
|
||||||
minutes = minutes.toString( ).padStart( 2, '0' );
|
|
||||||
|
|
||||||
let mergeTime = month + '.' + day + '.' + year + ' ' + hours + ':' + minutes + ' ' + x;
|
|
||||||
|
|
||||||
return mergeTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Change last number ( 36 = hours )
|
|
||||||
*/
|
|
||||||
|
|
||||||
const expireAfterMs = 1000 * 60 * 60 * 36; // milliseconds ( 36 hours )
|
|
||||||
const curtime = new Date( ).getTime( ); // 1711471510629
|
|
||||||
const issues = await github.rest.issues.listForRepo( { owner: context.repo.owner, repo: context.repo.repo, state: 'open' } );
|
|
||||||
|
|
||||||
console.log( ` 📦── Found ${issues.data.length} open issues` );
|
|
||||||
|
|
||||||
for ( const issue of issues.data )
|
|
||||||
{
|
|
||||||
|
|
||||||
const author = `${ issue.user.login }`;
|
|
||||||
let date_UpdateDate = new Date( `${ issue.updated_at }` ?? `${ issue.created_at }` ); // Tue Mar 26 2024 16:40:41 GMT+0000 (Coordinated Universal Time)
|
|
||||||
date_UpdateDate.toISOString( ) // Tue Mar 26 2024 16:40:41 GMT+0000 (Coordinated Universal Time) (string)
|
|
||||||
|
|
||||||
let date_UpdateHuman = dateTimeformat( date_UpdateDate ) + " UTC"; // 03.26.2024 4:40 PM UTC
|
|
||||||
const time_UpdateMs = new Date( issue.updated_at ).getTime( ); // 1711471241000
|
|
||||||
|
|
||||||
//if ( curtime < time_UpdateMs + expireAfterMs ) continue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Anything past this point is stale / to be closed
|
|
||||||
*/
|
|
||||||
|
|
||||||
const timeline = await github.rest.issues.listEventsForTimeline( { owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number } );
|
|
||||||
// const labelEvent = timeline.data.find( event => event.event === 'labeled' && event.label.name === 'status-stale' );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Get Issue Data
|
|
||||||
*/
|
|
||||||
|
|
||||||
const add_labels = issue.labels.map( label => label.name );
|
|
||||||
|
|
||||||
let iss_title = `${ issue.title }`;
|
|
||||||
const iss_title_lc = iss_title.toLowerCase( );
|
|
||||||
|
|
||||||
let iss_body = `${ issue.body }`;
|
|
||||||
const iss_body_lc = iss_body.toLowerCase( );
|
|
||||||
|
|
||||||
console.log( ` └── 📁 ` + iss_title );
|
|
||||||
console.log( ` └── 📄 Issue #${ issue.number } last updated on ${ date_UpdateHuman }` );
|
|
||||||
console.log( ` └── 📄 ${add_labels}` );
|
|
||||||
console.log( `\n\n` )
|
|
||||||
|
|
||||||
/*
|
|
||||||
Keywords
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bug_words = [ "bug", "broke", "issue", "fail" ];
|
|
||||||
const feat_words = [ "feature", "request", "add support" ];
|
|
||||||
const urgn_words = [ "urgent", "urgency", "emergency", "important", "critical" ];
|
|
||||||
const road_words = [ "roadmap", "road map", "planned" ];
|
|
||||||
|
|
||||||
/*
|
|
||||||
Tags
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bug_tag = `${{ env.PREFIX_BUG }}:`;
|
|
||||||
const bug_lbl = `${{ env.LABEL_BUG }}`;
|
|
||||||
const feat_tag = `${{ env.PREFIX_FEATURE }}:`;
|
|
||||||
const feat_lbl = `${{ env.LABEL_FEATURE }}`;
|
|
||||||
const urgn_tag = `${{ env.PREFIX_URGENT }}:`;
|
|
||||||
const urgn_lbl = `${{ env.LABEL_URGENT }}`;
|
|
||||||
const road_tag = `${{ env.PREFIX_ROADMAP }}:`;
|
|
||||||
const road_lbl = `${{ env.LABEL_ROADMAP }}`;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Label > Bugs
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bug_bIncWordT = bug_words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Find regex based phrases
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/Z99Gnq/2
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bug_findWordList = /^\b(?:I?\s*have\s*(?:a|an)\s*(?:issue|problem|bug))|(?:will\s*not\s*work)|(?:it\s*is\s*(?:broken|broke|stuck))|(?:found\s*(?:an?|the)\s*(?:bug|issue))|(?:can\s*I\s*fix\s*the\s*(?:bug|issue))|(?:(?:does not|doesn'?t|don'?t|won'?t|can'?t|can\s?not|will\s*not)\s*(?:work|load|function))|(?:it\s*(?:will\s?not|won'?t|can\s?not|can'?t))\s*(?:get|find)\s*the\s*(?:website|site|webpage|page)|(?:the\s*(?:window|frame)\s*is\s*(?:blank|white|empty|missing))\b$/igm;
|
|
||||||
const bug_bFoundMatchTitle = Boolean( bug_findWordList.test( iss_title ) );
|
|
||||||
const bug_bFoundMatchBody = Boolean( bug_findWordList.test( iss_body ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Do not change a title if the item starts with a PR: #
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/JOrqbN/1
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bug_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm;
|
|
||||||
const bug_bFoundPRTitle = Boolean( bug_findPRTitle.test( iss_title ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
- Check if issue title matches the issue label "Bug:"
|
|
||||||
- Check if title contains word in containsList
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ( iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) || bug_bIncWordT || bug_bFoundMatchTitle || bug_bFoundMatchBody )
|
|
||||||
{
|
|
||||||
|
|
||||||
add_labels.push( `${ bug_lbl }` );
|
|
||||||
|
|
||||||
if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` )
|
|
||||||
core.info( `Skipping: Detected ${ author }` )
|
|
||||||
|
|
||||||
// Rename title to contain Bug:
|
|
||||||
if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !bug_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
{
|
|
||||||
const title = issue.title;
|
|
||||||
let title_new = title.replace( /^\s?bug\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?fail\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?issue\s*(.*?)\b/gi, '' );
|
|
||||||
iss_title = `${ bug_tag } ${ title_new }`;
|
|
||||||
}
|
|
||||||
|
|
||||||
await github.rest.issues.update(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number,
|
|
||||||
title: `${ iss_title }`, labels: add_labels
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Label > Features
|
|
||||||
*/
|
|
||||||
|
|
||||||
const feat_bIncWordT = feat_words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Find regex based phrases
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/fR1Hm6/1
|
|
||||||
*/
|
|
||||||
|
|
||||||
const feat_findWordList = /^(?:(?:request|include|see)\s*(?:an?|the?)\s*(?:feature|addon|addition|plugin))|(?:(?:add|see|get)\s*support\s*(?:for|with|of))|(?:can\s*we\s*get\s*(?:the|a)\s*(?:ability|feature))|(?:💡 Feature:)$/igm;
|
|
||||||
const feat_bFoundMatchTitle = Boolean( feat_findWordList.test( iss_title ) );
|
|
||||||
const feat_bFoundMatchBody = Boolean( feat_findWordList.test( iss_body ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Do not change a title if the item starts with a PR: #
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/JOrqbN/1
|
|
||||||
*/
|
|
||||||
|
|
||||||
const feat_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm;
|
|
||||||
const feat_bFoundPRTitle = Boolean( feat_findPRTitle.test( iss_title ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
- Check if issue title matches the issue label "Feature:"
|
|
||||||
- Check if title contains word in containsList
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ( iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) || feat_bIncWordT || feat_bFoundMatchTitle || feat_bFoundMatchBody )
|
|
||||||
{
|
|
||||||
|
|
||||||
add_labels.push( `${ feat_lbl }` );
|
|
||||||
|
|
||||||
if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` )
|
|
||||||
core.info( `Skipping: Detected ${ author }` )
|
|
||||||
|
|
||||||
// Rename title to contain Feature:
|
|
||||||
if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !feat_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
{
|
|
||||||
const title = issue.title;
|
|
||||||
let title_new = title.replace( /^\s?feature\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?request\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?add(.*?)\s?feature\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?add(.*?)\s?support\s*(.*?)\b/gi, '' );
|
|
||||||
iss_title = `${ feat_tag } ${ title_new }`;
|
|
||||||
}
|
|
||||||
|
|
||||||
await github.rest.issues.update(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number,
|
|
||||||
title: `${ iss_title }`, labels: add_labels
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Label > Urgent
|
|
||||||
*/
|
|
||||||
|
|
||||||
const urgn_bIncWordT = urgn_words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Find regex based phrases
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/eE9tJX/2
|
|
||||||
*/
|
|
||||||
|
|
||||||
const urgn_findWordList = /(?:(?:this)?is\s*a?n?\s*?(?:emergency|urgent|important|vital|acute|crucial|grave|pressing|serious|top.?priority|high.?priority))|(?:reply|respond|answer|write|address)\s*(?:immediate|quick|asap|urgent|now|fast|(?:as)?\s*(?:soon|quick|immediate|fast))(?:ly)?|(?:need\s*(?:help|support|fixed|answer|reply|response)!)|(?:emergency|critical|urgen(?:t|cy)|high.?priority)/igm;
|
|
||||||
const urgn_bFoundMatchTitle = Boolean( urgn_findWordList.test( iss_title ) );
|
|
||||||
const urgn_bFoundMatchBody = Boolean( urgn_findWordList.test( iss_body ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Do not change a title if the item starts with a PR: #
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/JOrqbN/1
|
|
||||||
*/
|
|
||||||
|
|
||||||
const urgn_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm;
|
|
||||||
const urgn_bFoundPRTitle = Boolean( urgn_findPRTitle.test( iss_title ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
- Check if issue title matches the issue label "Urgent:"
|
|
||||||
- Check if title contains word in containsList
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ( iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) || urgn_bIncWordT || urgn_bFoundMatchTitle || urgn_bFoundMatchBody )
|
|
||||||
{
|
|
||||||
|
|
||||||
add_labels.push( `${ urgn_lbl }` );
|
|
||||||
|
|
||||||
if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` )
|
|
||||||
core.info( `Skipping: Detected ${ author }` )
|
|
||||||
|
|
||||||
// Rename title to contain Urgent:
|
|
||||||
if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !urgn_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
{
|
|
||||||
const title = issue.title;
|
|
||||||
let title_new = title.replace( /^\s?emergency\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?urgent\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?urgency\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?important\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?critical\s*(.*?)\b/gi, '' );
|
|
||||||
iss_title = `${ urgn_tag } ${ title_new }`;
|
|
||||||
}
|
|
||||||
|
|
||||||
await github.rest.issues.update(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number,
|
|
||||||
title: `${ iss_title }`, labels: add_labels
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Label > Roadmap
|
|
||||||
*/
|
|
||||||
|
|
||||||
const road_bIncWordT = road_words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Find regex based phrases
|
|
||||||
Roadmap requires headers #Summary and #Proposal | #Objective
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/ucajBZ/1
|
|
||||||
*/
|
|
||||||
|
|
||||||
const road_findWordList = /#\s*Summary[\S\s]+#\s*(?:Proposal|Objective)[^\]]+/igm;
|
|
||||||
const road_bFoundMatchTitle = Boolean( road_findWordList.test( iss_title ) );
|
|
||||||
const road_bFoundMatchBody = Boolean( road_findWordList.test( iss_body ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
Do not change a title if the item starts with a PR: #
|
|
||||||
|
|
||||||
Regex:
|
|
||||||
https://regex101.com/r/JOrqbN/1
|
|
||||||
*/
|
|
||||||
|
|
||||||
const road_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm;
|
|
||||||
const road_bFoundPRTitle = Boolean( road_findPRTitle.test( iss_title ) );
|
|
||||||
|
|
||||||
/*
|
|
||||||
- Check if issue title matches the issue label "Roadmap:"
|
|
||||||
- Check if title contains word in containsList
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ( iss_title_lc.startsWith( road_tag.toLowerCase( ) ) || road_bIncWordT || road_bFoundMatchTitle || road_bFoundMatchBody )
|
|
||||||
{
|
|
||||||
|
|
||||||
add_labels.push( `${ road_lbl }` );
|
|
||||||
|
|
||||||
if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` )
|
|
||||||
core.info( `Skipping: Detected ${ author }` )
|
|
||||||
|
|
||||||
// Rename title to contain Roadmap:
|
|
||||||
if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !road_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
|
|
||||||
{
|
|
||||||
const title = issue.title;
|
|
||||||
let title_new = title.replace( /^\s?emergency\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?urgent\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?urgency\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?important\s*(.*?)\b/gi, '' );
|
|
||||||
title_new = title.replace( /^\s?critical\s*(.*?)\b/gi, '' );
|
|
||||||
iss_title = `${ road_tag } ${ title_new }`;
|
|
||||||
}
|
|
||||||
|
|
||||||
await github.rest.issues.update(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number,
|
|
||||||
title: `${ iss_title }`, labels: add_labels
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
await github.rest.issues.update(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number,
|
|
||||||
state: 'closed', state_reason: 'not planned'
|
|
||||||
} );
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job [ Stale Issues ]
|
|
||||||
# #
|
|
||||||
|
|
||||||
job-issues-stale:
|
|
||||||
name: >-
|
|
||||||
💤 Check › Stale
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- job-labels-create
|
|
||||||
- job-issues-nolabel
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Stale Issues ] Check Condition
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "💤 Stale › Check Condition"
|
|
||||||
uses: actions/stale@v9
|
|
||||||
id: task_issues_stale_run
|
|
||||||
with:
|
|
||||||
repo-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
stale-issue-message: |
|
|
||||||
⚠️ It looks like there hasn't been any recent updates on this issue. If you created this issue and no longer consider it
|
|
||||||
open, then please login to github and close the issue.
|
|
||||||
|
|
||||||
If there is no further activity on this issue, it will be automatically closed in the next week.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<sub>I am a bot reaching out to you with an automated response.</sub>
|
|
||||||
stale-issue-label: 'Status 𐄂 Stale'
|
|
||||||
close-issue-label: 'Status 𐄂 Autoclosed'
|
|
||||||
exempt-issue-labels: 'Status 𐄂 Accepted,Status 𐄂 Review,Status 𐄂 Pending,Type ◦ Bug,Type ◦ Dependency,Type ◦ Docs,Type ◦ Feature,Type ◦ Git Action,Type ◦ Pull Request,Type ◦ Roadmap'
|
|
||||||
days-before-stale: 14
|
|
||||||
days-before-close: 7
|
|
||||||
days-before-pr-stale: -1
|
|
||||||
days-before-pr-close: -1
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job [ Lock Issues ]
|
|
||||||
# #
|
|
||||||
|
|
||||||
job-issues-lock:
|
|
||||||
name: >-
|
|
||||||
🔒 Check › Inactive
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- job-labels-create
|
|
||||||
- job-issues-nolabel
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Lock Issues ] Look for inactives
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "🔒 Lock › Inactives"
|
|
||||||
uses: dessant/lock-threads@v5
|
|
||||||
id: task_issues_lock_run
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
exclude-any-issue-labels: 'AC › Review Required,Status 𐄂 Accepted,Status 𐄂 Review,Status 𐄂 Pending,Type ◦ Bug,Type ◦ Dependency,Type ◦ Docs,Type ◦ Feature,Type ◦ Git Action,Type ◦ Roadmap,Type ◦ Internal'
|
|
||||||
add-issue-labels: 'Status 𐄂 Locked'
|
|
||||||
issue-inactive-days: '60'
|
|
||||||
issue-lock-reason: 'resolved'
|
|
||||||
issue-comment: >
|
|
||||||
⚠️ This **issue** has been automatically locked since there has not been any recent activity after it was closed.
|
|
||||||
|
|
||||||
Please open a new issue for related bugs.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<sub>I am a bot reaching out to you with an automated response.</sub>
|
|
||||||
exclude-any-pr-labels: 'AC › Review Required,Status 𐄂 Accepted,Status 𐄂 Review,Status 𐄂 Pending,Type ◦ Bug,Type ◦ Dependency,Type ◦ Docs,Type ◦ Feature,Type ◦ Git Action,Type ◦ Roadmap,Type ◦ Internal'
|
|
||||||
add-pr-labels: 'Status 𐄂 Locked'
|
|
||||||
pr-inactive-days: '365'
|
|
||||||
pr-lock-reason: 'resolved'
|
|
||||||
pr-comment: >
|
|
||||||
⚠️ This **pull request** has been automatically locked since there has not been any recent activity after it was closed.
|
|
||||||
|
|
||||||
Please open a new issue for related bugs.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<sub>I am a bot reaching out to you with an automated response.</sub>
|
|
||||||
183
.github/workflows/labels-clean..yml
vendored
183
.github/workflows/labels-clean..yml
vendored
@@ -1,183 +0,0 @@
|
|||||||
# #
|
|
||||||
# @type github workflow
|
|
||||||
# @desc manually activated workflow to remove issue labels
|
|
||||||
# @author Aetherinox
|
|
||||||
# @url https://github.com/Aetherinox
|
|
||||||
#
|
|
||||||
# This Github action must be activated manually. This workflow script will do the
|
|
||||||
# following:
|
|
||||||
#
|
|
||||||
# - Remove all existing labels in repository
|
|
||||||
# #
|
|
||||||
|
|
||||||
name: "🎫 Labels › Remove"
|
|
||||||
run-name: "🎫 Labels › Remove"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# triggers
|
|
||||||
# #
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# environment variables
|
|
||||||
# #
|
|
||||||
|
|
||||||
env:
|
|
||||||
BOT_NAME_1: EuropaServ
|
|
||||||
BOT_NAME_DEPENDABOT: dependabot[bot]
|
|
||||||
LABELS_JSON: |
|
|
||||||
[
|
|
||||||
{ "name": "AC › Changes Made", "color": "8F1784", "description": "Requested changes have been made and are pending a re-scan" },
|
|
||||||
{ "name": "AC › Changes Required", "color": "8F1784", "description": "Requires changes to be made to the package before being accepted" },
|
|
||||||
{ "name": "AC › Failed", "color": "a61f2d", "description": "Autocheck failed to run through a complete cycle, requires investigation" },
|
|
||||||
{ "name": "AC › Needs Rebase", "color": "8F1784", "description": "Due to the permissions on the requesting repo, this pull request must be rebased by the author" },
|
|
||||||
{ "name": "AC › Passed", "color": "146b4a", "description": "Ready to be reviewed" },
|
|
||||||
{ "name": "AC › Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" },
|
|
||||||
{ "name": "AC › Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" },
|
|
||||||
{ "name": "AC › Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" },
|
|
||||||
{ "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" },
|
|
||||||
{ "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" },
|
|
||||||
{ "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" },
|
|
||||||
{ "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" },
|
|
||||||
{ "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" },
|
|
||||||
{ "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" },
|
|
||||||
{ "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" },
|
|
||||||
{ "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" },
|
|
||||||
{ "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" },
|
|
||||||
{ "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" },
|
|
||||||
{ "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" },
|
|
||||||
{ "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" },
|
|
||||||
{ "name": "Type ◦ Bug", "color": "9a2c2c", "description": "Something isn't working" },
|
|
||||||
{ "name": "Type ◦ Dependency", "color": "243759", "description": "Item is associated to dependency" },
|
|
||||||
{ "name": "Type ◦ Docs", "color": "0e588d", "description": "Improvements or modifications to docs" },
|
|
||||||
{ "name": "Type ◦ Feature", "color": "3c4e93", "description": "Feature request" },
|
|
||||||
{ "name": "Type ◦ Git Action", "color": "030406", "description": "GitHub Action / workflow" },
|
|
||||||
{ "name": "Type ◦ Pull Request", "color": "8F1784", "description": "Normal pull request" },
|
|
||||||
{ "name": "Type ◦ Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" },
|
|
||||||
{ "name": "Type ◦ Internal", "color": "A51994", "description": "Assigned items are for internal developer use" },
|
|
||||||
{ "name": "Build ◦ Desktop", "color": "c7ca4a", "description": "Specific to desktop" },
|
|
||||||
{ "name": "Build ◦ Linux", "color": "c7ca4a", "description": "Specific to Linux" },
|
|
||||||
{ "name": "Build ◦ MacOS", "color": "c7ca4a", "description": "Specific to MacOS" },
|
|
||||||
{ "name": "Build ◦ Mobile", "color": "c7ca4a", "description": "Specific to mobile" },
|
|
||||||
{ "name": "Build ◦ Web", "color": "c7ca4a", "description": "Specific to web" },
|
|
||||||
{ "name": "Build ◦ Windows", "color": "c7ca4a", "description": "Specific to Windows" },
|
|
||||||
{ "name": "› API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" },
|
|
||||||
{ "name": "› Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" },
|
|
||||||
{ "name": "› Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" },
|
|
||||||
{ "name": "› Customization", "color": "E3F0FC", "description": "Customizations: plugins, themes, configs" },
|
|
||||||
{ "name": "› Design", "color": "FA70DE", "description": "Design related queries" },
|
|
||||||
{ "name": "› Dist", "color": "FA70DE", "description": "Installers and other forms of software distribution" },
|
|
||||||
{ "name": "› Enterprise", "color": "11447a", "description": "Issues about collaboration, administration, and so on" },
|
|
||||||
{ "name": "› Hardware", "color": "5a7503", "description": "YubiKey, other tokens, biometrics" },
|
|
||||||
{ "name": "› Import/Export", "color": "F5FFCC", "description": "Import from and export to different file formats" },
|
|
||||||
{ "name": "› Improvement", "color": "185c98", "description": "Enhance an existing feature" },
|
|
||||||
{ "name": "› Performance", "color": "006b75", "description": "Web and desktop performance issues" },
|
|
||||||
{ "name": "› Plugin Request", "color": "FCE9CA", "description": "Requested changes should be implemented as a plugin" },
|
|
||||||
{ "name": "› Security", "color": "F75D39", "description": "Security issues" },
|
|
||||||
{ "name": "› Self-Hosting", "color": "fad8c7", "description": "Self-hosting installations and configs" },
|
|
||||||
{ "name": "› Storage", "color": "5319e7", "description": "Storage providers: Dropbox, Google, WebDAV, etc." },
|
|
||||||
{ "name": "› Updater", "color": "1BADDE", "description": "Auto-updater issues" },
|
|
||||||
{ "name": "› UX", "color": "1BADDE", "description": "UX and usability" },
|
|
||||||
{ "name": "› Website", "color": "fef2c0", "description": "Website related issues" },
|
|
||||||
{ "name": "⚠ Urgent", "color": "a8740e", "description": "Requires urgent attention" },
|
|
||||||
{ "name": "⚠ Announcement", "color": "DB4712", "description": "Announcements" },
|
|
||||||
{ "name": "📰 Progress Report", "color": "392297", "description": "Development updates" },
|
|
||||||
{ "name": "📦 Release", "color": "277542", "description": "Release announcements" },
|
|
||||||
{ "name": "✔️ Poll", "color": "972255", "description": "Community polls" },
|
|
||||||
{ "name": "❔ Question", "color": "FFFFFF", "description": "All questions" }
|
|
||||||
]
|
|
||||||
|
|
||||||
# #
|
|
||||||
# jobs
|
|
||||||
# #
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job › Remove Labels
|
|
||||||
#
|
|
||||||
# This job removes all existing labels
|
|
||||||
# #
|
|
||||||
|
|
||||||
issues-labels-remove:
|
|
||||||
name: >-
|
|
||||||
🎫 Labels › Remove
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: 'read'
|
|
||||||
id-token: 'write'
|
|
||||||
issues: 'write'
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Delete Labels ] Start
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
✅ Start
|
|
||||||
id: task_label_remove_start
|
|
||||||
run: |
|
|
||||||
echo "Starting workflow"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Delete Labels ] Checkout
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
☑️ Checkout
|
|
||||||
id: task_label_remove_checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Delete Labels ] Start
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
🏷️ Delete Existing Labels
|
|
||||||
id: task_label_remove_run
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
script: |
|
|
||||||
const targetOwner = context.repo.owner;
|
|
||||||
const targetRepo = context.repo.repo;
|
|
||||||
|
|
||||||
// Fetch labels from the source repository
|
|
||||||
const response = await github.rest.issues.listLabelsForRepo({
|
|
||||||
owner: targetOwner,
|
|
||||||
repo: targetRepo,
|
|
||||||
});
|
|
||||||
console.log("Labels fetched: ", response.data);
|
|
||||||
|
|
||||||
const labels = response.data;
|
|
||||||
if (labels.length === 0) {
|
|
||||||
console.log("No labels found in the source repository.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch all labels from the target repository and delete them
|
|
||||||
const existingLabels = await github.rest.issues.listLabelsForRepo({
|
|
||||||
owner: targetOwner,
|
|
||||||
repo: targetRepo,
|
|
||||||
});
|
|
||||||
|
|
||||||
// const labels = JSON.parse( process.env.LABELS_JSON );
|
|
||||||
for ( const label of labels )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await github.rest.issues.deleteLabel(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
name: label.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch ( err )
|
|
||||||
{
|
|
||||||
console.error("Error: " + err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
182
.github/workflows/labels-create.yml
vendored
182
.github/workflows/labels-create.yml
vendored
@@ -1,182 +0,0 @@
|
|||||||
# #
|
|
||||||
# @type github workflow
|
|
||||||
# @desc manually activated workflow to create issue labels
|
|
||||||
# @author Aetherinox
|
|
||||||
# @url https://github.com/Aetherinox
|
|
||||||
#
|
|
||||||
# This Github action must be activated manually. This workflow script will do the
|
|
||||||
# following:
|
|
||||||
#
|
|
||||||
# - Scan issues / pull requests and make sure they have properly assigned labels:
|
|
||||||
# - `Bug`
|
|
||||||
# - `Feature`
|
|
||||||
# - `Urgent`
|
|
||||||
# - `Roadmap`
|
|
||||||
#
|
|
||||||
# - Workflow script will then scan each pr or issue and mark them as `Stale`
|
|
||||||
# if they haven't had any replies in 30 days.
|
|
||||||
#
|
|
||||||
# - Workflow will `autoclose` pr or issues which haven't had action in `365 days`.
|
|
||||||
# #
|
|
||||||
|
|
||||||
name: "🎫 Labels › Create"
|
|
||||||
run-name: "🎫 Labels › Create"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# triggers
|
|
||||||
# #
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# environment variables
|
|
||||||
# #
|
|
||||||
|
|
||||||
env:
|
|
||||||
BOT_NAME_1: AdminServ
|
|
||||||
BOT_NAME_2: AdminServX
|
|
||||||
BOT_NAME_3: EuropaServ
|
|
||||||
BOT_NAME_DEPENDABOT: dependabot[bot]
|
|
||||||
LABELS_JSON: |
|
|
||||||
[
|
|
||||||
{ "name": "AC › Changes Made", "color": "8F1784", "description": "Requested changes have been made and are pending a re-scan" },
|
|
||||||
{ "name": "AC › Changes Required", "color": "8F1784", "description": "Requires changes to be made to the package before being accepted" },
|
|
||||||
{ "name": "AC › Failed", "color": "a61f2d", "description": "Autocheck failed to run through a complete cycle, requires investigation" },
|
|
||||||
{ "name": "AC › Needs Rebase", "color": "8F1784", "description": "Due to the permissions on the requesting repo, this pull request must be rebased by the author" },
|
|
||||||
{ "name": "AC › Passed", "color": "146b4a", "description": "Ready to be reviewed" },
|
|
||||||
{ "name": "AC › Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" },
|
|
||||||
{ "name": "AC › Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" },
|
|
||||||
{ "name": "AC › Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" },
|
|
||||||
{ "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" },
|
|
||||||
{ "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" },
|
|
||||||
{ "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" },
|
|
||||||
{ "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" },
|
|
||||||
{ "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" },
|
|
||||||
{ "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" },
|
|
||||||
{ "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" },
|
|
||||||
{ "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" },
|
|
||||||
{ "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" },
|
|
||||||
{ "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" },
|
|
||||||
{ "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" },
|
|
||||||
{ "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" },
|
|
||||||
{ "name": "Type ◦ Bug", "color": "9a2c2c", "description": "Something isn't working" },
|
|
||||||
{ "name": "Type ◦ Dependency", "color": "243759", "description": "Item is associated to dependency" },
|
|
||||||
{ "name": "Type ◦ Docs", "color": "0e588d", "description": "Improvements or modifications to docs" },
|
|
||||||
{ "name": "Type ◦ Feature", "color": "3c4e93", "description": "Feature request" },
|
|
||||||
{ "name": "Type ◦ Git Action", "color": "030406", "description": "GitHub Action / workflow" },
|
|
||||||
{ "name": "Type ◦ Pull Request", "color": "8F1784", "description": "Normal pull request" },
|
|
||||||
{ "name": "Type ◦ Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" },
|
|
||||||
{ "name": "Type ◦ Internal", "color": "A51994", "description": "Assigned items are for internal developer use" },
|
|
||||||
{ "name": "Build ◦ Desktop", "color": "c7ca4a", "description": "Specific to desktop" },
|
|
||||||
{ "name": "Build ◦ Linux", "color": "c7ca4a", "description": "Specific to Linux" },
|
|
||||||
{ "name": "Build ◦ MacOS", "color": "c7ca4a", "description": "Specific to MacOS" },
|
|
||||||
{ "name": "Build ◦ Mobile", "color": "c7ca4a", "description": "Specific to mobile" },
|
|
||||||
{ "name": "Build ◦ Web", "color": "c7ca4a", "description": "Specific to web" },
|
|
||||||
{ "name": "Build ◦ Windows", "color": "c7ca4a", "description": "Specific to Windows" },
|
|
||||||
{ "name": "› API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" },
|
|
||||||
{ "name": "› Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" },
|
|
||||||
{ "name": "› Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" },
|
|
||||||
{ "name": "› Customization", "color": "E3F0FC", "description": "Customizations: plugins, themes, configs" },
|
|
||||||
{ "name": "› Design", "color": "FA70DE", "description": "Design related queries" },
|
|
||||||
{ "name": "› Dist", "color": "FA70DE", "description": "Installers and other forms of software distribution" },
|
|
||||||
{ "name": "› Enterprise", "color": "11447a", "description": "Issues about collaboration, administration, and so on" },
|
|
||||||
{ "name": "› Hardware", "color": "5a7503", "description": "YubiKey, other tokens, biometrics" },
|
|
||||||
{ "name": "› Import/Export", "color": "F5FFCC", "description": "Import from and export to different file formats" },
|
|
||||||
{ "name": "› Improvement", "color": "185c98", "description": "Enhance an existing feature" },
|
|
||||||
{ "name": "› Performance", "color": "006b75", "description": "Web and desktop performance issues" },
|
|
||||||
{ "name": "› Plugin Request", "color": "FCE9CA", "description": "Requested changes should be implemented as a plugin" },
|
|
||||||
{ "name": "› Security", "color": "F75D39", "description": "Security issues" },
|
|
||||||
{ "name": "› Self-Hosting", "color": "fad8c7", "description": "Self-hosting installations and configs" },
|
|
||||||
{ "name": "› Storage", "color": "5319e7", "description": "Storage providers: Dropbox, Google, WebDAV, etc." },
|
|
||||||
{ "name": "› Updater", "color": "1BADDE", "description": "Auto-updater issues" },
|
|
||||||
{ "name": "› UX", "color": "1BADDE", "description": "UX and usability" },
|
|
||||||
{ "name": "› Website", "color": "fef2c0", "description": "Website related issues" },
|
|
||||||
{ "name": "⚠ Urgent", "color": "a8740e", "description": "Requires urgent attention" },
|
|
||||||
{ "name": "⚠ Announcement", "color": "DB4712", "description": "Announcements" },
|
|
||||||
{ "name": "📰 Progress Report", "color": "392297", "description": "Development updates" },
|
|
||||||
{ "name": "📦 Release", "color": "277542", "description": "Release announcements" },
|
|
||||||
{ "name": "✔️ Poll", "color": "972255", "description": "Community polls" },
|
|
||||||
{ "name": "❔ Question", "color": "FFFFFF", "description": "All questions" }
|
|
||||||
]
|
|
||||||
|
|
||||||
# #
|
|
||||||
# jobs
|
|
||||||
# #
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job [ Verify / Create Labels ]
|
|
||||||
#
|
|
||||||
# This job will ensure you have labels already created in your repo.
|
|
||||||
# All labels come from the JSON table LABELS_JSON.
|
|
||||||
# #
|
|
||||||
|
|
||||||
issues-labels-create:
|
|
||||||
name: >-
|
|
||||||
🎫 Labels › Create
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: 'read'
|
|
||||||
id-token: 'write'
|
|
||||||
issues: 'write'
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Create Labels ] Start
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
✅ Start
|
|
||||||
id: task_label_create_start
|
|
||||||
run: |
|
|
||||||
echo "Assigning labels and assignees"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Create Labels ] Checkout
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
☑️ Checkout
|
|
||||||
id: task_label_create_checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# #
|
|
||||||
# [ Create Labels ] Verify Existing Labels
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: >-
|
|
||||||
🏷️ Verify Existing Labels
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
|
|
||||||
script: |
|
|
||||||
const labels = JSON.parse( process.env.LABELS_JSON );
|
|
||||||
for ( const label of labels )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await github.rest.issues.createLabel(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
name: label.name,
|
|
||||||
description: label.description || '',
|
|
||||||
color: label.color
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch ( err )
|
|
||||||
{
|
|
||||||
if ( err.status === 422 )
|
|
||||||
{
|
|
||||||
console.log( `Label '${label.name}' already exists. Skipping.` );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
console.error( `Error creating label '${label.name}': ${err}` );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
175
.github/workflows/ping-developer.yml
vendored
175
.github/workflows/ping-developer.yml
vendored
@@ -1,175 +0,0 @@
|
|||||||
# #
|
|
||||||
# @type github workflow
|
|
||||||
# @desc pings the developer
|
|
||||||
# @author Aetherinox
|
|
||||||
# @url https://github.com/Aetherinox
|
|
||||||
# #
|
|
||||||
|
|
||||||
name: "⚙️ Ping › Developer"
|
|
||||||
run-name: "⚙️ Ping › Developer"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# triggers
|
|
||||||
# #
|
|
||||||
|
|
||||||
on:
|
|
||||||
issue_comment:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
# #
|
|
||||||
# environment variables
|
|
||||||
# #
|
|
||||||
|
|
||||||
env:
|
|
||||||
BOT_NAME_1: AdminServ
|
|
||||||
BOT_NAME_2: AdminServX
|
|
||||||
BOT_NAME_3: EuropaServ
|
|
||||||
BOT_NAME_DEPENDABOT: dependabot[bot]
|
|
||||||
|
|
||||||
# #
|
|
||||||
# jobs
|
|
||||||
#
|
|
||||||
# env not available for job.if
|
|
||||||
# #
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: |
|
|
||||||
contains(github.event.comment.body, '/ping')
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Job > Complete > Get publish timestamp
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: "🕛 Get Timestamp"
|
|
||||||
id: task_complete_timestamp_get
|
|
||||||
run: |
|
|
||||||
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Add Label to accepted PR
|
|
||||||
#
|
|
||||||
# port 465
|
|
||||||
# server_port: 465
|
|
||||||
# secure: true
|
|
||||||
# ignore_cert: false
|
|
||||||
#
|
|
||||||
# port 587
|
|
||||||
# server_port: 587
|
|
||||||
# secure: false
|
|
||||||
# #
|
|
||||||
|
|
||||||
- name: Send mail
|
|
||||||
uses: dawidd6/action-send-mail@v4
|
|
||||||
with:
|
|
||||||
server_address: ${{secrets.EMAIL_SMTP}}
|
|
||||||
server_port: 465
|
|
||||||
secure: true
|
|
||||||
username: ${{secrets.EMAIL_FROM}}
|
|
||||||
password: ${{secrets.EMAIL_KEY}}
|
|
||||||
subject: "Github: Ping notification from ${{ github.repository }}"
|
|
||||||
to: ${{secrets.EMAIL_TO}}
|
|
||||||
from: ${{secrets.EMAIL_FROM}}
|
|
||||||
html_body: |
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<title>Title</title>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background: url('https://images.unsplash.com/photo-1541422348463-9bc715520974?fm=jpg&q=60&w=3000&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8M3x8ZGFyayUyMG1vdW50YWlufGVufDB8fDB8fHww');
|
|
||||||
font-size:9pt;
|
|
||||||
margin:0;
|
|
||||||
padding:0;
|
|
||||||
}
|
|
||||||
.background-overlay {
|
|
||||||
background-color: #1111119f;
|
|
||||||
}
|
|
||||||
.background-header {
|
|
||||||
background: url('https://process.fs.teachablecdn.com/ADNupMnWyR7kCWRvm76Laz/resize=width:705/https://cdn.filestackcontent.com/MipxnobQRRS5h7raz9aM');
|
|
||||||
background-size: cover;
|
|
||||||
background-size: 100%;
|
|
||||||
background-color:#1b1b1b;
|
|
||||||
padding:5px;
|
|
||||||
height:100px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="background-overlay">
|
|
||||||
<center>
|
|
||||||
<div class="background-header">
|
|
||||||
<a href="https://github.com/${{ github.repository }}"><img style="height:80px;padding-top:10px;" src="https://cdn0.iconfinder.com/data/icons/shift-logotypes/32/Github-512.png"></a>
|
|
||||||
</div>
|
|
||||||
</center>
|
|
||||||
|
|
||||||
<div style="font-size:9pt;padding: 20px;color:#FFF;">
|
|
||||||
<h3><span style="font-size:9pt;color:#cc6613;">[Github]</span> <span style="font-size:9pt;color:#FFF;">Dear ${{github.repository_owner}},</span></h3>
|
|
||||||
<p style="font-size:9pt;color:#FFF;"><br />You have received a ping notification from <a href="https://github.com/${{ github.repository }}">${{ github.repository }}</a> by <a href="https://github.com/${{ github.event.comment.user.login }}">${{ github.event.comment.user.login }}</a>.</p>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<center>
|
|
||||||
<table cellspacing="0" cellpadding="0" width="40%" class="center">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
style="font-size:9pt;background-color:#8a2138;color:#FFF;padding:6px;padding-left:10px;"><b>Repository</b></td>
|
|
||||||
<td style="font-size:9pt;padding-left:5px;color:#b3b3b3;background-color:#1b1b1b;padding-left:10px;">${{ github.repository }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
style="font-size:9pt;background-color:#8a2138;color:#FFF;padding:6px;padding-left:10px;"><b>Date</b></td>
|
|
||||||
<td style="font-size:9pt;padding-left:5px;color:#b3b3b3;background-color:#0f0f0f;padding-left:10px;">${{ env.NOW }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
style="font-size:9pt;background-color:#8a2138;color:#FFF;padding:6px;padding-left:10px;"><b>Commenter</b></td>
|
|
||||||
<td style="font-size:9pt;padding-left:5px;color:#b3b3b3;background-color:#1b1b1b;padding-left:10px;">${{ github.event.comment.user.login }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
style="font-size:9pt;background-color:#8a2138;color:#FFF;padding:6px;padding-left:10px;"><b>Issue #</b></td>
|
|
||||||
<td style="font-size:9pt;padding-left:5px;color:#b3b3b3;background-color:#0f0f0f;padding-left:10px;">${{ github.event.issue.number }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
style="font-size:9pt;background-color:#8a2138;color:#FFF;padding:6px;padding-left:10px;"><b>Action</b></td>
|
|
||||||
<td style="font-size:9pt;padding-left:5px;color:#b3b3b3;background-color:#1b1b1b;padding-left:10px;">Notification</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</center>
|
|
||||||
|
|
||||||
<br><br>
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<div style="font-family:Consolas;">
|
|
||||||
|
|
||||||
<textarea readonly=true style="font-size:9pt;width:60%;background-color:#363636;color:#FFF;padding:15px;border:1px solid #5a5a5a;" id="w3review" name="w3review" rows="20" cols="100">
|
|
||||||
${{ github.event.comment.body }}
|
|
||||||
</textarea>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</center>
|
|
||||||
|
|
||||||
<p> </p>
|
|
||||||
<p style="color:#FFF;"><br /> ~ Github
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br /><br />
|
|
||||||
|
|
||||||
<div style="background-color:#1b1b1b;padding:5px;line-height:70px;height:70px;text-align:center;">
|
|
||||||
<span style="color:#FFF;font-size:8pt;">Copyright © 2024 - Betelgeuse</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
ignore_cert: true
|
|
||||||
convert_markdown: true
|
|
||||||
priority: normal
|
|
||||||
69
.gitignore
vendored
69
.gitignore
vendored
@@ -1,64 +1,73 @@
|
|||||||
# #
|
# #
|
||||||
# Windows image file caches
|
# Binaries
|
||||||
# #
|
# #
|
||||||
|
|
||||||
Thumbs.db
|
*.exe
|
||||||
ehthumbs.db
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# Folder config file
|
# TVApp2 Specific
|
||||||
# #
|
# #
|
||||||
|
|
||||||
Desktop.ini
|
*.dat
|
||||||
|
*.xml
|
||||||
|
*.txt
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# Recycle Bin used on file shares
|
# Test binary
|
||||||
|
# build with `go test -c`
|
||||||
# #
|
# #
|
||||||
|
|
||||||
$RECYCLE.BIN/
|
*.test
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# Windows Installer files
|
# Output for go coverage tool, specifically when used with LiteIDE
|
||||||
# #
|
# #
|
||||||
|
|
||||||
*.cab
|
*.out
|
||||||
*.msi
|
|
||||||
*.msm
|
|
||||||
*.msp
|
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# Windows shortcuts
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
# #
|
# #
|
||||||
|
|
||||||
*.lnk
|
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# Operating System Files
|
# Go workspace file
|
||||||
# #
|
# #
|
||||||
|
|
||||||
.DS_Store
|
go.work
|
||||||
.AppleDouble
|
|
||||||
.LSOverride
|
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# Thumbnails
|
# Mac
|
||||||
# #
|
# #
|
||||||
|
|
||||||
._*
|
.DS_STORE
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# Other
|
# Visual Studio Code
|
||||||
# #
|
# #
|
||||||
|
|
||||||
.Spotlight-V100
|
.vscode/
|
||||||
.Trashes
|
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# Directories potentially created on remote AFP share
|
# Temp folders
|
||||||
# #
|
# #
|
||||||
|
|
||||||
.AppleDB
|
.temp/
|
||||||
.AppleDesktop
|
temp/
|
||||||
Network Trash Folder
|
work/
|
||||||
Temporary Items
|
|
||||||
.apdisk
|
# #
|
||||||
|
# Python Cache
|
||||||
|
# #
|
||||||
|
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
|
# #
|
||||||
|
# logs
|
||||||
|
# #
|
||||||
|
|
||||||
|
.log
|
||||||
|
|||||||
389
CONTRIBUTING.md
389
CONTRIBUTING.md
@@ -1,389 +0,0 @@
|
|||||||
<div align="center">
|
|
||||||
<h6>Thank you for your interest in contributing!</h6>
|
|
||||||
<h1>♾️ Contributing ♾️</h1>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[![Version][github-version-img]][github-version-uri]
|
|
||||||
[![Build Status][github-build-img]][github-build-uri]
|
|
||||||
[![Downloads][github-downloads-img]][github-downloads-uri]
|
|
||||||
[![Size][github-size-img]][github-size-img]
|
|
||||||
[![Last Commit][github-commit-img]][github-commit-img]
|
|
||||||
[![Contributors][contribs-all-img]](#contributors-)
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
## About
|
|
||||||
|
|
||||||
Below are a list of ways that you can help contribute to this project, as well as policies and guides that explain how to get started.
|
|
||||||
|
|
||||||
Please review everything on this page before you submit your contribution.
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
- [About](#about)
|
|
||||||
- [Issues, Bugs, Ideas](#issues-bugs-ideas)
|
|
||||||
- [Contributing](#contributing)
|
|
||||||
- [Before Submitting Pull Requests](#before-submitting-pull-requests)
|
|
||||||
- [Conventional Commit Specification](#conventional-commit-specification)
|
|
||||||
- [Types](#types)
|
|
||||||
- [Example 1:](#example-1)
|
|
||||||
- [Example 2:](#example-2)
|
|
||||||
- [Commiting](#commiting)
|
|
||||||
- [Commenting](#commenting)
|
|
||||||
- [Casing](#casing)
|
|
||||||
- [Indentation Style](#indentation-style)
|
|
||||||
- [Spaces Instead Of Tabs](#spaces-instead-of-tabs)
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
## Issues, Bugs, Ideas
|
|
||||||
Stuff happens, and sometimes as best as we try, there may be issues within this project that we are unaware of. That is the great thing about open-source; anyone can use the program and contribute to making it better.
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
If you have found a bug, have an issue, or maybe even a cool idea; you can let us know by [submitting it](https://github.com/aetherinox/thetvapp-docker/issues). However, before you submit your new issue, bug report, or feature request; head over to the [Issues Section](https://github.com/aetherinox/thetvapp-docker/issues) and ensure nobody else has already submitted it.
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
Once you are sure that your issue has not already being dealt with; you may submit a new issue at [here](https://github.com/aetherinox/thetvapp-docker/issues/new/choose). You'll be asked to specify exactly what your new submission targets, such as:
|
|
||||||
- Bug report
|
|
||||||
- Feature Suggestion
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
When writing a new submission; ensure you fill out any of the questions asked of you. If you do not provide enough information, we cannot help. Be as detailed as possible, and provide any logs or screenshots you may have to help us better understand what you mean. Failure to fill out the submission properly may result in it being closed without a response.
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
If you are submitting a bug report:
|
|
||||||
|
|
||||||
- Explain the issue
|
|
||||||
- Describe how you expect for a feature to work, and what you're seeing instead of what you expected.
|
|
||||||
- List possible options for a resolution or insight
|
|
||||||
- Provide screenshots, logs, or anything else that can visually help track down the issue.
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
|
|
||||||
[![Submit Issue][btn-github-submit-img]][btn-github-submit-uri]
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
|
|
||||||
**[`^ back to top ^`](#about)**
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
If you are looking to contribute to this project by actually submit your own code; please review this section completely. There is important information and policies provided below that you must follow for your pull request to get accepted.
|
|
||||||
|
|
||||||
The source is here for everyone to collectively share and colaborate on. If you think you have a possible solution to a problem; don't be afraid to get your hands dirty.
|
|
||||||
|
|
||||||
All contributions are made via pull requests. To create a pull request, you need a GitHub account. If you are unclear on this process, see [GitHub's documentation on forking and pull requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork). Pull requests should be targeted at the master branch.
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
### Before Submitting Pull Requests
|
|
||||||
|
|
||||||
- Follow the repository's code formatting conventions (see below);
|
|
||||||
- Include tests that prove that the change works as intended and does not add regressions;
|
|
||||||
- Document the changes in the code and/or the project's documentation;
|
|
||||||
- Your PR must pass the CI pipeline;
|
|
||||||
- When submitting your Pull Request, use one of the following branches:
|
|
||||||
- For bug fixes: `main` branch
|
|
||||||
- For features & functionality: `development` branch
|
|
||||||
- Include a proper git commit message following the [Conventional Commit Specification](https://www.conventionalcommits.org/en/v1.0.0/#specification).
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
If you have completed the above tasks, the pull request is ready to be reviewed and your pull request's label will be changed to "Ready for Review". At this point, a human will need to step in and manually verify your submission.
|
|
||||||
|
|
||||||
Reviewers will approve the pull request once they are satisfied with the patch it will be merged.
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
### Conventional Commit Specification
|
|
||||||
|
|
||||||
When commiting your changes, we require you to follow the [Conventional Commit Specification](https://www.conventionalcommits.org/en/v1.0.0/#specification). The **Conventional Commits** is a specification for the format and content of a commit message. The concept behind Conventional Commits is to provide a rich commit history that can be read and understood by both humans and automated tools. Conventional Commits have the following format:
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
```
|
|
||||||
<type>[(optional <scope>)]: <description>
|
|
||||||
|
|
||||||
[optional <body>]
|
|
||||||
|
|
||||||
[optional <footer(s)>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
#### Types
|
|
||||||
| Type | Description |
|
|
||||||
| --- | --- |
|
|
||||||
| `feat` | Introduce new feature |
|
|
||||||
| `fix` | Bug fix |
|
|
||||||
| `deps` | Add or update existing dependencies |
|
|
||||||
| `docs` | Change website or markdown documents. Does not mean changes to the documentation generator script itself, only the documents created from the generator. <br/><br/><small>E.g: documentation, readme.md or markdown</small> <br /><br /> |
|
|
||||||
| `build` | Changes to the build / compilation / packaging process or auxiliary tools such as doc generation<br /><br/><small>E.g: create new build tasks, update release script, etc.</small> |
|
|
||||||
| `test` | Add or refactor tests, no production code change. Changes the suite of automated tests for the app. |
|
|
||||||
| `perf` | Performance improvement of algorithms or execution time of the app. Does not change an existing feature. |
|
|
||||||
| `style` | Update / reformat style of source code. Does not change the way app is implemented. Changes that do not affect the meaning of the code<br /><br/><small>E.g: white-space, formatting, missing semi-colons, change tabs to spaces, etc)</small> |
|
|
||||||
| `refactor` | Change to production code that leads to no behavior difference,<br/><br/><small>E.g: split files, rename variables, rename package, improve code style, etc.</small> |
|
|
||||||
| `change` | Change an existing feature. |
|
|
||||||
| `chore` | Includes technical or preventative maintenance task that is necessary for managing the app or repo, such as updating grunt tasks, but is not tied to any specific feature. Usually done for maintanence purposes.<br/><br/><small>E.g: Edit .gitignore, .prettierrc, .prettierignore, .gitignore, eslint.config.js file</small> |
|
|
||||||
| `ci` | Changes related to Continuous Integration (usually `yml` and other configuration files). |
|
|
||||||
| `misc` | Anything that doesn't fit into another commit type. Usually doesn't change production code; yet is not ci, test or chore. |
|
|
||||||
| `revert` | Revert a previous commit |
|
|
||||||
| `remove` | Remove a feature from app. Features are usually first deprecated for a period of time before being removed. Removing a feature from the app may be considered a breaking change that will require a major version number increment.|
|
|
||||||
| `deprecate` | Deprecate existing functionality, but does not remove it from the app.|
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
##### Example 1:
|
|
||||||
|
|
||||||
```
|
|
||||||
feat(core): bug affecting menu [#22]
|
|
||||||
^───^────^ ^────────────────^ ^───^
|
|
||||||
| | | |
|
|
||||||
| | | └───⫸ (ISSUE): Reference issue ID
|
|
||||||
│ │ │
|
|
||||||
│ │ └───⫸ (DESC): Summary in present tense. Use lower case not title case!
|
|
||||||
│ │
|
|
||||||
│ └───────────⫸ (SCOPE): The package(s) that this change affects
|
|
||||||
│
|
|
||||||
└───────────────⫸ (TYPE): See list above
|
|
||||||
```
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
##### Example 2:
|
|
||||||
```
|
|
||||||
<type>(<scope>): <short summary> [issue]
|
|
||||||
| | | |
|
|
||||||
| | | └─⫸ Reference issue id (optional)
|
|
||||||
│ │ │
|
|
||||||
│ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
|
|
||||||
│ │
|
|
||||||
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
|
||||||
│ elements|forms|http|language-service|localize|platform-browser|
|
|
||||||
│ platform-browser-dynamic|platform-server|router|service-worker|
|
|
||||||
│ upgrade|zone.js|packaging|changelog|docs-infra|migrations|ngcc|ve|
|
|
||||||
│ devtools....
|
|
||||||
│
|
|
||||||
└─⫸ Commit Type: build|ci|doc|docs|feat|fix|perf|refactor|test
|
|
||||||
website|chore|style|type|revert|deprecate
|
|
||||||
```
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
### Commiting
|
|
||||||
If you are pushing a commit which addresses a submitted issue, reference your issue at the end of the commit message. You may also optionally add the major issue to the end of your commit body.
|
|
||||||
|
|
||||||
References should be on their own line, following the word `Ref` or `Refs`
|
|
||||||
|
|
||||||
```
|
|
||||||
Title: fix(core): fix error message displayed to users. [#22]
|
|
||||||
Description: The description of your commit
|
|
||||||
|
|
||||||
Ref: #22, #34, #37
|
|
||||||
```
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
### Commenting
|
|
||||||
|
|
||||||
Comment your code. If someone else comes along, they should be able to do a quick glance and have an idea of what is going on. Plus it helps novice readers to better understand the process.
|
|
||||||
|
|
||||||
You may use block style commenting, or single lines:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# #
|
|
||||||
# set perms and import user crontabs
|
|
||||||
# #
|
|
||||||
|
|
||||||
checkown "${cron_user}":"${cron_user}" "/config/crontabs/${cron_user}"
|
|
||||||
crontab -u "${cron_user}" "/config/crontabs/${cron_user}"
|
|
||||||
```
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
At the top of any new file introduced, please add the following header:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/usr/bin/with-contenv bash
|
|
||||||
# shellcheck shell=bash
|
|
||||||
|
|
||||||
# #
|
|
||||||
# @project thetvapp-docker
|
|
||||||
# @about DESCRIPTION OF WHAT FILE DOES
|
|
||||||
# @file /path/to/file.ext
|
|
||||||
# @repo https://github.com/Aetherinox/thetvapp-docker
|
|
||||||
# #
|
|
||||||
```
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
### Casing
|
|
||||||
|
|
||||||
When calling environment variables, you should use `UPPERCASE`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
arg_cron=$(echo ${CRON_TIME})
|
|
||||||
if [ -z "${arg_cron}" ]; then
|
|
||||||
arg_cron="0/60 * * * *"
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
When defining general variables, use `snake_case`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
migrations_dir="/migrations"
|
|
||||||
migrations_history="/config/.migrations"
|
|
||||||
```
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
### Indentation Style
|
|
||||||
You should be using the `Allman Style`. This style puts the brace associated with a control statement on the next line, indented. Statements within the braces are indented to the same level as the braces.
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
location ~ ^(.+\.php)(.*)$
|
|
||||||
{
|
|
||||||
# enable the next two lines for http auth
|
|
||||||
# auth_basic "Restricted";
|
|
||||||
# auth_basic_user_file /config/nginx/.htpasswd;
|
|
||||||
|
|
||||||
fastcgi_split_path_info ^(.+\.php)(.*)$;
|
|
||||||
if (!-f $document_root$fastcgi_script_name) { return 404; }
|
|
||||||
fastcgi_pass 127.0.0.1:9000;
|
|
||||||
fastcgi_index index.php;
|
|
||||||
include /etc/nginx/fastcgi_params;
|
|
||||||
}
|
|
||||||
|
|
||||||
# deny access to .htaccess/.htpasswd files
|
|
||||||
location ~ /\.ht
|
|
||||||
{
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
### Spaces Instead Of Tabs
|
|
||||||
When writing your code, set your IDE to utilize **spaces**, with a configured size of `4 characters`. If this project utilizes ESLint, you should find the file `.editorconfig` in the root directory of the repo which defines how the file should be formatted. Load that file into programs such as Visual Studio Code.
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
|
|
||||||
**[`^ back to top ^`](#about)**
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
<!-- markdownlint-disable -->
|
|
||||||
|
|
||||||
<!-- BADGE > GENERAL -->
|
|
||||||
[general-npmjs-uri]: https://npmjs.com
|
|
||||||
[general-nodejs-uri]: https://nodejs.org
|
|
||||||
[general-npmtrends-uri]: http://npmtrends.com/thetvapp-docker
|
|
||||||
|
|
||||||
<!-- BADGE > VERSION > GITHUB -->
|
|
||||||
[github-version-img]: https://img.shields.io/github/v/tag/Aetherinox/thetvapp-docker?logo=GitHub&label=Version&color=ba5225
|
|
||||||
[github-version-uri]: https://github.com/Aetherinox/thetvapp-docker/releases
|
|
||||||
|
|
||||||
<!-- BADGE > VERSION > NPMJS -->
|
|
||||||
[npm-version-img]: https://img.shields.io/npm/v/thetvapp-docker?logo=npm&label=Version&color=ba5225
|
|
||||||
[npm-version-uri]: https://npmjs.com/package/thetvapp-docker
|
|
||||||
|
|
||||||
<!-- BADGE > VERSION > PYPI -->
|
|
||||||
[pypi-version-img]: https://img.shields.io/pypi/v/thetvapp-docker-plugin
|
|
||||||
[pypi-version-uri]: https://pypi.org/project/thetvapp-docker-plugin/
|
|
||||||
|
|
||||||
<!-- BADGE > LICENSE > MIT -->
|
|
||||||
[license-mit-img]: https://img.shields.io/badge/MIT-FFF?logo=creativecommons&logoColor=FFFFFF&label=License&color=9d29a0
|
|
||||||
[license-mit-uri]: https://github.com/Aetherinox/thetvapp-docker/blob/main/LICENSE
|
|
||||||
|
|
||||||
<!-- BADGE > GITHUB > DOWNLOAD COUNT -->
|
|
||||||
[github-downloads-img]: https://img.shields.io/github/downloads/Aetherinox/thetvapp-docker/total?logo=github&logoColor=FFFFFF&label=Downloads&color=376892
|
|
||||||
[github-downloads-uri]: https://github.com/Aetherinox/thetvapp-docker/releases
|
|
||||||
|
|
||||||
<!-- BADGE > NPMJS > DOWNLOAD COUNT -->
|
|
||||||
[npmjs-downloads-img]: https://img.shields.io/npm/dw/%40aetherinox%2Fmkdocs-link-embeds?logo=npm&&label=Downloads&color=376892
|
|
||||||
[npmjs-downloads-uri]: https://npmjs.com/package/thetvapp-docker
|
|
||||||
|
|
||||||
<!-- BADGE > GITHUB > DOWNLOAD SIZE -->
|
|
||||||
[github-size-img]: https://img.shields.io/github/repo-size/Aetherinox/thetvapp-docker?logo=github&label=Size&color=59702a
|
|
||||||
[github-size-uri]: https://github.com/Aetherinox/thetvapp-docker/releases
|
|
||||||
|
|
||||||
<!-- BADGE > NPMJS > DOWNLOAD SIZE -->
|
|
||||||
[npmjs-size-img]: https://img.shields.io/npm/unpacked-size/thetvapp-docker/latest?logo=npm&label=Size&color=59702a
|
|
||||||
[npmjs-size-uri]: https://npmjs.com/package/thetvapp-docker
|
|
||||||
|
|
||||||
<!-- BADGE > CODECOV > COVERAGE -->
|
|
||||||
[codecov-coverage-img]: https://img.shields.io/codecov/c/github/Aetherinox/thetvapp-docker?token=MPAVASGIOG&logo=codecov&logoColor=FFFFFF&label=Coverage&color=354b9e
|
|
||||||
[codecov-coverage-uri]: https://codecov.io/github/Aetherinox/thetvapp-docker
|
|
||||||
|
|
||||||
<!-- BADGE > ALL CONTRIBUTORS -->
|
|
||||||
[contribs-all-img]: https://img.shields.io/github/all-contributors/Aetherinox/thetvapp-docker?logo=contributorcovenant&color=de1f6f&label=contributors
|
|
||||||
[contribs-all-uri]: https://github.com/all-contributors/all-contributors
|
|
||||||
|
|
||||||
<!-- BADGE > GITHUB > BUILD > NPM -->
|
|
||||||
[github-build-img]: https://img.shields.io/github/actions/workflow/status/Aetherinox/thetvapp-docker/deploy-docker.yml?logo=github&logoColor=FFFFFF&label=Build&color=%23278b30
|
|
||||||
[github-build-uri]: https://github.com/Aetherinox/thetvapp-docker/actions/workflows/deploy-docker.yml
|
|
||||||
|
|
||||||
<!-- BADGE > GITHUB > BUILD > Pypi -->
|
|
||||||
[github-build-pypi-img]: https://img.shields.io/github/actions/workflow/status/Aetherinox/thetvapp-docker/release-pypi.yml?logo=github&logoColor=FFFFFF&label=Build&color=%23278b30
|
|
||||||
[github-build-pypi-uri]: https://github.com/Aetherinox/thetvapp-docker/actions/workflows/pypi-release.yml
|
|
||||||
|
|
||||||
<!-- BADGE > GITHUB > TESTS -->
|
|
||||||
[github-tests-img]: https://img.shields.io/github/actions/workflow/status/Aetherinox/thetvapp-docker/npm-tests.yml?logo=github&label=Tests&color=2c6488
|
|
||||||
[github-tests-uri]: https://github.com/Aetherinox/thetvapp-docker/actions/workflows/npm-tests.yml
|
|
||||||
|
|
||||||
<!-- BADGE > GITHUB > COMMIT -->
|
|
||||||
[github-commit-img]: https://img.shields.io/github/last-commit/Aetherinox/thetvapp-docker?logo=conventionalcommits&logoColor=FFFFFF&label=Last%20Commit&color=313131
|
|
||||||
[github-commit-uri]: https://github.com/Aetherinox/thetvapp-docker/commits/main/
|
|
||||||
|
|
||||||
<!-- BADGE > BUTTON > SUBMIT ISSUES -->
|
|
||||||
[btn-github-submit-img]: https://img.shields.io/badge/submit%20new%20issue-de1f5c?style=for-the-badge&logo=github&logoColor=FFFFFF
|
|
||||||
[btn-github-submit-uri]: https://github.com/aetherinox/thetvapp-docker/issues
|
|
||||||
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
<!-- markdownlint-restore -->
|
|
||||||
165
Dockerfile
165
Dockerfile
@@ -1,118 +1,127 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
# #
|
# #
|
||||||
|
# @project TVApp2
|
||||||
|
# @usage docker image which allows you to download a m3u playlist and EPG guide data from
|
||||||
|
# multiple IPTV services.
|
||||||
# @file Dockerfile
|
# @file Dockerfile
|
||||||
# @about This docker file installs:
|
# @repo https://github.com/iFlip721/tvapp2
|
||||||
# - nginx
|
# https://github.com/aetherinox/tvapp2
|
||||||
# - php-fpm
|
# https://github.com/aetherinox/docker-base-alpine
|
||||||
# - theapptv
|
# https://git.binaryninja.net/pub_projects/tvapp2
|
||||||
|
#
|
||||||
|
# you can build your own image by running
|
||||||
|
# amd64 docker build --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:latest -t tvapp2:1.0.0 -t tvapp2:1.0.0-amd64 -f Dockerfile .
|
||||||
|
# arm64 docker build --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:1.0.0-arm64 -f Dockerfile.aarch64 .
|
||||||
|
#
|
||||||
|
# if you prefer to use `docker buildx`
|
||||||
|
# create docker buildx create --driver docker-container --name container --bootstrap --use
|
||||||
|
# amd64 docker buildx build --no-cache --pull --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:latest -t tvapp2:1.0.0 --platform=linux/amd64 --output type=docker --output type=docker .
|
||||||
|
# arm64 docker buildx build --no-cache --pull --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:latest -t tvapp2:1.0.0 --platform=linux/arm64 --output type=docker --output type=docker .
|
||||||
# #
|
# #
|
||||||
|
|
||||||
# #
|
|
||||||
# Base Image
|
|
||||||
# This container uses a modified version of the Linux server alpine image
|
|
||||||
# #
|
|
||||||
|
|
||||||
FROM ghcr.io/linuxserver/baseimage-alpine:3.20
|
FROM ghcr.io/aetherinox/alpine-base:3.20-amd64
|
||||||
|
|
||||||
# #
|
|
||||||
# Set Labels
|
|
||||||
# #
|
|
||||||
|
|
||||||
LABEL maintainer="Aetherinox"
|
|
||||||
LABEL org.opencontainers.image.authors="Aetherinox"
|
|
||||||
LABEL org.opencontainers.image.vendor="Aetherinox"
|
|
||||||
LABEL org.opencontainers.image.title="TheTVApp Grabber"
|
|
||||||
LABEL org.opencontainers.image.description="thetvapp image by Aetherinox"
|
|
||||||
LABEL org.opencontainers.image.source="https://github.com/Aetherinox/thetvapp-docker"
|
|
||||||
LABEL org.opencontainers.image.documentation="https://github.com/Aetherinox/thetvapp-docker"
|
|
||||||
LABEL org.opencontainers.image.url="https://github.com/Aetherinox/thetvapp-docker"
|
|
||||||
LABEL org.opencontainers.image.licenses="MIT"
|
|
||||||
LABEL build_version="1.0.0"
|
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# Set Args
|
# Set Args
|
||||||
# #
|
# #
|
||||||
|
|
||||||
ARG BUILD_DATE
|
ARG BUILDDATE
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
ARG NGINX_VERSION
|
|
||||||
ARG CRON_TIME
|
# #
|
||||||
ENV CRON_TIME="0/60 * * * *"
|
# Set Labels
|
||||||
|
# #
|
||||||
|
|
||||||
|
LABEL maintainer="aetherinox, iFlip721"
|
||||||
|
LABEL org.opencontainers.image.authors="aetherinox, iFlip721"
|
||||||
|
LABEL org.opencontainers.image.vendor="BinaryNinja"
|
||||||
|
LABEL org.opencontainers.image.title="TvApp m3u playlist and EPG guide downloader"
|
||||||
|
LABEL org.opencontainers.image.description="Download m3u playlist and EPG guide data for the IPTV service TheTVApp"
|
||||||
|
LABEL org.opencontainers.image.source="https://git.binaryninja.net/pub_projects/tvapp2"
|
||||||
|
LABEL org.opencontainers.image.documentation="https://git.binaryninja.net/pub_projects/tvapp2/wiki"
|
||||||
|
LABEL org.opencontainers.image.url="https://git.binaryninja.net/pub_projects/tvapp2/packages"
|
||||||
|
LABEL org.opencontainers.image.licenses="MIT"
|
||||||
|
LABEL build_version="TvApp2 v${VERSION} build-date: ${BUILDDATE}"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Set Env Var
|
||||||
|
# #
|
||||||
|
|
||||||
ENV TZ="Etc/UTC"
|
ENV TZ="Etc/UTC"
|
||||||
|
ENV URL_REPO_BASE="https://github.com/aetherinox/alpine-base/pkgs/container/alpine-base"
|
||||||
ENV URL_XML="https://raw.githubusercontent.com/dtankdempse/thetvapp-m3u/refs/heads/main/guide/epg.xml"
|
ENV URL_REPO_APP="https://git.binaryninja.net/pub_projects/tvapp2"
|
||||||
ENV URL_XML_GZ="https://raw.githubusercontent.com/dtankdempse/thetvapp-m3u/refs/heads/main/guide/epg.xml.gz"
|
ENV FILE_NAME="index.html"
|
||||||
ENV URL_M3U="https://thetvapp-m3u.data-search.workers.dev/playlist"
|
ENV PORT_HTTP=4124
|
||||||
ENV FILE_NAME="thetvapp"
|
ENV NODE_VERSION=18.20.5
|
||||||
|
ENV YARN_VERSION=1.22.22
|
||||||
ENV PORT_HTTP=80
|
|
||||||
ENV PORT_HTTPS=443
|
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# Install
|
# Install
|
||||||
# #
|
# #
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
if [ -z ${NGINX_VERSION+x} ]; then \
|
apk add --no-cache \
|
||||||
NGINX_VERSION=$(curl -sL "http://dl-cdn.alpinelinux.org/alpine/v3.20/main/x86_64/APKINDEX.tar.gz" | tar -xz -C /tmp \
|
wget \
|
||||||
&& awk '/^P:nginx$/,/V:/' /tmp/APKINDEX | sed -n 2p | sed 's/^V://'); \
|
bash \
|
||||||
fi && \
|
nano \
|
||||||
apk add --no-cache \
|
npm \
|
||||||
wget \
|
openssl
|
||||||
logrotate \
|
|
||||||
openssl \
|
# #
|
||||||
apache2-utils \
|
# Copy docker-entrypoint
|
||||||
nginx \
|
# #
|
||||||
nginx==${NGINX_VERSION} \
|
|
||||||
nginx-mod-http-fancyindex==${NGINX_VERSION} && \
|
COPY docker-entrypoint.sh /usr/local/bin/
|
||||||
echo "**** Install Build Packages ****" && \
|
|
||||||
echo "**** Configure Nginx ****" && \
|
|
||||||
echo 'fastcgi_param HTTP_PROXY ""; # https://httpoxy.org/' >> \
|
|
||||||
/etc/nginx/fastcgi_params && \
|
|
||||||
echo 'fastcgi_param PATH_INFO $fastcgi_path_info; # http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_split_path_info' >> \
|
|
||||||
/etc/nginx/fastcgi_params && \
|
|
||||||
echo 'fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/#connecting-nginx-to-php-fpm' >> \
|
|
||||||
/etc/nginx/fastcgi_params && \
|
|
||||||
echo 'fastcgi_param SERVER_NAME $host; # Send HTTP_HOST as SERVER_NAME. If HTTP_HOST is blank, send the value of server_name from nginx (default is `_`)' >> \
|
|
||||||
/etc/nginx/fastcgi_params && \
|
|
||||||
rm -f /etc/nginx/http.d/default.conf && \
|
|
||||||
rm -f /etc/nginx/conf.d/stream.conf && \
|
|
||||||
rm -f /config/www/index.html && \
|
|
||||||
echo "**** Setup Logrotate ****" && \
|
|
||||||
sed -i "s#/var/log/messages {}.*# #g" \
|
|
||||||
/etc/logrotate.conf && \
|
|
||||||
sed -i 's#/usr/sbin/logrotate /etc/logrotate.conf#/usr/sbin/logrotate /etc/logrotate.conf -s /config/log/logrotate.status#g' \
|
|
||||||
/etc/periodic/daily/logrotate
|
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# Set work directory
|
# Set work directory
|
||||||
# #
|
# #
|
||||||
|
|
||||||
WORKDIR /config/www
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# add local files
|
# copy node package.json to workdir
|
||||||
|
# #
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# #
|
||||||
|
# install node (production)
|
||||||
|
# #
|
||||||
|
|
||||||
|
RUN npm install --only=production
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Add local files
|
||||||
|
# #
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
# COPY node_modules/ package.json package-lock.json formatted.dat index.js ./
|
||||||
|
|
||||||
|
# #
|
||||||
|
# when copying with the command above, all files in root folder will be copied.
|
||||||
|
# #
|
||||||
|
|
||||||
|
RUN rm -rf ./root
|
||||||
|
RUN rm ./Dockerfile ./Dockerfile.aarch64 docker-entrypoint.sh
|
||||||
|
|
||||||
|
# #
|
||||||
|
# copy s6-overlays root to image root
|
||||||
# #
|
# #
|
||||||
|
|
||||||
COPY root/ /
|
COPY root/ /
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# ports and volumes
|
# Ports and volumes
|
||||||
# #
|
# #
|
||||||
|
|
||||||
EXPOSE ${PORT_HTTP} ${PORT_HTTPS}
|
EXPOSE ${PORT_HTTP}/tcp
|
||||||
|
|
||||||
# #
|
|
||||||
# Add Cron Task Files
|
|
||||||
# #
|
|
||||||
|
|
||||||
ADD run.sh /
|
|
||||||
ADD download.sh /
|
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# In case user sets up the cron for a longer duration, do a first run
|
# In case user sets up the cron for a longer duration, do a first run
|
||||||
# and then keep the container running. Hacky, but whatever.
|
# and then keep the container running. Hacky, but whatever.
|
||||||
# #
|
# #
|
||||||
|
|
||||||
CMD ["sh", "-c", "/run.sh ; /download.sh ; tail -f /dev/null"]
|
CMD ["sh", "-c", "npm start"]
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
# syntax=docker/dockerfile:1
|
|
||||||
|
|
||||||
# #
|
|
||||||
# @file Dockerfile.IncPhp
|
|
||||||
# @about This docker file installs:
|
|
||||||
# - nginx
|
|
||||||
# - php-fpm
|
|
||||||
# - theapptv
|
|
||||||
# #
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Base Image
|
|
||||||
# This container uses a modified version of the Linux server alpine image
|
|
||||||
# #
|
|
||||||
|
|
||||||
FROM ghcr.io/linuxserver/baseimage-alpine:3.20
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Set Labels
|
|
||||||
# #
|
|
||||||
|
|
||||||
LABEL maintainer="Aetherinox"
|
|
||||||
LABEL org.opencontainers.image.authors="Aetherinox"
|
|
||||||
LABEL org.opencontainers.image.vendor="Aetherinox"
|
|
||||||
LABEL org.opencontainers.image.title="TheTVApp Grabber"
|
|
||||||
LABEL org.opencontainers.image.description="thetvapp image by Aetherinox"
|
|
||||||
LABEL org.opencontainers.image.source="https://github.com/Aetherinox/thetvapp-docker"
|
|
||||||
LABEL org.opencontainers.image.documentation="https://github.com/Aetherinox/thetvapp-docker"
|
|
||||||
LABEL org.opencontainers.image.url="https://github.com/Aetherinox/thetvapp-docker"
|
|
||||||
LABEL org.opencontainers.image.licenses="MIT"
|
|
||||||
LABEL build_version="1.0.0"
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Set Args
|
|
||||||
# #
|
|
||||||
|
|
||||||
ARG BUILD_DATE
|
|
||||||
ARG VERSION
|
|
||||||
ARG NGINX_VERSION
|
|
||||||
ARG CRON_TIME
|
|
||||||
ENV CRON_TIME="0/60 * * * *"
|
|
||||||
ENV TZ="Etc/UTC"
|
|
||||||
|
|
||||||
ENV URL_XML="https://raw.githubusercontent.com/dtankdempse/thetvapp-m3u/refs/heads/main/guide/epg.xml"
|
|
||||||
ENV URL_XML_GZ="https://raw.githubusercontent.com/dtankdempse/thetvapp-m3u/refs/heads/main/guide/epg.xml.gz"
|
|
||||||
ENV URL_M3U="https://thetvapp-m3u.data-search.workers.dev/playlist"
|
|
||||||
ENV FILE_NAME="thetvapp"
|
|
||||||
|
|
||||||
ENV PORT_HTTP=80
|
|
||||||
ENV PORT_HTTPS=443
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Install
|
|
||||||
# #
|
|
||||||
|
|
||||||
RUN \
|
|
||||||
if [ -z ${NGINX_VERSION+x} ]; then \
|
|
||||||
NGINX_VERSION=$(curl -sL "http://dl-cdn.alpinelinux.org/alpine/v3.20/main/x86_64/APKINDEX.tar.gz" | tar -xz -C /tmp \
|
|
||||||
&& awk '/^P:nginx$/,/V:/' /tmp/APKINDEX | sed -n 2p | sed 's/^V://'); \
|
|
||||||
fi && \
|
|
||||||
apk add --no-cache \
|
|
||||||
wget \
|
|
||||||
logrotate \
|
|
||||||
openssl \
|
|
||||||
apache2-utils \
|
|
||||||
nginx \
|
|
||||||
php83 \
|
|
||||||
php83-fileinfo \
|
|
||||||
php83-fpm \
|
|
||||||
php83-mbstring \
|
|
||||||
nginx==${NGINX_VERSION} \
|
|
||||||
nginx-mod-http-fancyindex==${NGINX_VERSION} && \
|
|
||||||
echo "**** Install Build Packages ****" && \
|
|
||||||
echo "**** Configure Nginx ****" && \
|
|
||||||
echo 'fastcgi_param HTTP_PROXY ""; # https://httpoxy.org/' >> \
|
|
||||||
/etc/nginx/fastcgi_params && \
|
|
||||||
echo 'fastcgi_param PATH_INFO $fastcgi_path_info; # http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_split_path_info' >> \
|
|
||||||
/etc/nginx/fastcgi_params && \
|
|
||||||
echo 'fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/#connecting-nginx-to-php-fpm' >> \
|
|
||||||
/etc/nginx/fastcgi_params && \
|
|
||||||
echo 'fastcgi_param SERVER_NAME $host; # Send HTTP_HOST as SERVER_NAME. If HTTP_HOST is blank, send the value of server_name from nginx (default is `_`)' >> \
|
|
||||||
/etc/nginx/fastcgi_params && \
|
|
||||||
rm -f /etc/nginx/http.d/default.conf && \
|
|
||||||
rm -f /etc/nginx/conf.d/stream.conf && \
|
|
||||||
rm -f /config/www/index.html && \
|
|
||||||
echo "**** Check PHP version and symlink ****" && \
|
|
||||||
if [ "$(readlink /usr/bin/php)" != "php83" ]; then \
|
|
||||||
rm -rf /usr/bin/php && \
|
|
||||||
ln -s /usr/bin/php83 /usr/bin/php; \
|
|
||||||
fi && \
|
|
||||||
echo "**** Configure PHP ****" && \
|
|
||||||
sed -i "s#;error_log = log/php83/error.log.*#error_log = /config/log/php/error.log#g" \
|
|
||||||
/etc/php83/php-fpm.conf && \
|
|
||||||
sed -i "s#user = nobody.*#user = abc#g" \
|
|
||||||
/etc/php83/php-fpm.d/www.conf && \
|
|
||||||
sed -i "s#group = nobody.*#group = abc#g" \
|
|
||||||
/etc/php83/php-fpm.d/www.conf && \
|
|
||||||
echo "**** Setup Logrotate ****" && \
|
|
||||||
sed -i "s#/var/log/messages {}.*# #g" \
|
|
||||||
/etc/logrotate.conf && \
|
|
||||||
sed -i 's#/usr/sbin/logrotate /etc/logrotate.conf#/usr/sbin/logrotate /etc/logrotate.conf -s /config/log/logrotate.status#g' \
|
|
||||||
/etc/periodic/daily/logrotate
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Set work directory
|
|
||||||
# #
|
|
||||||
|
|
||||||
WORKDIR /config/www
|
|
||||||
|
|
||||||
# #
|
|
||||||
# add local files
|
|
||||||
# #
|
|
||||||
|
|
||||||
COPY root/ /
|
|
||||||
|
|
||||||
# #
|
|
||||||
# ports and volumes
|
|
||||||
# #
|
|
||||||
|
|
||||||
EXPOSE ${PORT_HTTP} ${PORT_HTTPS}
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Add Cron Task Files
|
|
||||||
# #
|
|
||||||
|
|
||||||
ADD run.sh /
|
|
||||||
ADD download.sh /
|
|
||||||
|
|
||||||
# #
|
|
||||||
# In case user sets up the cron for a longer duration, do a first run
|
|
||||||
# and then keep the container running. Hacky, but whatever.
|
|
||||||
# #
|
|
||||||
|
|
||||||
CMD ["sh", "-c", "/run.sh ; /download.sh ; tail -f /dev/null"]
|
|
||||||
126
Dockerfile.aarch64
Normal file
126
Dockerfile.aarch64
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# #
|
||||||
|
# @project TVApp2
|
||||||
|
# @usage docker image which allows you to download a m3u playlist and EPG guide data from
|
||||||
|
# multiple IPTV services.
|
||||||
|
# @file Dockerfile
|
||||||
|
# @repo https://github.com/iFlip721/tvapp2
|
||||||
|
# https://github.com/aetherinox/tvapp2
|
||||||
|
# https://github.com/aetherinox/docker-base-alpine
|
||||||
|
# https://git.binaryninja.net/pub_projects/tvapp2
|
||||||
|
#
|
||||||
|
# you can build your own image by running
|
||||||
|
# amd64 docker build --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:latest -t tvapp2:1.0.0 -t tvapp2:1.0.0-amd64 -f Dockerfile .
|
||||||
|
# arm64 docker build --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:1.0.0-arm64 -f Dockerfile.aarch64 .
|
||||||
|
#
|
||||||
|
# if you prefer to use `docker buildx`
|
||||||
|
# create docker buildx create --driver docker-container --name container --bootstrap --use
|
||||||
|
# amd64 docker buildx build --no-cache --pull --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:latest -t tvapp2:1.0.0 --platform=linux/amd64 --output type=docker --output type=docker .
|
||||||
|
# arm64 docker buildx build --no-cache --pull --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:latest -t tvapp2:1.0.0 --platform=linux/arm64 --output type=docker --output type=docker .
|
||||||
|
# #
|
||||||
|
|
||||||
|
FROM ghcr.io/aetherinox/alpine-base:3.20-arm64
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Set Args
|
||||||
|
# #
|
||||||
|
|
||||||
|
ARG BUILDDATE
|
||||||
|
ARG VERSION
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Set Labels
|
||||||
|
# #
|
||||||
|
|
||||||
|
LABEL maintainer="aetherinox, iFlip721"
|
||||||
|
LABEL org.opencontainers.image.authors="aetherinox, iFlip721"
|
||||||
|
LABEL org.opencontainers.image.vendor="BinaryNinja"
|
||||||
|
LABEL org.opencontainers.image.title="TvApp m3u playlist and EPG guide downloader"
|
||||||
|
LABEL org.opencontainers.image.description="Download m3u playlist and EPG guide data for the IPTV service TheTVApp"
|
||||||
|
LABEL org.opencontainers.image.source="https://git.binaryninja.net/pub_projects/tvapp2"
|
||||||
|
LABEL org.opencontainers.image.documentation="https://git.binaryninja.net/pub_projects/tvapp2/wiki"
|
||||||
|
LABEL org.opencontainers.image.url="https://git.binaryninja.net/pub_projects/tvapp2/packages"
|
||||||
|
LABEL org.opencontainers.image.licenses="MIT"
|
||||||
|
LABEL build_version="TvApp2 v${VERSION} build-date: ${BUILDDATE}"
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Set Env Var
|
||||||
|
# #
|
||||||
|
|
||||||
|
ENV TZ="Etc/UTC"
|
||||||
|
ENV URL_REPO_BASE="https://github.com/aetherinox/alpine-base/pkgs/container/alpine-base"
|
||||||
|
ENV URL_REPO_APP="https://git.binaryninja.net/pub_projects/tvapp2"
|
||||||
|
ENV FILE_NAME="index.html"
|
||||||
|
ENV PORT_HTTP=4124
|
||||||
|
ENV NODE_VERSION=18.20.5
|
||||||
|
ENV YARN_VERSION=1.22.22
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Install
|
||||||
|
# #
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apk add --no-cache \
|
||||||
|
wget \
|
||||||
|
bash \
|
||||||
|
nano \
|
||||||
|
npm \
|
||||||
|
openssl
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Copy docker-entrypoint
|
||||||
|
# #
|
||||||
|
|
||||||
|
COPY docker-entrypoint.sh /usr/local/bin/
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Set work directory
|
||||||
|
# #
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
# #
|
||||||
|
# copy node package.json to workdir
|
||||||
|
# #
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# #
|
||||||
|
# install node (production)
|
||||||
|
# #
|
||||||
|
|
||||||
|
RUN npm install --only=production
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Add local files
|
||||||
|
# #
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
# COPY node_modules/ package.json package-lock.json formatted.dat index.js ./
|
||||||
|
|
||||||
|
# #
|
||||||
|
# when copying with the command above, all files in root folder will be copied.
|
||||||
|
# #
|
||||||
|
|
||||||
|
RUN rm -rf ./root
|
||||||
|
RUN rm ./Dockerfile ./Dockerfile.aarch64 docker-entrypoint.sh
|
||||||
|
|
||||||
|
# #
|
||||||
|
# copy s6-overlays root to image root
|
||||||
|
# #
|
||||||
|
|
||||||
|
COPY root/ /
|
||||||
|
|
||||||
|
# #
|
||||||
|
# Ports and volumes
|
||||||
|
# #
|
||||||
|
|
||||||
|
EXPOSE ${PORT_HTTP}/tcp
|
||||||
|
|
||||||
|
# #
|
||||||
|
# In case user sets up the cron for a longer duration, do a first run
|
||||||
|
# and then keep the container running. Hacky, but whatever.
|
||||||
|
# #
|
||||||
|
|
||||||
|
CMD ["sh", "-c", "npm start"]
|
||||||
20
LICENSE
20
LICENSE
@@ -1,21 +1,9 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Gistr - Copyright (c) 2025 Aetherinox
|
Copyright (c) 2025 pub_projects
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|||||||
32
docker-entrypoint.sh
Normal file
32
docker-entrypoint.sh
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# #
|
||||||
|
# @project TVApp2
|
||||||
|
# @usage docker image which allows you to download a m3u playlist and EPG guide data from
|
||||||
|
# multiple IPTV services.
|
||||||
|
# @file Dockerfile
|
||||||
|
# @repo https://github.com/iFlip721/tvapp2
|
||||||
|
# https://github.com/aetherinox/tvapp2
|
||||||
|
# https://github.com/aetherinox/docker-base-alpine
|
||||||
|
# https://git.binaryninja.net/pub_projects/tvapp2
|
||||||
|
#
|
||||||
|
# you can build your own image by running
|
||||||
|
# amd64 docker build --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:latest -t tvapp2:1.0.0 -t tvapp2:1.0.0-amd64 -f Dockerfile .
|
||||||
|
# arm64 docker build --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:1.0.0-arm64 -f Dockerfile.aarch64 .
|
||||||
|
#
|
||||||
|
# if you prefer to use `docker buildx`
|
||||||
|
# create docker buildx create --driver docker-container --name container --bootstrap --use
|
||||||
|
# amd64 docker buildx build --no-cache --pull --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:latest -t tvapp2:1.0.0 --platform=linux/amd64 --output type=docker --output type=docker .
|
||||||
|
# arm64 docker buildx build --no-cache --pull --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:latest -t tvapp2:1.0.0 --platform=linux/arm64 --output type=docker --output type=docker .
|
||||||
|
# #
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Run command with node if the first argument contains a "-" or is not a system command. The last
|
||||||
|
# part inside the "{}" is a workaround for the following bug in ash/dash:
|
||||||
|
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264
|
||||||
|
if [ "${1#-}" != "${1}" ] || [ -z "$(command -v "${1}")" ] || { [ -f "${1}" ] && ! [ -x "${1}" ]; }; then
|
||||||
|
set -- node "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$@"
|
||||||
BIN
docs/img/002.png
BIN
docs/img/002.png
Binary file not shown.
|
Before Width: | Height: | Size: 84 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
32
download.sh
32
download.sh
@@ -1,32 +0,0 @@
|
|||||||
#!/usr/bin/with-contenv bash
|
|
||||||
# shellcheck shell=bash
|
|
||||||
|
|
||||||
# #
|
|
||||||
# @project thetvapp-docker
|
|
||||||
# @about download script for fetching m3u8 and xml
|
|
||||||
# @file /download.sh
|
|
||||||
# @repo https://github.com/Aetherinox/thetvapp-docker
|
|
||||||
# #
|
|
||||||
|
|
||||||
DATE=$(TZ=${TZ} date '+%m-%d-%Y %H:%M:%S')
|
|
||||||
|
|
||||||
# #
|
|
||||||
# Run Download
|
|
||||||
# #
|
|
||||||
|
|
||||||
echo -e
|
|
||||||
echo -e " Start : Downloading latest ${FILE_NAME} m3u + xml"
|
|
||||||
|
|
||||||
# Download .xml
|
|
||||||
wget -q -O /config/www/${FILE_NAME}.xml ${URL_XML}
|
|
||||||
echo -e " Getting ${FILE_NAME}.xml › ${URL_XML}"
|
|
||||||
|
|
||||||
# Download .xml.gz
|
|
||||||
wget -q -O /config/www/${FILE_NAME}.xml.gz ${URL_XML_GZ}
|
|
||||||
echo -e " Getting ${FILE_NAME}.xml.gz › ${URL_XML_GZ}"
|
|
||||||
|
|
||||||
# Download .m3u8
|
|
||||||
wget -q -O /config/www/${FILE_NAME}.m3u8 ${URL_M3U}
|
|
||||||
echo -e " Getting ${FILE_NAME}.m3u8 › ${URL_M3U}"
|
|
||||||
|
|
||||||
echo -e " End : Finished update at ${DATE}"
|
|
||||||
658
index.js
Normal file
658
index.js
Normal file
@@ -0,0 +1,658 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const https = require('https');
|
||||||
|
const path = require('path');
|
||||||
|
const UserAgent = require('user-agents');
|
||||||
|
const http = require('http');
|
||||||
|
const os = require('os');
|
||||||
|
const zlib = require('zlib');
|
||||||
|
const { randomUUID } = require('crypto');
|
||||||
|
const { channel } = require('diagnostics_channel');
|
||||||
|
const cache = new Map();
|
||||||
|
|
||||||
|
let URLS_FILE;
|
||||||
|
let FORMATTED_FILE;
|
||||||
|
let EPG_FILE;
|
||||||
|
const xmltv_epg = 'https://git.binaryninja.net/pub_projects/XMLTV-EPG/raw/branch/main/xmltv.1.xml';
|
||||||
|
const externalURL = 'https://git.binaryninja.net/pub_projects/tvapp2-externals/raw/branch/main/urls.txt';
|
||||||
|
const externalEPG = 'https://git.binaryninja.net/pub_projects/XMLTV-EPG/raw/branch/main/xmltv.1.xml';
|
||||||
|
const externalFORMATTED_1 = 'https://git.binaryninja.net/pub_projects/tvapp2-externals/raw/branch/main/formatted.dat';
|
||||||
|
const externalFORMATTED_2 = '';
|
||||||
|
const externalFORMATTED_3 = '';
|
||||||
|
const externalEvents = '';
|
||||||
|
|
||||||
|
if (process.pkg) {
|
||||||
|
console.log('Process package');
|
||||||
|
const basePath = path.dirname(process.execPath);
|
||||||
|
URLS_FILE = path.join(basePath, 'urls.txt');
|
||||||
|
FORMATTED_FILE = path.join(basePath, 'formatted.dat');
|
||||||
|
//EPG_FILE = path.join(basePath, 'epg.xml');
|
||||||
|
EPG_FILE = path.join(basePath, 'xmltv.1.xml');
|
||||||
|
EPG_FILE.length;
|
||||||
|
} else {
|
||||||
|
console.log('Process locals');
|
||||||
|
URLS_FILE = path.resolve(__dirname, 'urls.txt');
|
||||||
|
FORMATTED_FILE = path.resolve(__dirname, 'formatted.dat');
|
||||||
|
EPG_FILE = path.resolve(__dirname, 'xmltv.1.xml');
|
||||||
|
}
|
||||||
|
|
||||||
|
class Semaphore {
|
||||||
|
constructor(max) {
|
||||||
|
this.max = max;
|
||||||
|
this.queue = [];
|
||||||
|
this.active = 0;
|
||||||
|
}
|
||||||
|
async acquire() {
|
||||||
|
if (this.active < this.max) {
|
||||||
|
this.active++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return new Promise((resolve) => this.queue.push(resolve));
|
||||||
|
}
|
||||||
|
release() {
|
||||||
|
this.active--;
|
||||||
|
if (this.queue.length > 0) {
|
||||||
|
const resolve = this.queue.shift();
|
||||||
|
this.active++;
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const semaphore = new Semaphore(5);
|
||||||
|
|
||||||
|
let urls = [];
|
||||||
|
let tokenData = {
|
||||||
|
subdomain: null,
|
||||||
|
token: null,
|
||||||
|
url: null,
|
||||||
|
validationUrl: null,
|
||||||
|
cookies: null,
|
||||||
|
};
|
||||||
|
let lastTokenFetchTime = 0;
|
||||||
|
|
||||||
|
const log = (message) => {
|
||||||
|
const now = new Date();
|
||||||
|
console.log(`[${now.toLocaleTimeString()}] ${message}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function downloadFile(url, filePath) {
|
||||||
|
console.log(`Fetching ${url}`);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const isHttps = new URL(url).protocol === 'https:';
|
||||||
|
const httpModule = isHttps ? require('https') : require('http');
|
||||||
|
const file = fs.createWriteStream(filePath);
|
||||||
|
|
||||||
|
httpModule
|
||||||
|
.get(url, (response) => {
|
||||||
|
if (response.statusCode !== 200) {
|
||||||
|
console.error(`Failed to download file: ${url}. Status code: ${response.statusCode}`);
|
||||||
|
return reject(new Error(`Failed to download file: ${url}. Status code: ${response.statusCode}`));
|
||||||
|
}
|
||||||
|
response.pipe(file);
|
||||||
|
file.on('finish', () => {
|
||||||
|
log(`Sucess: ${filePath}`);
|
||||||
|
file.close(() => resolve(true));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.on('error', (err) => {
|
||||||
|
console.error(`Error downloading file: ${url}. Error: ${err.message}`);
|
||||||
|
fs.unlink(filePath, () => reject(err));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureFileExists(url, filePath) {
|
||||||
|
try {
|
||||||
|
await downloadFile(url, filePath);
|
||||||
|
} catch (error) {
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
console.warn(`Using existing file for ${filePath} due to download failure.`);
|
||||||
|
} else {
|
||||||
|
console.error(`Critical: Failed to download ${url}, and no local file exists.`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// REMOVED REFERENCE CALLS TO THIS FUNCTION
|
||||||
|
// TODO: UPDATES TO HANDLER FOR SPORT EVENTS
|
||||||
|
async function fetchSportsData() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const isHttps = new URL(externalEvents).protocol === 'https:';
|
||||||
|
const httpModule = isHttps ? require('https') : require('http');
|
||||||
|
httpModule
|
||||||
|
.get(url, (response) => {
|
||||||
|
if (response.statusCode !== 200) {
|
||||||
|
console.error(`Failed to fetch sports data. Status code: ${response.statusCode}`);
|
||||||
|
return reject(new Error(`Failed to fetch sports data. Status code: ${response.statusCode}`));
|
||||||
|
}
|
||||||
|
let data = '';
|
||||||
|
response.on('data', (chunk) => (data += chunk));
|
||||||
|
response.on('end', () => {
|
||||||
|
log('Fetched sports data successfully.');
|
||||||
|
resolve(data);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.on('error', (err) => {
|
||||||
|
console.error(`Error fetching sports data: ${err.message}`);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchRemote(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const mod = url.startsWith('https') ? https : http;
|
||||||
|
mod
|
||||||
|
.get(url, { headers: { 'Accept-Encoding': 'gzip, deflate, br' } }, (resp) => {
|
||||||
|
if (resp.statusCode !== 200) {
|
||||||
|
return reject(new Error(`HTTP ${resp.statusCode} for ${url}`));
|
||||||
|
}
|
||||||
|
const chunks = [];
|
||||||
|
resp.on('data', (chunk) => chunks.push(chunk));
|
||||||
|
resp.on('end', () => {
|
||||||
|
const buffer = Buffer.concat(chunks);
|
||||||
|
const encoding = resp.headers['content-encoding'];
|
||||||
|
if (encoding === 'gzip') {
|
||||||
|
zlib.gunzip(buffer, (err, decoded) => {
|
||||||
|
if (err) return reject(err);
|
||||||
|
resolve(decoded);
|
||||||
|
});
|
||||||
|
} else if (encoding === 'deflate') {
|
||||||
|
zlib.inflate(buffer, (err, decoded) => {
|
||||||
|
if (err) return reject(err);
|
||||||
|
resolve(decoded);
|
||||||
|
});
|
||||||
|
} else if (encoding === 'br') {
|
||||||
|
zlib.brotliDecompress(buffer, (err, decoded) => {
|
||||||
|
if (err) return reject(err);
|
||||||
|
resolve(decoded);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve(buffer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.on('error', reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function serveKey(req, res) {
|
||||||
|
try {
|
||||||
|
const uriParam = new URL(req.url, `http://${req.headers.host}`).searchParams.get('uri');
|
||||||
|
if (!uriParam) {
|
||||||
|
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
||||||
|
return res.end('Error: Missing "uri" parameter for key download.');
|
||||||
|
}
|
||||||
|
const keyData = await fetchRemote(uriParam);
|
||||||
|
res.writeHead(200, { 'Content-Type': 'application/octet-stream' });
|
||||||
|
res.end(keyData);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error in serveKey:', err.message);
|
||||||
|
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||||
|
res.end('Error fetching key.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let gCookies = {};
|
||||||
|
const USERAGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
|
||||||
|
|
||||||
|
function parseSetCookieHeaders(setCookieValues) {
|
||||||
|
if (!Array.isArray(setCookieValues)) return;
|
||||||
|
setCookieValues.forEach((line) => {
|
||||||
|
const [cookiePair] = line.split(';');
|
||||||
|
if (cookiePair) {
|
||||||
|
const [key, val] = cookiePair.split('=');
|
||||||
|
if (key && val) {
|
||||||
|
gCookies[key.trim()] = val.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCookieHeader() {
|
||||||
|
const pairs = [];
|
||||||
|
for (const [k, v] of Object.entries(gCookies)) {
|
||||||
|
pairs.push(`${k}=${v}`);
|
||||||
|
}
|
||||||
|
return pairs.join('; ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchPage(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const opts = {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'User-Agent': USERAGENT,
|
||||||
|
Accept: '*/*',
|
||||||
|
Cookie: buildCookieHeader(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
https
|
||||||
|
.get(url, opts, (res) => {
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
return reject(new Error(`Non-200 status ${res.statusCode} => ${url}`));
|
||||||
|
}
|
||||||
|
if (res.headers['set-cookie']) {
|
||||||
|
parseSetCookieHeaders(res.headers['set-cookie']);
|
||||||
|
}
|
||||||
|
let data = '';
|
||||||
|
res.on('data', (chunk) => (data += chunk));
|
||||||
|
res.on('end', () => resolve(data));
|
||||||
|
})
|
||||||
|
.on('error', reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getTokenizedUrl(channelUrl) {
|
||||||
|
try {
|
||||||
|
const html = await fetchPage(channelUrl);
|
||||||
|
|
||||||
|
let streamName;
|
||||||
|
let streamHost;
|
||||||
|
if (channelUrl.includes('espn-')) {
|
||||||
|
streamName = 'ESPN';
|
||||||
|
} else if (channelUrl.includes('espn2-')) {
|
||||||
|
streamName = 'ESPN2';
|
||||||
|
} else {
|
||||||
|
const streamNameMatch = html.match(/id="stream_name" name="([^"]+)"/);
|
||||||
|
if (!streamNameMatch) {
|
||||||
|
log('No "stream_name" found');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
streamName = streamNameMatch[1];
|
||||||
|
}
|
||||||
|
if (channelUrl.match('tvpass\.org')) {
|
||||||
|
streamHost = 'tvpass.org';
|
||||||
|
};
|
||||||
|
if (channelUrl.match('thetvapp\.to')) {
|
||||||
|
streamHost = 'thetvapp.to';
|
||||||
|
};
|
||||||
|
const tokenUrl = `https://${streamHost}/token/${streamName}?quality=hd`;
|
||||||
|
const tokenResponse = await fetchPage(tokenUrl);
|
||||||
|
let finalUrl;
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(tokenResponse);
|
||||||
|
finalUrl = json.url;
|
||||||
|
} catch (err) {
|
||||||
|
log('Failed to parse token JSON');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!finalUrl) {
|
||||||
|
log('No URL found in the token JSON');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
log(`Tokenized URL: ${finalUrl}`);
|
||||||
|
return finalUrl;
|
||||||
|
} catch (err) {
|
||||||
|
log(`Fatal error fetching token: ${err.message}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function serveChannelPlaylist(req, res) {
|
||||||
|
await semaphore.acquire();
|
||||||
|
try {
|
||||||
|
const urlParam = new URL(req.url, `http://${req.headers.host}`).searchParams.get('url');
|
||||||
|
if (!urlParam) {
|
||||||
|
log('Error: Missing URL parameter');
|
||||||
|
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
||||||
|
res.end('Error: Missing URL parameter.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const decodedUrl = decodeURIComponent(urlParam);
|
||||||
|
if (decodedUrl.endsWith('.ts')) {
|
||||||
|
res.writeHead(302, { Location: decodedUrl });
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const cachedUrl = getCache(decodedUrl);
|
||||||
|
if (cachedUrl) {
|
||||||
|
const rewrittenPlaylist = await rewritePlaylist(cachedUrl, req);
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Type': 'application/vnd.apple.mpegurl',
|
||||||
|
'Content-Disposition': 'inline; filename="playlist.m3u8"',
|
||||||
|
});
|
||||||
|
res.end(rewrittenPlaylist);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log(`Fetching stream: ${urlParam}`);
|
||||||
|
const finalUrl = await getTokenizedUrl(decodedUrl);
|
||||||
|
if (!finalUrl) {
|
||||||
|
log('Error: Failed to retrieve tokenized URL');
|
||||||
|
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||||
|
res.end('Error: Failed to retrieve tokenized URL.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setCache(decodedUrl, finalUrl, 4 * 60 * 60 * 1000);
|
||||||
|
const hdUrl = finalUrl.replace('tracks-v2a1', 'tracks-v1a1');
|
||||||
|
const rewrittenPlaylist = await rewritePlaylist(hdUrl, req);
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Type': 'application/vnd.apple.mpegurl',
|
||||||
|
'Content-Disposition': 'inline; filename="playlist.m3u8"',
|
||||||
|
});
|
||||||
|
res.end(rewrittenPlaylist);
|
||||||
|
log('Served playlist');
|
||||||
|
} catch (error) {
|
||||||
|
log(`Error processing request: ${error.message}`);
|
||||||
|
if (!res.headersSent) {
|
||||||
|
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||||
|
res.end('Error processing request.');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
semaphore.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rewritePlaylist(originalUrl, req) {
|
||||||
|
const rawData = await fetchRemote(originalUrl);
|
||||||
|
const protocol = req.headers['x-forwarded-proto']?.split(',')[0] || (req.socket.encrypted ? 'https' : 'http');
|
||||||
|
const host = req.headers.host;
|
||||||
|
const baseUrl = `${protocol}://${host}`;
|
||||||
|
const playlistContent = rawData.toString('utf8');
|
||||||
|
return playlistContent
|
||||||
|
.replace(/URI="([^"]+)"/g, (match, uri) => {
|
||||||
|
const resolvedUri = new URL(uri, originalUrl).href;
|
||||||
|
return `URI="${baseUrl}/key?uri=${encodeURIComponent(resolvedUri)}"`;
|
||||||
|
})
|
||||||
|
.replace(/^([^#].*\.m3u8)(\?.*)?$/gm, (match, uri) => {
|
||||||
|
const resolvedUri = new URL(uri, originalUrl).href;
|
||||||
|
return `${baseUrl}/channel?url=${encodeURIComponent(resolvedUri)}`;
|
||||||
|
})
|
||||||
|
.replace(/^([^#].*\.ts)(\?.*)?$/gm, (match, uri) => {
|
||||||
|
const resolvedUri = new URL(uri, originalUrl).href;
|
||||||
|
return `${baseUrl}/channel?url=${encodeURIComponent(resolvedUri)}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function servePlaylist(response, req) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const protocol = req.headers['x-forwarded-proto']?.split(',')[0] || (req.socket.encrypted ? 'https' : 'http');
|
||||||
|
const host = req.headers.host;
|
||||||
|
const baseUrl = `${protocol}://${host}`;
|
||||||
|
const formattedContent = fs.readFileSync(FORMATTED_FILE, 'utf-8');
|
||||||
|
const updatedContent = formattedContent
|
||||||
|
.replace(/(https?:\/\/[^\s]*thetvapp[^\s]*)/g, (fullUrl) => {
|
||||||
|
return `${baseUrl}/channel?url=${encodeURIComponent(fullUrl)}`;
|
||||||
|
})
|
||||||
|
.replace(/(https?:\/\/[^\s]*tvpass[^\s]*)/g, (fullUrl) => {
|
||||||
|
return `${baseUrl}/channel?url=${encodeURIComponent(fullUrl)}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
response.writeHead(200, {
|
||||||
|
'Content-Type': 'application/x-mpegURL',
|
||||||
|
'Content-Disposition': 'inline; filename="playlist.m3u8"',
|
||||||
|
});
|
||||||
|
response.end(updatedContent);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
console.error('Error in servePlaylist:', error.message);
|
||||||
|
response.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||||
|
response.end(`Error serving playlist: ${error.message}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function serveXmltv(response, req) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const protocol = req.headers['x-forwarded-proto']?.split(',')[0] || (req.socket.encrypted ? 'https' : 'http');
|
||||||
|
const host = req.headers.host;
|
||||||
|
const baseUrl = `${protocol}://${host}`;
|
||||||
|
const formattedContent = fs.readFileSync(EPG_FILE, 'utf-8');
|
||||||
|
|
||||||
|
response.writeHead(200, {
|
||||||
|
'Content-Type': 'application/xml',
|
||||||
|
'Content-Disposition': 'inline; filename="xmltv.1.xml"',
|
||||||
|
});
|
||||||
|
response.end(formattedContent);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
console.error('Error in servePlaylist:', error.message);
|
||||||
|
response.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||||
|
response.end(`Error serving playlist: ${error.message}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
ORIGINAL ASYNC HANDLER - HOPE ALL IS WELL DTANK - JOB WELL DONE
|
||||||
|
async function serveXmltv(response, req) {
|
||||||
|
try {
|
||||||
|
const protocol = req.headers['x-forwarded-proto']?.split(',')[0] || (req.socket.encrypted ? 'https' : 'http');
|
||||||
|
const host = req.headers.host;
|
||||||
|
const baseUrl = `${protocol}://${host}`;
|
||||||
|
//const sportsData = await fetchSportsData();
|
||||||
|
const formattedContent = fs.readFileSync(EPG_FILE, 'utf-8');
|
||||||
|
//const updatedContent = formattedContent
|
||||||
|
//.replace(/#\[SPORTS\]/g, sportsData || '')
|
||||||
|
//.replace(/(https?:\/\/[^\s]*thetvapp[^\s]*)/g, (fullUrl) => {
|
||||||
|
//return `${baseUrl}/channel?url=${encodeURIComponent(fullUrl)}`;
|
||||||
|
//});
|
||||||
|
response.writeHead(200, {
|
||||||
|
'Content-Type': 'application/x-mpegURL',
|
||||||
|
'Content-Disposition': 'inline; filename="playlist.m3u8"',
|
||||||
|
});
|
||||||
|
response.end(updatedContent);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in servePlaylist:', error.message);
|
||||||
|
response.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||||
|
response.end(`Error serving playlist: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function servePlaylist(response, req) {
|
||||||
|
try {
|
||||||
|
const protocol = req.headers['x-forwarded-proto']?.split(',')[0] || (req.socket.encrypted ? 'https' : 'http');
|
||||||
|
const host = req.headers.host;
|
||||||
|
const baseUrl = `${protocol}://${host}`;
|
||||||
|
//const sportsData = await fetchSportsData();
|
||||||
|
const formattedContent = fs.readFileSync(FORMATTED_FILE, 'utf-8');
|
||||||
|
const updatedContent = formattedContent
|
||||||
|
//.replace(/#\[SPORTS\]/g, sportsData || '')
|
||||||
|
.replace(/(https?:\/\/[^\s]*thetvapp[^\s]*)/g, (fullUrl) => {
|
||||||
|
return `${baseUrl}/channel?url=${encodeURIComponent(fullUrl)}`;
|
||||||
|
})
|
||||||
|
.replace(/(https?:\/\/[^\s]*tvpass[^\s]*)/g, (fullUrl) => {
|
||||||
|
return `${baseUrl}/channel?url=${encodeURIComponent(fullUrl)}`;
|
||||||
|
});
|
||||||
|
response.writeHead(200, {
|
||||||
|
'Content-Type': 'application/x-mpegURL',
|
||||||
|
'Content-Disposition': 'inline; filename="playlist.m3u8"',
|
||||||
|
});
|
||||||
|
response.end(updatedContent);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in servePlaylist:', error.message);
|
||||||
|
response.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||||
|
response.end(`Error serving playlist: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function setCache(key, value, ttl) {
|
||||||
|
const expiry = Date.now() + ttl;
|
||||||
|
cache.set(key, { value, expiry });
|
||||||
|
log(`Cache set: ${key}, expires in ${ttl / 1000} seconds`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCache(key) {
|
||||||
|
const cached = cache.get(key);
|
||||||
|
if (cached && cached.expiry > Date.now()) {
|
||||||
|
return cached.value;
|
||||||
|
} else {
|
||||||
|
if (cached) log(`Cache expired for key: ${key}`);
|
||||||
|
cache.delete(key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initialize() {
|
||||||
|
try {
|
||||||
|
log('Initializing server...');
|
||||||
|
await ensureFileExists(externalURL, URLS_FILE);
|
||||||
|
await ensureFileExists(externalFORMATTED_1, FORMATTED_FILE);
|
||||||
|
await ensureFileExists(externalEPG, EPG_FILE);
|
||||||
|
urls = fs.readFileSync(URLS_FILE, 'utf-8').split('\n').filter(Boolean);
|
||||||
|
if (urls.length === 0) {
|
||||||
|
throw new Error(`No valid URLs found in ${URLS_FILE}`);
|
||||||
|
}
|
||||||
|
log('Initialization complete.');
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Initialization error: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = http.createServer((req, res) => {
|
||||||
|
const handleRequest = async () => {
|
||||||
|
const protocol = req.headers['x-forwarded-proto']?.split(',')[0] || (req.socket.encrypted ? 'https' : 'http');
|
||||||
|
const host = req.headers.host;
|
||||||
|
const baseUrl = `${protocol}://${host}`;
|
||||||
|
if (req.url === '/' && req.method === 'GET') {
|
||||||
|
const htmlContent = `<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Playlist Details</title>
|
||||||
|
<meta name="robots" content="noindex, nofollow">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 470px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #007bff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.details p {
|
||||||
|
margin: 10px 0;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.warning {
|
||||||
|
color: #ff4e4e;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#firewall-warning {
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
#warning-container p {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<center>
|
||||||
|
<div class="container main-container">
|
||||||
|
<h1>Playlist Details</h1>
|
||||||
|
<div class="details">
|
||||||
|
<p><strong>Playlist URL:</strong> <a id="playlist-url" target="_blank"></a></p>
|
||||||
|
<p><strong>EPG URL:</strong> <a id="epg-url" target="_blank"></a></p>
|
||||||
|
</div>
|
||||||
|
<div id="firewall-warning"></div>
|
||||||
|
<div id="warning-container" class="container"></div>
|
||||||
|
</div>
|
||||||
|
</center>
|
||||||
|
<script>
|
||||||
|
const baseURL = window.location.origin;
|
||||||
|
const playlistURL = baseURL + "/playlist";
|
||||||
|
const epgURL = baseURL + "/epg";
|
||||||
|
document.getElementById("playlist-url").textContent = playlistURL;
|
||||||
|
document.getElementById("playlist-url").href = playlistURL;
|
||||||
|
document.getElementById("epg-url").textContent = epgURL;
|
||||||
|
document.getElementById("epg-url").href = epgURL;
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
const host = window.location.hostname;
|
||||||
|
if (host === "localhost" || host === "127.0.0.1") {
|
||||||
|
const warning = document.createElement("div");
|
||||||
|
warning.style.color = "#ff4e4e";
|
||||||
|
warning.style.fontSize = "14px";
|
||||||
|
warning.style.textAlign = "center";
|
||||||
|
warning.style.fontWeight = "bold";
|
||||||
|
warning.innerHTML = "<p>Warning: If you are accessing this page via 127.0.0.1 or localhost, proxying will not work on other devices. Please load this page using your computer's IP address (e.g., 192.168.x.x) and port in order to access the playlist from other devices on your network.</p>" +
|
||||||
|
"<p>How to locate IP address on <a href='https://www.youtube.com/watch?v=UAhDHXN2c6E' target='_blank'>Windows</a> or <a href='https://www.youtube.com/watch?v=gaIYP4TZfHI' target='_blank'>Linux</a>.</p>";
|
||||||
|
document.getElementById("warning-container").appendChild(warning);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
const port = window.location.port || (window.location.protocol === "https:" ? "443" : "80");
|
||||||
|
const warningMessage =
|
||||||
|
"<p>Ensure that port <strong>" + port + "</strong> is open and allowed through your Windows (<a href='https://youtu.be/zOZWlTplrcA?si=nGXrHKU4sAQsy18e&t=18' target='_blank'>how to</a>) or Linux (<a href='https://youtu.be/7c_V_3nWWbA?si=Hkd_II9myn-AkNnS&t=12' target='_blank'>how to</a>) firewall settings. This will enable other devices, such as Firestick, Android, and others, to connect to the server and request the playlist through the proxy.</p>";
|
||||||
|
document.getElementById("firewall-warning").innerHTML = warningMessage;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||||
|
res.end(htmlContent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req.url === '/playlist' && req.method === 'GET') {
|
||||||
|
log('Playlist request received');
|
||||||
|
await servePlaylist(res, req);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req.url.startsWith('/channel') && req.method === 'GET') {
|
||||||
|
await serveChannelPlaylist(req, res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req.url.startsWith('/key') && req.method === 'GET') {
|
||||||
|
await serveKey(req, res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req.url === '/epg' && req.method === 'GET') {
|
||||||
|
log('Epg request received');
|
||||||
|
await serveXmltv(res, req);
|
||||||
|
return;
|
||||||
|
/*res.writeHead(302, {
|
||||||
|
Location: 'https://raw.githubusercontent.com/dtankdempse/thetvapp-m3u/refs/heads/main/guide/epg.xml',
|
||||||
|
});
|
||||||
|
res.end();
|
||||||
|
return;*/
|
||||||
|
}
|
||||||
|
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||||
|
res.end('Not Found');
|
||||||
|
};
|
||||||
|
handleRequest().catch((error) => {
|
||||||
|
console.error('Error handling request:', error);
|
||||||
|
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||||
|
res.end('Internal Server Error');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await initialize();
|
||||||
|
const PORT = 4124;
|
||||||
|
server.listen(PORT, '0.0.0.0', () => {
|
||||||
|
log(`Server is running on port ${PORT}`);
|
||||||
|
});
|
||||||
|
})();
|
||||||
16
node_modules/.bin/playwright
generated
vendored
Normal file
16
node_modules/.bin/playwright
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../playwright/cli.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../playwright/cli.js" "$@"
|
||||||
|
fi
|
||||||
16
node_modules/.bin/playwright-core
generated
vendored
Normal file
16
node_modules/.bin/playwright-core
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../playwright-core/cli.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../playwright-core/cli.js" "$@"
|
||||||
|
fi
|
||||||
17
node_modules/.bin/playwright-core.cmd
generated
vendored
Normal file
17
node_modules/.bin/playwright-core.cmd
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@ECHO off
|
||||||
|
GOTO start
|
||||||
|
:find_dp0
|
||||||
|
SET dp0=%~dp0
|
||||||
|
EXIT /b
|
||||||
|
:start
|
||||||
|
SETLOCAL
|
||||||
|
CALL :find_dp0
|
||||||
|
|
||||||
|
IF EXIST "%dp0%\node.exe" (
|
||||||
|
SET "_prog=%dp0%\node.exe"
|
||||||
|
) ELSE (
|
||||||
|
SET "_prog=node"
|
||||||
|
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||||
|
)
|
||||||
|
|
||||||
|
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\playwright-core\cli.js" %*
|
||||||
28
node_modules/.bin/playwright-core.ps1
generated
vendored
Normal file
28
node_modules/.bin/playwright-core.ps1
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||||
|
|
||||||
|
$exe=""
|
||||||
|
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||||
|
# Fix case when both the Windows and Linux builds of Node
|
||||||
|
# are installed in the same directory
|
||||||
|
$exe=".exe"
|
||||||
|
}
|
||||||
|
$ret=0
|
||||||
|
if (Test-Path "$basedir/node$exe") {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "$basedir/node$exe" "$basedir/../playwright-core/cli.js" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../playwright-core/cli.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../playwright-core/cli.js" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../playwright-core/cli.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
17
node_modules/.bin/playwright.cmd
generated
vendored
Normal file
17
node_modules/.bin/playwright.cmd
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@ECHO off
|
||||||
|
GOTO start
|
||||||
|
:find_dp0
|
||||||
|
SET dp0=%~dp0
|
||||||
|
EXIT /b
|
||||||
|
:start
|
||||||
|
SETLOCAL
|
||||||
|
CALL :find_dp0
|
||||||
|
|
||||||
|
IF EXIST "%dp0%\node.exe" (
|
||||||
|
SET "_prog=%dp0%\node.exe"
|
||||||
|
) ELSE (
|
||||||
|
SET "_prog=node"
|
||||||
|
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||||
|
)
|
||||||
|
|
||||||
|
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\playwright\cli.js" %*
|
||||||
28
node_modules/.bin/playwright.ps1
generated
vendored
Normal file
28
node_modules/.bin/playwright.ps1
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||||
|
|
||||||
|
$exe=""
|
||||||
|
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||||
|
# Fix case when both the Windows and Linux builds of Node
|
||||||
|
# are installed in the same directory
|
||||||
|
$exe=".exe"
|
||||||
|
}
|
||||||
|
$ret=0
|
||||||
|
if (Test-Path "$basedir/node$exe") {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "$basedir/node$exe" "$basedir/../playwright/cli.js" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../playwright/cli.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../playwright/cli.js" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../playwright/cli.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
49
node_modules/.package-lock.json
generated
vendored
Normal file
49
node_modules/.package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "thetvapp-m3u",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"node_modules/lodash.clonedeep": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
|
||||||
|
},
|
||||||
|
"node_modules/playwright": {
|
||||||
|
"version": "1.49.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz",
|
||||||
|
"integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright-core": "1.49.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.49.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz",
|
||||||
|
"integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==",
|
||||||
|
"bin": {
|
||||||
|
"playwright-core": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/user-agents": {
|
||||||
|
"version": "1.1.388",
|
||||||
|
"resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.1.388.tgz",
|
||||||
|
"integrity": "sha512-zsFa+jzuM+7DiB9es9iYL0fbPeN0Kc9Bor6PcNwKN1X46yHus3oXrH3RTyJ1CsVxQpzG9iXdU3V3Io0FboFvhQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash.clonedeep": "^4.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
node_modules/lodash.clonedeep/LICENSE
generated
vendored
Normal file
47
node_modules/lodash.clonedeep/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
Copyright jQuery Foundation and other contributors <https://jquery.org/>
|
||||||
|
|
||||||
|
Based on Underscore.js, copyright Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
This software consists of voluntary contributions made by many
|
||||||
|
individuals. For exact contribution history, see the revision history
|
||||||
|
available at https://github.com/lodash/lodash
|
||||||
|
|
||||||
|
The following license applies to all parts of this software except as
|
||||||
|
documented below:
|
||||||
|
|
||||||
|
====
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
====
|
||||||
|
|
||||||
|
Copyright and related rights for sample code are waived via CC0. Sample
|
||||||
|
code is defined as all source code displayed within the prose of the
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
CC0: http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
====
|
||||||
|
|
||||||
|
Files located in the node_modules and vendor directories are externally
|
||||||
|
maintained libraries used by this software which have their own
|
||||||
|
licenses; we recommend you read them, as their terms may differ from the
|
||||||
|
terms above.
|
||||||
18
node_modules/lodash.clonedeep/README.md
generated
vendored
Normal file
18
node_modules/lodash.clonedeep/README.md
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# lodash.clonedeep v4.5.0
|
||||||
|
|
||||||
|
The [lodash](https://lodash.com/) method `_.cloneDeep` exported as a [Node.js](https://nodejs.org/) module.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Using npm:
|
||||||
|
```bash
|
||||||
|
$ {sudo -H} npm i -g npm
|
||||||
|
$ npm i --save lodash.clonedeep
|
||||||
|
```
|
||||||
|
|
||||||
|
In Node.js:
|
||||||
|
```js
|
||||||
|
var cloneDeep = require('lodash.clonedeep');
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [documentation](https://lodash.com/docs#cloneDeep) or [package source](https://github.com/lodash/lodash/blob/4.5.0-npm-packages/lodash.clonedeep) for more details.
|
||||||
1748
node_modules/lodash.clonedeep/index.js
generated
vendored
Normal file
1748
node_modules/lodash.clonedeep/index.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
17
node_modules/lodash.clonedeep/package.json
generated
vendored
Normal file
17
node_modules/lodash.clonedeep/package.json
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash.clonedeep",
|
||||||
|
"version": "4.5.0",
|
||||||
|
"description": "The lodash method `_.cloneDeep` exported as a module.",
|
||||||
|
"homepage": "https://lodash.com/",
|
||||||
|
"icon": "https://lodash.com/icon.svg",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": "lodash-modularized, clonedeep",
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine.bublitz@gmail.com> (https://github.com/phated)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (https://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"repository": "lodash/lodash",
|
||||||
|
"scripts": { "test": "echo \"See https://travis-ci.org/lodash/lodash-cli for testing details.\"" }
|
||||||
|
}
|
||||||
202
node_modules/playwright-core/LICENSE
generated
vendored
Normal file
202
node_modules/playwright-core/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Portions Copyright (c) Microsoft Corporation.
|
||||||
|
Portions Copyright 2017 Google Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
5
node_modules/playwright-core/NOTICE
generated
vendored
Normal file
5
node_modules/playwright-core/NOTICE
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Playwright
|
||||||
|
Copyright (c) Microsoft Corporation
|
||||||
|
|
||||||
|
This software contains code derived from the Puppeteer project (https://github.com/puppeteer/puppeteer),
|
||||||
|
available under the Apache 2.0 license (https://github.com/puppeteer/puppeteer/blob/master/LICENSE).
|
||||||
3
node_modules/playwright-core/README.md
generated
vendored
Normal file
3
node_modules/playwright-core/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# playwright-core
|
||||||
|
|
||||||
|
This package contains the no-browser flavor of [Playwright](http://github.com/microsoft/playwright).
|
||||||
2
node_modules/playwright-core/bin/README.md
generated
vendored
Normal file
2
node_modules/playwright-core/bin/README.md
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
See building instructions at [`/browser_patches/winldd/README.md`](../../../browser_patches/winldd/README.md)
|
||||||
|
|
||||||
5
node_modules/playwright-core/bin/install_media_pack.ps1
generated
vendored
Normal file
5
node_modules/playwright-core/bin/install_media_pack.ps1
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
$osInfo = Get-WmiObject -Class Win32_OperatingSystem
|
||||||
|
# check if running on Windows Server
|
||||||
|
if ($osInfo.ProductType -eq 3) {
|
||||||
|
Install-WindowsFeature Server-Media-Foundation
|
||||||
|
}
|
||||||
42
node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh
generated
vendored
Normal file
42
node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
if [[ $(arch) == "aarch64" ]]; then
|
||||||
|
echo "ERROR: not supported on Linux Arm64"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
|
||||||
|
if [[ ! -f "/etc/os-release" ]]; then
|
||||||
|
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||||
|
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||||
|
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. make sure to remove old beta if any.
|
||||||
|
if dpkg --get-selections | grep -q "^google-chrome-beta[[:space:]]*install$" >/dev/null; then
|
||||||
|
apt-get remove -y google-chrome-beta
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Update apt lists (needed to install curl and chrome dependencies)
|
||||||
|
apt-get update
|
||||||
|
|
||||||
|
# 3. Install curl to download chrome
|
||||||
|
if ! command -v curl >/dev/null; then
|
||||||
|
apt-get install -y curl
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. download chrome beta from dl.google.com and install it.
|
||||||
|
cd /tmp
|
||||||
|
curl -O https://dl.google.com/linux/direct/google-chrome-beta_current_amd64.deb
|
||||||
|
apt-get install -y ./google-chrome-beta_current_amd64.deb
|
||||||
|
rm -rf ./google-chrome-beta_current_amd64.deb
|
||||||
|
cd -
|
||||||
|
google-chrome-beta --version
|
||||||
13
node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh
generated
vendored
Normal file
13
node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
rm -rf "/Applications/Google Chrome Beta.app"
|
||||||
|
cd /tmp
|
||||||
|
curl -o ./googlechromebeta.dmg -k https://dl.google.com/chrome/mac/universal/beta/googlechromebeta.dmg
|
||||||
|
hdiutil attach -nobrowse -quiet -noautofsck -noautoopen -mountpoint /Volumes/googlechromebeta.dmg ./googlechromebeta.dmg
|
||||||
|
cp -pR "/Volumes/googlechromebeta.dmg/Google Chrome Beta.app" /Applications
|
||||||
|
hdiutil detach /Volumes/googlechromebeta.dmg
|
||||||
|
rm -rf /tmp/googlechromebeta.dmg
|
||||||
|
|
||||||
|
/Applications/Google\ Chrome\ Beta.app/Contents/MacOS/Google\ Chrome\ Beta --version
|
||||||
24
node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1
generated
vendored
Normal file
24
node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$url = 'https://dl.google.com/tag/s/dl/chrome/install/beta/googlechromebetastandaloneenterprise64.msi'
|
||||||
|
|
||||||
|
Write-Host "Downloading Google Chrome Beta"
|
||||||
|
$wc = New-Object net.webclient
|
||||||
|
$msiInstaller = "$env:temp\google-chrome-beta.msi"
|
||||||
|
$wc.Downloadfile($url, $msiInstaller)
|
||||||
|
|
||||||
|
Write-Host "Installing Google Chrome Beta"
|
||||||
|
$arguments = "/i `"$msiInstaller`" /quiet"
|
||||||
|
Start-Process msiexec.exe -ArgumentList $arguments -Wait
|
||||||
|
Remove-Item $msiInstaller
|
||||||
|
|
||||||
|
$suffix = "\\Google\\Chrome Beta\\Application\\chrome.exe"
|
||||||
|
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
|
||||||
|
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
|
||||||
|
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
|
||||||
|
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
|
||||||
|
} else {
|
||||||
|
Write-Host "ERROR: Failed to install Google Chrome Beta."
|
||||||
|
Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
42
node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh
generated
vendored
Normal file
42
node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
if [[ $(arch) == "aarch64" ]]; then
|
||||||
|
echo "ERROR: not supported on Linux Arm64"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
|
||||||
|
if [[ ! -f "/etc/os-release" ]]; then
|
||||||
|
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||||
|
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||||
|
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. make sure to remove old stable if any.
|
||||||
|
if dpkg --get-selections | grep -q "^google-chrome[[:space:]]*install$" >/dev/null; then
|
||||||
|
apt-get remove -y google-chrome
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Update apt lists (needed to install curl and chrome dependencies)
|
||||||
|
apt-get update
|
||||||
|
|
||||||
|
# 3. Install curl to download chrome
|
||||||
|
if ! command -v curl >/dev/null; then
|
||||||
|
apt-get install -y curl
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. download chrome stable from dl.google.com and install it.
|
||||||
|
cd /tmp
|
||||||
|
curl -O https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||||
|
apt-get install -y ./google-chrome-stable_current_amd64.deb
|
||||||
|
rm -rf ./google-chrome-stable_current_amd64.deb
|
||||||
|
cd -
|
||||||
|
google-chrome --version
|
||||||
12
node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh
generated
vendored
Normal file
12
node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
rm -rf "/Applications/Google Chrome.app"
|
||||||
|
cd /tmp
|
||||||
|
curl -o ./googlechrome.dmg -k https://dl.google.com/chrome/mac/universal/stable/GGRO/googlechrome.dmg
|
||||||
|
hdiutil attach -nobrowse -quiet -noautofsck -noautoopen -mountpoint /Volumes/googlechrome.dmg ./googlechrome.dmg
|
||||||
|
cp -pR "/Volumes/googlechrome.dmg/Google Chrome.app" /Applications
|
||||||
|
hdiutil detach /Volumes/googlechrome.dmg
|
||||||
|
rm -rf /tmp/googlechrome.dmg
|
||||||
|
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version
|
||||||
24
node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1
generated
vendored
Normal file
24
node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$url = 'https://dl.google.com/tag/s/dl/chrome/install/googlechromestandaloneenterprise64.msi'
|
||||||
|
|
||||||
|
$wc = New-Object net.webclient
|
||||||
|
$msiInstaller = "$env:temp\google-chrome.msi"
|
||||||
|
Write-Host "Downloading Google Chrome"
|
||||||
|
$wc.Downloadfile($url, $msiInstaller)
|
||||||
|
|
||||||
|
Write-Host "Installing Google Chrome"
|
||||||
|
$arguments = "/i `"$msiInstaller`" /quiet"
|
||||||
|
Start-Process msiexec.exe -ArgumentList $arguments -Wait
|
||||||
|
Remove-Item $msiInstaller
|
||||||
|
|
||||||
|
|
||||||
|
$suffix = "\\Google\\Chrome\\Application\\chrome.exe"
|
||||||
|
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
|
||||||
|
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
|
||||||
|
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
|
||||||
|
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
|
||||||
|
} else {
|
||||||
|
Write-Host "ERROR: Failed to install Google Chrome."
|
||||||
|
Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
42
node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh
generated
vendored
Normal file
42
node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
if [[ $(arch) == "aarch64" ]]; then
|
||||||
|
echo "ERROR: not supported on Linux Arm64"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
|
||||||
|
if [[ ! -f "/etc/os-release" ]]; then
|
||||||
|
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||||
|
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||||
|
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. make sure to remove old beta if any.
|
||||||
|
if dpkg --get-selections | grep -q "^microsoft-edge-beta[[:space:]]*install$" >/dev/null; then
|
||||||
|
apt-get remove -y microsoft-edge-beta
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Install curl to download Microsoft gpg key
|
||||||
|
if ! command -v curl >/dev/null; then
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y curl
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Add the GPG key, the apt repo, update the apt cache, and install the package
|
||||||
|
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg
|
||||||
|
install -o root -g root -m 644 /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/
|
||||||
|
sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/edge stable main" > /etc/apt/sources.list.d/microsoft-edge-dev.list'
|
||||||
|
rm /tmp/microsoft.gpg
|
||||||
|
apt-get update && apt-get install -y microsoft-edge-beta
|
||||||
|
|
||||||
|
microsoft-edge-beta --version
|
||||||
11
node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh
generated
vendored
Normal file
11
node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
cd /tmp
|
||||||
|
curl -o ./msedge_beta.pkg -k "$1"
|
||||||
|
# Note: there's no way to uninstall previously installed MSEdge.
|
||||||
|
# However, running PKG again seems to update installation.
|
||||||
|
sudo installer -pkg /tmp/msedge_beta.pkg -target /
|
||||||
|
rm -rf /tmp/msedge_beta.pkg
|
||||||
|
/Applications/Microsoft\ Edge\ Beta.app/Contents/MacOS/Microsoft\ Edge\ Beta --version
|
||||||
23
node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1
generated
vendored
Normal file
23
node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$url = $args[0]
|
||||||
|
|
||||||
|
Write-Host "Downloading Microsoft Edge Beta"
|
||||||
|
$wc = New-Object net.webclient
|
||||||
|
$msiInstaller = "$env:temp\microsoft-edge-beta.msi"
|
||||||
|
$wc.Downloadfile($url, $msiInstaller)
|
||||||
|
|
||||||
|
Write-Host "Installing Microsoft Edge Beta"
|
||||||
|
$arguments = "/i `"$msiInstaller`" /quiet"
|
||||||
|
Start-Process msiexec.exe -ArgumentList $arguments -Wait
|
||||||
|
Remove-Item $msiInstaller
|
||||||
|
|
||||||
|
$suffix = "\\Microsoft\\Edge Beta\\Application\\msedge.exe"
|
||||||
|
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
|
||||||
|
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
|
||||||
|
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
|
||||||
|
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
|
||||||
|
} else {
|
||||||
|
Write-Host "ERROR: Failed to install Microsoft Edge Beta."
|
||||||
|
Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
42
node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh
generated
vendored
Normal file
42
node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
if [[ $(arch) == "aarch64" ]]; then
|
||||||
|
echo "ERROR: not supported on Linux Arm64"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
|
||||||
|
if [[ ! -f "/etc/os-release" ]]; then
|
||||||
|
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||||
|
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||||
|
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. make sure to remove old dev if any.
|
||||||
|
if dpkg --get-selections | grep -q "^microsoft-edge-dev[[:space:]]*install$" >/dev/null; then
|
||||||
|
apt-get remove -y microsoft-edge-dev
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Install curl to download Microsoft gpg key
|
||||||
|
if ! command -v curl >/dev/null; then
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y curl
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Add the GPG key, the apt repo, update the apt cache, and install the package
|
||||||
|
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg
|
||||||
|
install -o root -g root -m 644 /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/
|
||||||
|
sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/edge stable main" > /etc/apt/sources.list.d/microsoft-edge-dev.list'
|
||||||
|
rm /tmp/microsoft.gpg
|
||||||
|
apt-get update && apt-get install -y microsoft-edge-dev
|
||||||
|
|
||||||
|
microsoft-edge-dev --version
|
||||||
11
node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh
generated
vendored
Normal file
11
node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
cd /tmp
|
||||||
|
curl -o ./msedge_dev.pkg -k "$1"
|
||||||
|
# Note: there's no way to uninstall previously installed MSEdge.
|
||||||
|
# However, running PKG again seems to update installation.
|
||||||
|
sudo installer -pkg /tmp/msedge_dev.pkg -target /
|
||||||
|
rm -rf /tmp/msedge_dev.pkg
|
||||||
|
/Applications/Microsoft\ Edge\ Dev.app/Contents/MacOS/Microsoft\ Edge\ Dev --version
|
||||||
23
node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1
generated
vendored
Normal file
23
node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$url = $args[0]
|
||||||
|
|
||||||
|
Write-Host "Downloading Microsoft Edge Dev"
|
||||||
|
$wc = New-Object net.webclient
|
||||||
|
$msiInstaller = "$env:temp\microsoft-edge-dev.msi"
|
||||||
|
$wc.Downloadfile($url, $msiInstaller)
|
||||||
|
|
||||||
|
Write-Host "Installing Microsoft Edge Dev"
|
||||||
|
$arguments = "/i `"$msiInstaller`" /quiet"
|
||||||
|
Start-Process msiexec.exe -ArgumentList $arguments -Wait
|
||||||
|
Remove-Item $msiInstaller
|
||||||
|
|
||||||
|
$suffix = "\\Microsoft\\Edge Dev\\Application\\msedge.exe"
|
||||||
|
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
|
||||||
|
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
|
||||||
|
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
|
||||||
|
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
|
||||||
|
} else {
|
||||||
|
Write-Host "ERROR: Failed to install Microsoft Edge Dev."
|
||||||
|
Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
42
node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh
generated
vendored
Normal file
42
node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
if [[ $(arch) == "aarch64" ]]; then
|
||||||
|
echo "ERROR: not supported on Linux Arm64"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
|
||||||
|
if [[ ! -f "/etc/os-release" ]]; then
|
||||||
|
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||||
|
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||||
|
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. make sure to remove old stable if any.
|
||||||
|
if dpkg --get-selections | grep -q "^microsoft-edge-stable[[:space:]]*install$" >/dev/null; then
|
||||||
|
apt-get remove -y microsoft-edge-stable
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Install curl to download Microsoft gpg key
|
||||||
|
if ! command -v curl >/dev/null; then
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y curl
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Add the GPG key, the apt repo, update the apt cache, and install the package
|
||||||
|
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg
|
||||||
|
install -o root -g root -m 644 /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/
|
||||||
|
sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/edge stable main" > /etc/apt/sources.list.d/microsoft-edge-stable.list'
|
||||||
|
rm /tmp/microsoft.gpg
|
||||||
|
apt-get update && apt-get install -y microsoft-edge-stable
|
||||||
|
|
||||||
|
microsoft-edge-stable --version
|
||||||
11
node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh
generated
vendored
Normal file
11
node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
cd /tmp
|
||||||
|
curl -o ./msedge_stable.pkg -k "$1"
|
||||||
|
# Note: there's no way to uninstall previously installed MSEdge.
|
||||||
|
# However, running PKG again seems to update installation.
|
||||||
|
sudo installer -pkg /tmp/msedge_stable.pkg -target /
|
||||||
|
rm -rf /tmp/msedge_stable.pkg
|
||||||
|
/Applications/Microsoft\ Edge.app/Contents/MacOS/Microsoft\ Edge --version
|
||||||
24
node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1
generated
vendored
Normal file
24
node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$url = $args[0]
|
||||||
|
|
||||||
|
Write-Host "Downloading Microsoft Edge"
|
||||||
|
$wc = New-Object net.webclient
|
||||||
|
$msiInstaller = "$env:temp\microsoft-edge-stable.msi"
|
||||||
|
$wc.Downloadfile($url, $msiInstaller)
|
||||||
|
|
||||||
|
Write-Host "Installing Microsoft Edge"
|
||||||
|
$arguments = "/i `"$msiInstaller`" /quiet"
|
||||||
|
Start-Process msiexec.exe -ArgumentList $arguments -Wait
|
||||||
|
Remove-Item $msiInstaller
|
||||||
|
|
||||||
|
$suffix = "\\Microsoft\\Edge\\Application\\msedge.exe"
|
||||||
|
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
|
||||||
|
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
|
||||||
|
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
|
||||||
|
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
|
||||||
|
} else {
|
||||||
|
Write-Host "ERROR: Failed to install Microsoft Edge."
|
||||||
|
Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
65
node_modules/playwright-core/browsers.json
generated
vendored
Normal file
65
node_modules/playwright-core/browsers.json
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"comment": "Do not edit this file, use utils/roll_browser.js",
|
||||||
|
"browsers": [
|
||||||
|
{
|
||||||
|
"name": "chromium",
|
||||||
|
"revision": "1148",
|
||||||
|
"installByDefault": true,
|
||||||
|
"browserVersion": "131.0.6778.33"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "chromium-headless-shell",
|
||||||
|
"revision": "1148",
|
||||||
|
"installByDefault": true,
|
||||||
|
"browserVersion": "131.0.6778.33"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "chromium-tip-of-tree",
|
||||||
|
"revision": "1277",
|
||||||
|
"installByDefault": false,
|
||||||
|
"browserVersion": "132.0.6834.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "firefox",
|
||||||
|
"revision": "1466",
|
||||||
|
"installByDefault": true,
|
||||||
|
"browserVersion": "132.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "firefox-beta",
|
||||||
|
"revision": "1465",
|
||||||
|
"installByDefault": false,
|
||||||
|
"browserVersion": "132.0b8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "webkit",
|
||||||
|
"revision": "2104",
|
||||||
|
"installByDefault": true,
|
||||||
|
"revisionOverrides": {
|
||||||
|
"mac10.14": "1446",
|
||||||
|
"mac10.15": "1616",
|
||||||
|
"mac11": "1816",
|
||||||
|
"mac11-arm64": "1816",
|
||||||
|
"mac12": "2009",
|
||||||
|
"mac12-arm64": "2009",
|
||||||
|
"ubuntu20.04-x64": "2092",
|
||||||
|
"ubuntu20.04-arm64": "2092"
|
||||||
|
},
|
||||||
|
"browserVersion": "18.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ffmpeg",
|
||||||
|
"revision": "1010",
|
||||||
|
"installByDefault": true,
|
||||||
|
"revisionOverrides": {
|
||||||
|
"mac12": "1010",
|
||||||
|
"mac12-arm64": "1010"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "android",
|
||||||
|
"revision": "1001",
|
||||||
|
"installByDefault": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
18
node_modules/playwright-core/cli.js
generated
vendored
Normal file
18
node_modules/playwright-core/cli.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
const { program } = require('./lib/cli/programWithTestStub');
|
||||||
|
program.parse(process.argv);
|
||||||
17
node_modules/playwright-core/index.d.ts
generated
vendored
Normal file
17
node_modules/playwright-core/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './types/types';
|
||||||
33
node_modules/playwright-core/index.js
generated
vendored
Normal file
33
node_modules/playwright-core/index.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
const minimumMajorNodeVersion = 14;
|
||||||
|
const currentNodeVersion = process.versions.node;
|
||||||
|
const semver = currentNodeVersion.split('.');
|
||||||
|
const [major] = [+semver[0]];
|
||||||
|
|
||||||
|
if (major < minimumMajorNodeVersion) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(
|
||||||
|
'You are running Node.js ' +
|
||||||
|
currentNodeVersion +
|
||||||
|
'.\n' +
|
||||||
|
`Playwright requires Node.js ${minimumMajorNodeVersion} or higher. \n` +
|
||||||
|
'Please update your version of Node.js.'
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = require('./lib/inprocess');
|
||||||
28
node_modules/playwright-core/index.mjs
generated
vendored
Normal file
28
node_modules/playwright-core/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import playwright from './index.js';
|
||||||
|
|
||||||
|
export const chromium = playwright.chromium;
|
||||||
|
export const firefox = playwright.firefox;
|
||||||
|
export const webkit = playwright.webkit;
|
||||||
|
export const selectors = playwright.selectors;
|
||||||
|
export const devices = playwright.devices;
|
||||||
|
export const errors = playwright.errors;
|
||||||
|
export const request = playwright.request;
|
||||||
|
export const _electron = playwright._electron;
|
||||||
|
export const _android = playwright._android;
|
||||||
|
export default playwright;
|
||||||
69
node_modules/playwright-core/lib/androidServerImpl.js
generated
vendored
Normal file
69
node_modules/playwright-core/lib/androidServerImpl.js
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.AndroidServerLauncherImpl = void 0;
|
||||||
|
var _utilsBundle = require("./utilsBundle");
|
||||||
|
var _utils = require("./utils");
|
||||||
|
var _playwright = require("./server/playwright");
|
||||||
|
var _playwrightServer = require("./remote/playwrightServer");
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class AndroidServerLauncherImpl {
|
||||||
|
async launchServer(options = {}) {
|
||||||
|
const playwright = (0, _playwright.createPlaywright)({
|
||||||
|
sdkLanguage: 'javascript',
|
||||||
|
isServer: true
|
||||||
|
});
|
||||||
|
// 1. Pre-connect to the device
|
||||||
|
let devices = await playwright.android.devices({
|
||||||
|
host: options.adbHost,
|
||||||
|
port: options.adbPort,
|
||||||
|
omitDriverInstall: options.omitDriverInstall
|
||||||
|
});
|
||||||
|
if (devices.length === 0) throw new Error('No devices found');
|
||||||
|
if (options.deviceSerialNumber) {
|
||||||
|
devices = devices.filter(d => d.serial === options.deviceSerialNumber);
|
||||||
|
if (devices.length === 0) throw new Error(`No device with serial number '${options.deviceSerialNumber}' not found`);
|
||||||
|
}
|
||||||
|
if (devices.length > 1) throw new Error(`More than one device found. Please specify deviceSerialNumber`);
|
||||||
|
const device = devices[0];
|
||||||
|
const path = options.wsPath ? options.wsPath.startsWith('/') ? options.wsPath : `/${options.wsPath}` : `/${(0, _utils.createGuid)()}`;
|
||||||
|
|
||||||
|
// 2. Start the server
|
||||||
|
const server = new _playwrightServer.PlaywrightServer({
|
||||||
|
mode: 'launchServer',
|
||||||
|
path,
|
||||||
|
maxConnections: 1,
|
||||||
|
preLaunchedAndroidDevice: device
|
||||||
|
});
|
||||||
|
const wsEndpoint = await server.listen(options.port, options.host);
|
||||||
|
|
||||||
|
// 3. Return the BrowserServer interface
|
||||||
|
const browserServer = new _utilsBundle.ws.EventEmitter();
|
||||||
|
browserServer.wsEndpoint = () => wsEndpoint;
|
||||||
|
browserServer.close = () => device.close();
|
||||||
|
browserServer.kill = () => device.close();
|
||||||
|
device.on('close', () => {
|
||||||
|
server.close();
|
||||||
|
browserServer.emit('close');
|
||||||
|
});
|
||||||
|
return browserServer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.AndroidServerLauncherImpl = AndroidServerLauncherImpl;
|
||||||
92
node_modules/playwright-core/lib/browserServerImpl.js
generated
vendored
Normal file
92
node_modules/playwright-core/lib/browserServerImpl.js
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.BrowserServerLauncherImpl = void 0;
|
||||||
|
var _utilsBundle = require("./utilsBundle");
|
||||||
|
var _clientHelper = require("./client/clientHelper");
|
||||||
|
var _utils = require("./utils");
|
||||||
|
var _instrumentation = require("./server/instrumentation");
|
||||||
|
var _playwright = require("./server/playwright");
|
||||||
|
var _playwrightServer = require("./remote/playwrightServer");
|
||||||
|
var _helper = require("./server/helper");
|
||||||
|
var _stackTrace = require("./utils/stackTrace");
|
||||||
|
var _socksProxy = require("./common/socksProxy");
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class BrowserServerLauncherImpl {
|
||||||
|
constructor(browserName) {
|
||||||
|
this._browserName = void 0;
|
||||||
|
this._browserName = browserName;
|
||||||
|
}
|
||||||
|
async launchServer(options = {}) {
|
||||||
|
const playwright = (0, _playwright.createPlaywright)({
|
||||||
|
sdkLanguage: 'javascript',
|
||||||
|
isServer: true
|
||||||
|
});
|
||||||
|
// TODO: enable socks proxy once ipv6 is supported.
|
||||||
|
const socksProxy = false ? new _socksProxy.SocksProxy() : undefined;
|
||||||
|
playwright.options.socksProxyPort = await (socksProxy === null || socksProxy === void 0 ? void 0 : socksProxy.listen(0));
|
||||||
|
|
||||||
|
// 1. Pre-launch the browser
|
||||||
|
const metadata = (0, _instrumentation.serverSideCallMetadata)();
|
||||||
|
const browser = await playwright[this._browserName].launch(metadata, {
|
||||||
|
...options,
|
||||||
|
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined,
|
||||||
|
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
|
||||||
|
env: options.env ? (0, _clientHelper.envObjectToArray)(options.env) : undefined
|
||||||
|
}, toProtocolLogger(options.logger)).catch(e => {
|
||||||
|
const log = _helper.helper.formatBrowserLogs(metadata.log);
|
||||||
|
(0, _stackTrace.rewriteErrorMessage)(e, `${e.message} Failed to launch browser.${log}`);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
const path = options.wsPath ? options.wsPath.startsWith('/') ? options.wsPath : `/${options.wsPath}` : `/${(0, _utils.createGuid)()}`;
|
||||||
|
|
||||||
|
// 2. Start the server
|
||||||
|
const server = new _playwrightServer.PlaywrightServer({
|
||||||
|
mode: 'launchServer',
|
||||||
|
path,
|
||||||
|
maxConnections: Infinity,
|
||||||
|
preLaunchedBrowser: browser,
|
||||||
|
preLaunchedSocksProxy: socksProxy
|
||||||
|
});
|
||||||
|
const wsEndpoint = await server.listen(options.port, options.host);
|
||||||
|
|
||||||
|
// 3. Return the BrowserServer interface
|
||||||
|
const browserServer = new _utilsBundle.ws.EventEmitter();
|
||||||
|
browserServer.process = () => browser.options.browserProcess.process;
|
||||||
|
browserServer.wsEndpoint = () => wsEndpoint;
|
||||||
|
browserServer.close = () => browser.options.browserProcess.close();
|
||||||
|
browserServer[Symbol.asyncDispose] = browserServer.close;
|
||||||
|
browserServer.kill = () => browser.options.browserProcess.kill();
|
||||||
|
browserServer._disconnectForTest = () => server.close();
|
||||||
|
browserServer._userDataDirForTest = browser._userDataDirForTest;
|
||||||
|
browser.options.browserProcess.onclose = (exitCode, signal) => {
|
||||||
|
socksProxy === null || socksProxy === void 0 || socksProxy.close().catch(() => {});
|
||||||
|
server.close();
|
||||||
|
browserServer.emit('close', exitCode, signal);
|
||||||
|
};
|
||||||
|
return browserServer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.BrowserServerLauncherImpl = BrowserServerLauncherImpl;
|
||||||
|
function toProtocolLogger(logger) {
|
||||||
|
return logger ? (direction, message) => {
|
||||||
|
if (logger.isEnabled('protocol', 'verbose')) logger.log('protocol', 'verbose', (direction === 'send' ? 'SEND ► ' : '◀ RECV ') + JSON.stringify(message), [], {});
|
||||||
|
} : undefined;
|
||||||
|
}
|
||||||
95
node_modules/playwright-core/lib/cli/driver.js
generated
vendored
Normal file
95
node_modules/playwright-core/lib/cli/driver.js
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.launchBrowserServer = launchBrowserServer;
|
||||||
|
exports.printApiJson = printApiJson;
|
||||||
|
exports.runDriver = runDriver;
|
||||||
|
exports.runServer = runServer;
|
||||||
|
var _fs = _interopRequireDefault(require("fs"));
|
||||||
|
var playwright = _interopRequireWildcard(require("../.."));
|
||||||
|
var _server = require("../server");
|
||||||
|
var _transport = require("../protocol/transport");
|
||||||
|
var _playwrightServer = require("../remote/playwrightServer");
|
||||||
|
var _processLauncher = require("../utils/processLauncher");
|
||||||
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||||
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
|
function printApiJson() {
|
||||||
|
// Note: this file is generated by build-playwright-driver.sh
|
||||||
|
console.log(JSON.stringify(require('../../api.json')));
|
||||||
|
}
|
||||||
|
function runDriver() {
|
||||||
|
const dispatcherConnection = new _server.DispatcherConnection();
|
||||||
|
new _server.RootDispatcher(dispatcherConnection, async (rootScope, {
|
||||||
|
sdkLanguage
|
||||||
|
}) => {
|
||||||
|
const playwright = (0, _server.createPlaywright)({
|
||||||
|
sdkLanguage
|
||||||
|
});
|
||||||
|
return new _server.PlaywrightDispatcher(rootScope, playwright);
|
||||||
|
});
|
||||||
|
const transport = new _transport.PipeTransport(process.stdout, process.stdin);
|
||||||
|
transport.onmessage = message => dispatcherConnection.dispatch(JSON.parse(message));
|
||||||
|
// Certain Language Binding JSON parsers (e.g. .NET) do not like strings with lone surrogates.
|
||||||
|
const isJavaScriptLanguageBinding = !process.env.PW_LANG_NAME || process.env.PW_LANG_NAME === 'javascript';
|
||||||
|
const replacer = !isJavaScriptLanguageBinding && String.prototype.toWellFormed ? (key, value) => {
|
||||||
|
if (typeof value === 'string') return value.toWellFormed();
|
||||||
|
return value;
|
||||||
|
} : undefined;
|
||||||
|
dispatcherConnection.onmessage = message => transport.send(JSON.stringify(message, replacer));
|
||||||
|
transport.onclose = () => {
|
||||||
|
// Drop any messages during shutdown on the floor.
|
||||||
|
dispatcherConnection.onmessage = () => {};
|
||||||
|
(0, _processLauncher.gracefullyProcessExitDoNotHang)(0);
|
||||||
|
};
|
||||||
|
// Ignore the SIGINT signal in the driver process so the parent can gracefully close the connection.
|
||||||
|
// We still will destruct everything (close browsers and exit) when the transport pipe closes.
|
||||||
|
process.on('SIGINT', () => {
|
||||||
|
// Keep the process running.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function runServer(options) {
|
||||||
|
const {
|
||||||
|
port,
|
||||||
|
host,
|
||||||
|
path = '/',
|
||||||
|
maxConnections = Infinity,
|
||||||
|
extension
|
||||||
|
} = options;
|
||||||
|
const server = new _playwrightServer.PlaywrightServer({
|
||||||
|
mode: extension ? 'extension' : 'default',
|
||||||
|
path,
|
||||||
|
maxConnections
|
||||||
|
});
|
||||||
|
const wsEndpoint = await server.listen(port, host);
|
||||||
|
process.on('exit', () => server.close().catch(console.error));
|
||||||
|
console.log('Listening on ' + wsEndpoint);
|
||||||
|
process.stdin.on('close', () => (0, _processLauncher.gracefullyProcessExitDoNotHang)(0));
|
||||||
|
}
|
||||||
|
async function launchBrowserServer(browserName, configFile) {
|
||||||
|
let options = {};
|
||||||
|
if (configFile) options = JSON.parse(_fs.default.readFileSync(configFile).toString());
|
||||||
|
const browserType = playwright[browserName];
|
||||||
|
const server = await browserType.launchServer(options);
|
||||||
|
console.log(server.wsEndpoint());
|
||||||
|
}
|
||||||
607
node_modules/playwright-core/lib/cli/program.js
generated
vendored
Normal file
607
node_modules/playwright-core/lib/cli/program.js
generated
vendored
Normal file
@@ -0,0 +1,607 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "program", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _utilsBundle.program;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var _fs = _interopRequireDefault(require("fs"));
|
||||||
|
var _os = _interopRequireDefault(require("os"));
|
||||||
|
var _path = _interopRequireDefault(require("path"));
|
||||||
|
var _utilsBundle = require("../utilsBundle");
|
||||||
|
var _driver = require("./driver");
|
||||||
|
var _traceViewer = require("../server/trace/viewer/traceViewer");
|
||||||
|
var playwright = _interopRequireWildcard(require("../.."));
|
||||||
|
var _child_process = require("child_process");
|
||||||
|
var _utils = require("../utils");
|
||||||
|
var _server = require("../server");
|
||||||
|
var _errors = require("../client/errors");
|
||||||
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||||
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
|
const packageJSON = require('../../package.json');
|
||||||
|
_utilsBundle.program.version('Version ' + (process.env.PW_CLI_DISPLAY_VERSION || packageJSON.version)).name(buildBasePlaywrightCLICommand(process.env.PW_LANG_NAME));
|
||||||
|
_utilsBundle.program.command('mark-docker-image [dockerImageNameTemplate]', {
|
||||||
|
hidden: true
|
||||||
|
}).description('mark docker image').allowUnknownOption(true).action(function (dockerImageNameTemplate) {
|
||||||
|
(0, _utils.assert)(dockerImageNameTemplate, 'dockerImageNameTemplate is required');
|
||||||
|
(0, _server.writeDockerVersion)(dockerImageNameTemplate).catch(logErrorAndExit);
|
||||||
|
});
|
||||||
|
commandWithOpenOptions('open [url]', 'open page in browser specified via -b, --browser', []).action(function (url, options) {
|
||||||
|
open(options, url, codegenId()).catch(logErrorAndExit);
|
||||||
|
}).addHelpText('afterAll', `
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
$ open
|
||||||
|
$ open -b webkit https://example.com`);
|
||||||
|
commandWithOpenOptions('codegen [url]', 'open page and generate code for user actions', [['-o, --output <file name>', 'saves the generated script to a file'], ['--target <language>', `language to generate, one of javascript, playwright-test, python, python-async, python-pytest, csharp, csharp-mstest, csharp-nunit, java, java-junit`, codegenId()], ['--save-trace <filename>', 'record a trace for the session and save it to a file'], ['--test-id-attribute <attributeName>', 'use the specified attribute to generate data test ID selectors']]).action(function (url, options) {
|
||||||
|
codegen(options, url).catch(logErrorAndExit);
|
||||||
|
}).addHelpText('afterAll', `
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
$ codegen
|
||||||
|
$ codegen --target=python
|
||||||
|
$ codegen -b webkit https://example.com`);
|
||||||
|
_utilsBundle.program.command('debug <app> [args...]', {
|
||||||
|
hidden: true
|
||||||
|
}).description('run command in debug mode: disable timeout, open inspector').allowUnknownOption(true).action(function (app, options) {
|
||||||
|
(0, _child_process.spawn)(app, options, {
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
PWDEBUG: '1'
|
||||||
|
},
|
||||||
|
stdio: 'inherit'
|
||||||
|
});
|
||||||
|
}).addHelpText('afterAll', `
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
$ debug node test.js
|
||||||
|
$ debug npm run test`);
|
||||||
|
function suggestedBrowsersToInstall() {
|
||||||
|
return _server.registry.executables().filter(e => e.installType !== 'none' && e.type !== 'tool').map(e => e.name).join(', ');
|
||||||
|
}
|
||||||
|
function defaultBrowsersToInstall(options) {
|
||||||
|
let executables = _server.registry.defaultExecutables();
|
||||||
|
if (options.noShell) executables = executables.filter(e => e.name !== 'chromium-headless-shell');
|
||||||
|
if (options.onlyShell) executables = executables.filter(e => e.name !== 'chromium');
|
||||||
|
return executables;
|
||||||
|
}
|
||||||
|
function checkBrowsersToInstall(args, options) {
|
||||||
|
if (options.noShell && options.onlyShell) throw new Error(`Only one of --no-shell and --only-shell can be specified`);
|
||||||
|
const faultyArguments = [];
|
||||||
|
const executables = [];
|
||||||
|
const handleArgument = arg => {
|
||||||
|
const executable = _server.registry.findExecutable(arg);
|
||||||
|
if (!executable || executable.installType === 'none') faultyArguments.push(arg);else executables.push(executable);
|
||||||
|
if ((executable === null || executable === void 0 ? void 0 : executable.browserName) === 'chromium') executables.push(_server.registry.findExecutable('ffmpeg'));
|
||||||
|
};
|
||||||
|
for (const arg of args) {
|
||||||
|
if (arg === 'chromium') {
|
||||||
|
if (!options.onlyShell) handleArgument('chromium');
|
||||||
|
if (!options.noShell) handleArgument('chromium-headless-shell');
|
||||||
|
} else {
|
||||||
|
handleArgument(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (faultyArguments.length) throw new Error(`Invalid installation targets: ${faultyArguments.map(name => `'${name}'`).join(', ')}. Expecting one of: ${suggestedBrowsersToInstall()}`);
|
||||||
|
return executables;
|
||||||
|
}
|
||||||
|
_utilsBundle.program.command('install [browser...]').description('ensure browsers necessary for this version of Playwright are installed').option('--with-deps', 'install system dependencies for browsers').option('--dry-run', 'do not execute installation, only print information').option('--force', 'force reinstall of stable browser channels').option('--only-shell', 'only install headless shell when installing chromium').option('--no-shell', 'do not install chromium headless shell').action(async function (args, options) {
|
||||||
|
// For '--no-shell' option, commander sets `shell: false` instead.
|
||||||
|
if (options.shell === false) options.noShell = true;
|
||||||
|
if ((0, _utils.isLikelyNpxGlobal)()) {
|
||||||
|
console.error((0, _utils.wrapInASCIIBox)([`WARNING: It looks like you are running 'npx playwright install' without first`, `installing your project's dependencies.`, ``, `To avoid unexpected behavior, please install your dependencies first, and`, `then run Playwright's install command:`, ``, ` npm install`, ` npx playwright install`, ``, `If your project does not yet depend on Playwright, first install the`, `applicable npm package (most commonly @playwright/test), and`, `then run Playwright's install command to download the browsers:`, ``, ` npm install @playwright/test`, ` npx playwright install`, ``].join('\n'), 1));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const hasNoArguments = !args.length;
|
||||||
|
const executables = hasNoArguments ? defaultBrowsersToInstall(options) : checkBrowsersToInstall(args, options);
|
||||||
|
if (options.withDeps) await _server.registry.installDeps(executables, !!options.dryRun);
|
||||||
|
if (options.dryRun) {
|
||||||
|
for (const executable of executables) {
|
||||||
|
var _executable$directory, _executable$downloadU;
|
||||||
|
const version = executable.browserVersion ? `version ` + executable.browserVersion : '';
|
||||||
|
console.log(`browser: ${executable.name}${version ? ' ' + version : ''}`);
|
||||||
|
console.log(` Install location: ${(_executable$directory = executable.directory) !== null && _executable$directory !== void 0 ? _executable$directory : '<system>'}`);
|
||||||
|
if ((_executable$downloadU = executable.downloadURLs) !== null && _executable$downloadU !== void 0 && _executable$downloadU.length) {
|
||||||
|
const [url, ...fallbacks] = executable.downloadURLs;
|
||||||
|
console.log(` Download url: ${url}`);
|
||||||
|
for (let i = 0; i < fallbacks.length; ++i) console.log(` Download fallback ${i + 1}: ${fallbacks[i]}`);
|
||||||
|
}
|
||||||
|
console.log(``);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const forceReinstall = hasNoArguments ? false : !!options.force;
|
||||||
|
await _server.registry.install(executables, forceReinstall);
|
||||||
|
await _server.registry.validateHostRequirementsForExecutablesIfNeeded(executables, process.env.PW_LANG_NAME || 'javascript').catch(e => {
|
||||||
|
e.name = 'Playwright Host validation warning';
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`Failed to install browsers\n${e}`);
|
||||||
|
(0, _utils.gracefullyProcessExitDoNotHang)(1);
|
||||||
|
}
|
||||||
|
}).addHelpText('afterAll', `
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- $ install
|
||||||
|
Install default browsers.
|
||||||
|
|
||||||
|
- $ install chrome firefox
|
||||||
|
Install custom browsers, supports ${suggestedBrowsersToInstall()}.`);
|
||||||
|
_utilsBundle.program.command('uninstall').description('Removes browsers used by this installation of Playwright from the system (chromium, firefox, webkit, ffmpeg). This does not include branded channels.').option('--all', 'Removes all browsers used by any Playwright installation from the system.').action(async options => {
|
||||||
|
delete process.env.PLAYWRIGHT_SKIP_BROWSER_GC;
|
||||||
|
await _server.registry.uninstall(!!options.all).then(({
|
||||||
|
numberOfBrowsersLeft
|
||||||
|
}) => {
|
||||||
|
if (!options.all && numberOfBrowsersLeft > 0) {
|
||||||
|
console.log('Successfully uninstalled Playwright browsers for the current Playwright installation.');
|
||||||
|
console.log(`There are still ${numberOfBrowsersLeft} browsers left, used by other Playwright installations.\nTo uninstall Playwright browsers for all installations, re-run with --all flag.`);
|
||||||
|
}
|
||||||
|
}).catch(logErrorAndExit);
|
||||||
|
});
|
||||||
|
_utilsBundle.program.command('install-deps [browser...]').description('install dependencies necessary to run browsers (will ask for sudo permissions)').option('--dry-run', 'Do not execute installation commands, only print them').action(async function (args, options) {
|
||||||
|
try {
|
||||||
|
if (!args.length) await _server.registry.installDeps(defaultBrowsersToInstall({}), !!options.dryRun);else await _server.registry.installDeps(checkBrowsersToInstall(args, {}), !!options.dryRun);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`Failed to install browser dependencies\n${e}`);
|
||||||
|
(0, _utils.gracefullyProcessExitDoNotHang)(1);
|
||||||
|
}
|
||||||
|
}).addHelpText('afterAll', `
|
||||||
|
Examples:
|
||||||
|
- $ install-deps
|
||||||
|
Install dependencies for default browsers.
|
||||||
|
|
||||||
|
- $ install-deps chrome firefox
|
||||||
|
Install dependencies for specific browsers, supports ${suggestedBrowsersToInstall()}.`);
|
||||||
|
const browsers = [{
|
||||||
|
alias: 'cr',
|
||||||
|
name: 'Chromium',
|
||||||
|
type: 'chromium'
|
||||||
|
}, {
|
||||||
|
alias: 'ff',
|
||||||
|
name: 'Firefox',
|
||||||
|
type: 'firefox'
|
||||||
|
}, {
|
||||||
|
alias: 'wk',
|
||||||
|
name: 'WebKit',
|
||||||
|
type: 'webkit'
|
||||||
|
}];
|
||||||
|
for (const {
|
||||||
|
alias,
|
||||||
|
name,
|
||||||
|
type
|
||||||
|
} of browsers) {
|
||||||
|
commandWithOpenOptions(`${alias} [url]`, `open page in ${name}`, []).action(function (url, options) {
|
||||||
|
open({
|
||||||
|
...options,
|
||||||
|
browser: type
|
||||||
|
}, url, options.target).catch(logErrorAndExit);
|
||||||
|
}).addHelpText('afterAll', `
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
$ ${alias} https://example.com`);
|
||||||
|
}
|
||||||
|
commandWithOpenOptions('screenshot <url> <filename>', 'capture a page screenshot', [['--wait-for-selector <selector>', 'wait for selector before taking a screenshot'], ['--wait-for-timeout <timeout>', 'wait for timeout in milliseconds before taking a screenshot'], ['--full-page', 'whether to take a full page screenshot (entire scrollable area)']]).action(function (url, filename, command) {
|
||||||
|
screenshot(command, command, url, filename).catch(logErrorAndExit);
|
||||||
|
}).addHelpText('afterAll', `
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
$ screenshot -b webkit https://example.com example.png`);
|
||||||
|
commandWithOpenOptions('pdf <url> <filename>', 'save page as pdf', [['--wait-for-selector <selector>', 'wait for given selector before saving as pdf'], ['--wait-for-timeout <timeout>', 'wait for given timeout in milliseconds before saving as pdf']]).action(function (url, filename, options) {
|
||||||
|
pdf(options, options, url, filename).catch(logErrorAndExit);
|
||||||
|
}).addHelpText('afterAll', `
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
$ pdf https://example.com example.pdf`);
|
||||||
|
_utilsBundle.program.command('run-driver', {
|
||||||
|
hidden: true
|
||||||
|
}).action(function (options) {
|
||||||
|
(0, _driver.runDriver)();
|
||||||
|
});
|
||||||
|
_utilsBundle.program.command('run-server', {
|
||||||
|
hidden: true
|
||||||
|
}).option('--port <port>', 'Server port').option('--host <host>', 'Server host').option('--path <path>', 'Endpoint Path', '/').option('--max-clients <maxClients>', 'Maximum clients').option('--mode <mode>', 'Server mode, either "default" or "extension"').action(function (options) {
|
||||||
|
(0, _driver.runServer)({
|
||||||
|
port: options.port ? +options.port : undefined,
|
||||||
|
host: options.host,
|
||||||
|
path: options.path,
|
||||||
|
maxConnections: options.maxClients ? +options.maxClients : Infinity,
|
||||||
|
extension: options.mode === 'extension' || !!process.env.PW_EXTENSION_MODE
|
||||||
|
}).catch(logErrorAndExit);
|
||||||
|
});
|
||||||
|
_utilsBundle.program.command('print-api-json', {
|
||||||
|
hidden: true
|
||||||
|
}).action(function (options) {
|
||||||
|
(0, _driver.printApiJson)();
|
||||||
|
});
|
||||||
|
_utilsBundle.program.command('launch-server', {
|
||||||
|
hidden: true
|
||||||
|
}).requiredOption('--browser <browserName>', 'Browser name, one of "chromium", "firefox" or "webkit"').option('--config <path-to-config-file>', 'JSON file with launchServer options').action(function (options) {
|
||||||
|
(0, _driver.launchBrowserServer)(options.browser, options.config);
|
||||||
|
});
|
||||||
|
_utilsBundle.program.command('show-trace [trace...]').option('-b, --browser <browserType>', 'browser to use, one of cr, chromium, ff, firefox, wk, webkit', 'chromium').option('-h, --host <host>', 'Host to serve trace on; specifying this option opens trace in a browser tab').option('-p, --port <port>', 'Port to serve trace on, 0 for any free port; specifying this option opens trace in a browser tab').option('--stdin', 'Accept trace URLs over stdin to update the viewer').description('show trace viewer').action(function (traces, options) {
|
||||||
|
if (options.browser === 'cr') options.browser = 'chromium';
|
||||||
|
if (options.browser === 'ff') options.browser = 'firefox';
|
||||||
|
if (options.browser === 'wk') options.browser = 'webkit';
|
||||||
|
const openOptions = {
|
||||||
|
host: options.host,
|
||||||
|
port: +options.port,
|
||||||
|
isServer: !!options.stdin
|
||||||
|
};
|
||||||
|
if (options.port !== undefined || options.host !== undefined) (0, _traceViewer.runTraceInBrowser)(traces, openOptions).catch(logErrorAndExit);else (0, _traceViewer.runTraceViewerApp)(traces, options.browser, openOptions, true).catch(logErrorAndExit);
|
||||||
|
}).addHelpText('afterAll', `
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
$ show-trace https://example.com/trace.zip`);
|
||||||
|
async function launchContext(options, extraOptions) {
|
||||||
|
validateOptions(options);
|
||||||
|
const browserType = lookupBrowserType(options);
|
||||||
|
const launchOptions = extraOptions;
|
||||||
|
if (options.channel) launchOptions.channel = options.channel;
|
||||||
|
launchOptions.handleSIGINT = false;
|
||||||
|
const contextOptions =
|
||||||
|
// Copy the device descriptor since we have to compare and modify the options.
|
||||||
|
options.device ? {
|
||||||
|
...playwright.devices[options.device]
|
||||||
|
} : {};
|
||||||
|
|
||||||
|
// In headful mode, use host device scale factor for things to look nice.
|
||||||
|
// In headless, keep things the way it works in Playwright by default.
|
||||||
|
// Assume high-dpi on MacOS. TODO: this is not perfect.
|
||||||
|
if (!extraOptions.headless) contextOptions.deviceScaleFactor = _os.default.platform() === 'darwin' ? 2 : 1;
|
||||||
|
|
||||||
|
// Work around the WebKit GTK scrolling issue.
|
||||||
|
if (browserType.name() === 'webkit' && process.platform === 'linux') {
|
||||||
|
delete contextOptions.hasTouch;
|
||||||
|
delete contextOptions.isMobile;
|
||||||
|
}
|
||||||
|
if (contextOptions.isMobile && browserType.name() === 'firefox') contextOptions.isMobile = undefined;
|
||||||
|
if (options.blockServiceWorkers) contextOptions.serviceWorkers = 'block';
|
||||||
|
|
||||||
|
// Proxy
|
||||||
|
|
||||||
|
if (options.proxyServer) {
|
||||||
|
launchOptions.proxy = {
|
||||||
|
server: options.proxyServer
|
||||||
|
};
|
||||||
|
if (options.proxyBypass) launchOptions.proxy.bypass = options.proxyBypass;
|
||||||
|
}
|
||||||
|
const browser = await browserType.launch(launchOptions);
|
||||||
|
if (process.env.PWTEST_CLI_IS_UNDER_TEST) {
|
||||||
|
process._didSetSourcesForTest = text => {
|
||||||
|
process.stdout.write('\n-------------8<-------------\n');
|
||||||
|
process.stdout.write(text);
|
||||||
|
process.stdout.write('\n-------------8<-------------\n');
|
||||||
|
const autoExitCondition = process.env.PWTEST_CLI_AUTO_EXIT_WHEN;
|
||||||
|
if (autoExitCondition && text.includes(autoExitCondition)) closeBrowser();
|
||||||
|
};
|
||||||
|
// Make sure we exit abnormally when browser crashes.
|
||||||
|
const logs = [];
|
||||||
|
require('playwright-core/lib/utilsBundle').debug.log = (...args) => {
|
||||||
|
const line = require('util').format(...args) + '\n';
|
||||||
|
logs.push(line);
|
||||||
|
process.stderr.write(line);
|
||||||
|
};
|
||||||
|
browser.on('disconnected', () => {
|
||||||
|
const hasCrashLine = logs.some(line => line.includes('process did exit:') && !line.includes('process did exit: exitCode=0, signal=null'));
|
||||||
|
if (hasCrashLine) {
|
||||||
|
process.stderr.write('Detected browser crash.\n');
|
||||||
|
(0, _utils.gracefullyProcessExitDoNotHang)(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Viewport size
|
||||||
|
if (options.viewportSize) {
|
||||||
|
try {
|
||||||
|
const [width, height] = options.viewportSize.split(',').map(n => parseInt(n, 10));
|
||||||
|
contextOptions.viewport = {
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Invalid viewport size format: use "width, height", for example --viewport-size=800,600');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Geolocation
|
||||||
|
|
||||||
|
if (options.geolocation) {
|
||||||
|
try {
|
||||||
|
const [latitude, longitude] = options.geolocation.split(',').map(n => parseFloat(n.trim()));
|
||||||
|
contextOptions.geolocation = {
|
||||||
|
latitude,
|
||||||
|
longitude
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Invalid geolocation format, should be "lat,long". For example --geolocation="37.819722,-122.478611"');
|
||||||
|
}
|
||||||
|
contextOptions.permissions = ['geolocation'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// User agent
|
||||||
|
|
||||||
|
if (options.userAgent) contextOptions.userAgent = options.userAgent;
|
||||||
|
|
||||||
|
// Lang
|
||||||
|
|
||||||
|
if (options.lang) contextOptions.locale = options.lang;
|
||||||
|
|
||||||
|
// Color scheme
|
||||||
|
|
||||||
|
if (options.colorScheme) contextOptions.colorScheme = options.colorScheme;
|
||||||
|
|
||||||
|
// Timezone
|
||||||
|
|
||||||
|
if (options.timezone) contextOptions.timezoneId = options.timezone;
|
||||||
|
|
||||||
|
// Storage
|
||||||
|
|
||||||
|
if (options.loadStorage) contextOptions.storageState = options.loadStorage;
|
||||||
|
if (options.ignoreHttpsErrors) contextOptions.ignoreHTTPSErrors = true;
|
||||||
|
|
||||||
|
// HAR
|
||||||
|
|
||||||
|
if (options.saveHar) {
|
||||||
|
contextOptions.recordHar = {
|
||||||
|
path: _path.default.resolve(process.cwd(), options.saveHar),
|
||||||
|
mode: 'minimal'
|
||||||
|
};
|
||||||
|
if (options.saveHarGlob) contextOptions.recordHar.urlFilter = options.saveHarGlob;
|
||||||
|
contextOptions.serviceWorkers = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close app when the last window closes.
|
||||||
|
|
||||||
|
const context = await browser.newContext(contextOptions);
|
||||||
|
let closingBrowser = false;
|
||||||
|
async function closeBrowser() {
|
||||||
|
// We can come here multiple times. For example, saving storage creates
|
||||||
|
// a temporary page and we call closeBrowser again when that page closes.
|
||||||
|
if (closingBrowser) return;
|
||||||
|
closingBrowser = true;
|
||||||
|
if (options.saveTrace) await context.tracing.stop({
|
||||||
|
path: options.saveTrace
|
||||||
|
});
|
||||||
|
if (options.saveStorage) await context.storageState({
|
||||||
|
path: options.saveStorage
|
||||||
|
}).catch(e => null);
|
||||||
|
if (options.saveHar) await context.close();
|
||||||
|
await browser.close();
|
||||||
|
}
|
||||||
|
context.on('page', page => {
|
||||||
|
page.on('dialog', () => {}); // Prevent dialogs from being automatically dismissed.
|
||||||
|
page.on('close', () => {
|
||||||
|
const hasPage = browser.contexts().some(context => context.pages().length > 0);
|
||||||
|
if (hasPage) return;
|
||||||
|
// Avoid the error when the last page is closed because the browser has been closed.
|
||||||
|
closeBrowser().catch(() => {});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
process.on('SIGINT', async () => {
|
||||||
|
await closeBrowser();
|
||||||
|
(0, _utils.gracefullyProcessExitDoNotHang)(130);
|
||||||
|
});
|
||||||
|
const timeout = options.timeout ? parseInt(options.timeout, 10) : 0;
|
||||||
|
context.setDefaultTimeout(timeout);
|
||||||
|
context.setDefaultNavigationTimeout(timeout);
|
||||||
|
if (options.saveTrace) await context.tracing.start({
|
||||||
|
screenshots: true,
|
||||||
|
snapshots: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Omit options that we add automatically for presentation purpose.
|
||||||
|
delete launchOptions.headless;
|
||||||
|
delete launchOptions.executablePath;
|
||||||
|
delete launchOptions.handleSIGINT;
|
||||||
|
delete contextOptions.deviceScaleFactor;
|
||||||
|
return {
|
||||||
|
browser,
|
||||||
|
browserName: browserType.name(),
|
||||||
|
context,
|
||||||
|
contextOptions,
|
||||||
|
launchOptions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async function openPage(context, url) {
|
||||||
|
const page = await context.newPage();
|
||||||
|
if (url) {
|
||||||
|
if (_fs.default.existsSync(url)) url = 'file://' + _path.default.resolve(url);else if (!url.startsWith('http') && !url.startsWith('file://') && !url.startsWith('about:') && !url.startsWith('data:')) url = 'http://' + url;
|
||||||
|
await page.goto(url).catch(error => {
|
||||||
|
if (process.env.PWTEST_CLI_AUTO_EXIT_WHEN && (0, _errors.isTargetClosedError)(error)) {
|
||||||
|
// Tests with PWTEST_CLI_AUTO_EXIT_WHEN might close page too fast, resulting
|
||||||
|
// in a stray navigation aborted error. We should ignore it.
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
async function open(options, url, language) {
|
||||||
|
const {
|
||||||
|
context,
|
||||||
|
launchOptions,
|
||||||
|
contextOptions
|
||||||
|
} = await launchContext(options, {
|
||||||
|
headless: !!process.env.PWTEST_CLI_HEADLESS,
|
||||||
|
executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH
|
||||||
|
});
|
||||||
|
await context._enableRecorder({
|
||||||
|
language,
|
||||||
|
launchOptions,
|
||||||
|
contextOptions,
|
||||||
|
device: options.device,
|
||||||
|
saveStorage: options.saveStorage,
|
||||||
|
handleSIGINT: false
|
||||||
|
});
|
||||||
|
await openPage(context, url);
|
||||||
|
}
|
||||||
|
async function codegen(options, url) {
|
||||||
|
const {
|
||||||
|
target: language,
|
||||||
|
output: outputFile,
|
||||||
|
testIdAttribute: testIdAttributeName
|
||||||
|
} = options;
|
||||||
|
const tracesDir = _path.default.join(_os.default.tmpdir(), `playwright-recorder-trace-${Date.now()}`);
|
||||||
|
const {
|
||||||
|
context,
|
||||||
|
launchOptions,
|
||||||
|
contextOptions
|
||||||
|
} = await launchContext(options, {
|
||||||
|
headless: !!process.env.PWTEST_CLI_HEADLESS,
|
||||||
|
executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH,
|
||||||
|
tracesDir
|
||||||
|
});
|
||||||
|
_utilsBundle.dotenv.config({
|
||||||
|
path: 'playwright.env'
|
||||||
|
});
|
||||||
|
await context._enableRecorder({
|
||||||
|
language,
|
||||||
|
launchOptions,
|
||||||
|
contextOptions,
|
||||||
|
device: options.device,
|
||||||
|
saveStorage: options.saveStorage,
|
||||||
|
mode: 'recording',
|
||||||
|
codegenMode: process.env.PW_RECORDER_IS_TRACE_VIEWER ? 'trace-events' : 'actions',
|
||||||
|
testIdAttributeName,
|
||||||
|
outputFile: outputFile ? _path.default.resolve(outputFile) : undefined,
|
||||||
|
handleSIGINT: false
|
||||||
|
});
|
||||||
|
await openPage(context, url);
|
||||||
|
}
|
||||||
|
async function waitForPage(page, captureOptions) {
|
||||||
|
if (captureOptions.waitForSelector) {
|
||||||
|
console.log(`Waiting for selector ${captureOptions.waitForSelector}...`);
|
||||||
|
await page.waitForSelector(captureOptions.waitForSelector);
|
||||||
|
}
|
||||||
|
if (captureOptions.waitForTimeout) {
|
||||||
|
console.log(`Waiting for timeout ${captureOptions.waitForTimeout}...`);
|
||||||
|
await page.waitForTimeout(parseInt(captureOptions.waitForTimeout, 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function screenshot(options, captureOptions, url, path) {
|
||||||
|
const {
|
||||||
|
context
|
||||||
|
} = await launchContext(options, {
|
||||||
|
headless: true
|
||||||
|
});
|
||||||
|
console.log('Navigating to ' + url);
|
||||||
|
const page = await openPage(context, url);
|
||||||
|
await waitForPage(page, captureOptions);
|
||||||
|
console.log('Capturing screenshot into ' + path);
|
||||||
|
await page.screenshot({
|
||||||
|
path,
|
||||||
|
fullPage: !!captureOptions.fullPage
|
||||||
|
});
|
||||||
|
// launchContext takes care of closing the browser.
|
||||||
|
await page.close();
|
||||||
|
}
|
||||||
|
async function pdf(options, captureOptions, url, path) {
|
||||||
|
if (options.browser !== 'chromium') throw new Error('PDF creation is only working with Chromium');
|
||||||
|
const {
|
||||||
|
context
|
||||||
|
} = await launchContext({
|
||||||
|
...options,
|
||||||
|
browser: 'chromium'
|
||||||
|
}, {
|
||||||
|
headless: true
|
||||||
|
});
|
||||||
|
console.log('Navigating to ' + url);
|
||||||
|
const page = await openPage(context, url);
|
||||||
|
await waitForPage(page, captureOptions);
|
||||||
|
console.log('Saving as pdf into ' + path);
|
||||||
|
await page.pdf({
|
||||||
|
path
|
||||||
|
});
|
||||||
|
// launchContext takes care of closing the browser.
|
||||||
|
await page.close();
|
||||||
|
}
|
||||||
|
function lookupBrowserType(options) {
|
||||||
|
let name = options.browser;
|
||||||
|
if (options.device) {
|
||||||
|
const device = playwright.devices[options.device];
|
||||||
|
name = device.defaultBrowserType;
|
||||||
|
}
|
||||||
|
let browserType;
|
||||||
|
switch (name) {
|
||||||
|
case 'chromium':
|
||||||
|
browserType = playwright.chromium;
|
||||||
|
break;
|
||||||
|
case 'webkit':
|
||||||
|
browserType = playwright.webkit;
|
||||||
|
break;
|
||||||
|
case 'firefox':
|
||||||
|
browserType = playwright.firefox;
|
||||||
|
break;
|
||||||
|
case 'cr':
|
||||||
|
browserType = playwright.chromium;
|
||||||
|
break;
|
||||||
|
case 'wk':
|
||||||
|
browserType = playwright.webkit;
|
||||||
|
break;
|
||||||
|
case 'ff':
|
||||||
|
browserType = playwright.firefox;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (browserType) return browserType;
|
||||||
|
_utilsBundle.program.help();
|
||||||
|
}
|
||||||
|
function validateOptions(options) {
|
||||||
|
if (options.device && !(options.device in playwright.devices)) {
|
||||||
|
const lines = [`Device descriptor not found: '${options.device}', available devices are:`];
|
||||||
|
for (const name in playwright.devices) lines.push(` "${name}"`);
|
||||||
|
throw new Error(lines.join('\n'));
|
||||||
|
}
|
||||||
|
if (options.colorScheme && !['light', 'dark'].includes(options.colorScheme)) throw new Error('Invalid color scheme, should be one of "light", "dark"');
|
||||||
|
}
|
||||||
|
function logErrorAndExit(e) {
|
||||||
|
if (process.env.PWDEBUGIMPL) console.error(e);else console.error(e.name + ': ' + e.message);
|
||||||
|
(0, _utils.gracefullyProcessExitDoNotHang)(1);
|
||||||
|
}
|
||||||
|
function codegenId() {
|
||||||
|
return process.env.PW_LANG_NAME || 'playwright-test';
|
||||||
|
}
|
||||||
|
function commandWithOpenOptions(command, description, options) {
|
||||||
|
let result = _utilsBundle.program.command(command).description(description);
|
||||||
|
for (const option of options) result = result.option(option[0], ...option.slice(1));
|
||||||
|
return result.option('-b, --browser <browserType>', 'browser to use, one of cr, chromium, ff, firefox, wk, webkit', 'chromium').option('--block-service-workers', 'block service workers').option('--channel <channel>', 'Chromium distribution channel, "chrome", "chrome-beta", "msedge-dev", etc').option('--color-scheme <scheme>', 'emulate preferred color scheme, "light" or "dark"').option('--device <deviceName>', 'emulate device, for example "iPhone 11"').option('--geolocation <coordinates>', 'specify geolocation coordinates, for example "37.819722,-122.478611"').option('--ignore-https-errors', 'ignore https errors').option('--load-storage <filename>', 'load context storage state from the file, previously saved with --save-storage').option('--lang <language>', 'specify language / locale, for example "en-GB"').option('--proxy-server <proxy>', 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option('--proxy-bypass <bypass>', 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option('--save-har <filename>', 'save HAR file with all network activity at the end').option('--save-har-glob <glob pattern>', 'filter entries in the HAR by matching url against this glob pattern').option('--save-storage <filename>', 'save context storage state at the end, for later use with --load-storage').option('--timezone <time zone>', 'time zone to emulate, for example "Europe/Rome"').option('--timeout <timeout>', 'timeout for Playwright actions in milliseconds, no timeout by default').option('--user-agent <ua string>', 'specify user agent string').option('--viewport-size <size>', 'specify browser viewport size in pixels, for example "1280, 720"');
|
||||||
|
}
|
||||||
|
function buildBasePlaywrightCLICommand(cliTargetLang) {
|
||||||
|
switch (cliTargetLang) {
|
||||||
|
case 'python':
|
||||||
|
return `playwright`;
|
||||||
|
case 'java':
|
||||||
|
return `mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="...options.."`;
|
||||||
|
case 'csharp':
|
||||||
|
return `pwsh bin/Debug/netX/playwright.ps1`;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
const packageManagerCommand = (0, _utils.getPackageManagerExecCommand)();
|
||||||
|
return `${packageManagerCommand} playwright`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
node_modules/playwright-core/lib/cli/programWithTestStub.js
generated
vendored
Normal file
67
node_modules/playwright-core/lib/cli/programWithTestStub.js
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "program", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _program.program;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var _utils = require("../utils");
|
||||||
|
var _program = require("./program");
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
|
function printPlaywrightTestError(command) {
|
||||||
|
const packages = [];
|
||||||
|
for (const pkg of ['playwright', 'playwright-chromium', 'playwright-firefox', 'playwright-webkit']) {
|
||||||
|
try {
|
||||||
|
require.resolve(pkg);
|
||||||
|
packages.push(pkg);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
if (!packages.length) packages.push('playwright');
|
||||||
|
const packageManager = (0, _utils.getPackageManager)();
|
||||||
|
if (packageManager === 'yarn') {
|
||||||
|
console.error(`Please install @playwright/test package before running "yarn playwright ${command}"`);
|
||||||
|
console.error(` yarn remove ${packages.join(' ')}`);
|
||||||
|
console.error(' yarn add -D @playwright/test');
|
||||||
|
} else if (packageManager === 'pnpm') {
|
||||||
|
console.error(`Please install @playwright/test package before running "pnpm exec playwright ${command}"`);
|
||||||
|
console.error(` pnpm remove ${packages.join(' ')}`);
|
||||||
|
console.error(' pnpm add -D @playwright/test');
|
||||||
|
} else {
|
||||||
|
console.error(`Please install @playwright/test package before running "npx playwright ${command}"`);
|
||||||
|
console.error(` npm uninstall ${packages.join(' ')}`);
|
||||||
|
console.error(' npm install -D @playwright/test');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const kExternalPlaywrightTestCommands = [['test', 'Run tests with Playwright Test.'], ['show-report', 'Show Playwright Test HTML report.'], ['merge-reports', 'Merge Playwright Test Blob reports']];
|
||||||
|
function addExternalPlaywrightTestCommands() {
|
||||||
|
for (const [command, description] of kExternalPlaywrightTestCommands) {
|
||||||
|
const playwrightTest = _program.program.command(command).allowUnknownOption(true);
|
||||||
|
playwrightTest.description(`${description} Available in @playwright/test package.`);
|
||||||
|
playwrightTest.action(async () => {
|
||||||
|
printPlaywrightTestError(command);
|
||||||
|
(0, _utils.gracefullyProcessExitDoNotHang)(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!process.env.PW_LANG_NAME) addExternalPlaywrightTestCommands();
|
||||||
50
node_modules/playwright-core/lib/client/accessibility.js
generated
vendored
Normal file
50
node_modules/playwright-core/lib/client/accessibility.js
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.Accessibility = void 0;
|
||||||
|
/**
|
||||||
|
* Copyright 2017 Google Inc. All rights reserved.
|
||||||
|
* Modifications copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function axNodeFromProtocol(axNode) {
|
||||||
|
const result = {
|
||||||
|
...axNode,
|
||||||
|
value: axNode.valueNumber !== undefined ? axNode.valueNumber : axNode.valueString,
|
||||||
|
checked: axNode.checked === 'checked' ? true : axNode.checked === 'unchecked' ? false : axNode.checked,
|
||||||
|
pressed: axNode.pressed === 'pressed' ? true : axNode.pressed === 'released' ? false : axNode.pressed,
|
||||||
|
children: axNode.children ? axNode.children.map(axNodeFromProtocol) : undefined
|
||||||
|
};
|
||||||
|
delete result.valueNumber;
|
||||||
|
delete result.valueString;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
class Accessibility {
|
||||||
|
constructor(channel) {
|
||||||
|
this._channel = void 0;
|
||||||
|
this._channel = channel;
|
||||||
|
}
|
||||||
|
async snapshot(options = {}) {
|
||||||
|
const root = options.root ? options.root._elementChannel : undefined;
|
||||||
|
const result = await this._channel.accessibilitySnapshot({
|
||||||
|
interestingOnly: options.interestingOnly,
|
||||||
|
root
|
||||||
|
});
|
||||||
|
return result.rootAXNode ? axNodeFromProtocol(result.rootAXNode) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Accessibility = Accessibility;
|
||||||
473
node_modules/playwright-core/lib/client/android.js
generated
vendored
Normal file
473
node_modules/playwright-core/lib/client/android.js
generated
vendored
Normal file
@@ -0,0 +1,473 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.AndroidWebView = exports.AndroidSocket = exports.AndroidInput = exports.AndroidDevice = exports.Android = void 0;
|
||||||
|
var _fs = _interopRequireDefault(require("fs"));
|
||||||
|
var _utils = require("../utils");
|
||||||
|
var _events = require("./events");
|
||||||
|
var _browserContext = require("./browserContext");
|
||||||
|
var _channelOwner = require("./channelOwner");
|
||||||
|
var _timeoutSettings = require("../common/timeoutSettings");
|
||||||
|
var _waiter = require("./waiter");
|
||||||
|
var _events2 = require("events");
|
||||||
|
var _connection = require("./connection");
|
||||||
|
var _errors = require("./errors");
|
||||||
|
var _timeoutRunner = require("../utils/timeoutRunner");
|
||||||
|
let _Symbol$asyncDispose;
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
class Android extends _channelOwner.ChannelOwner {
|
||||||
|
static from(android) {
|
||||||
|
return android._object;
|
||||||
|
}
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
|
this._timeoutSettings = void 0;
|
||||||
|
this._serverLauncher = void 0;
|
||||||
|
this._timeoutSettings = new _timeoutSettings.TimeoutSettings();
|
||||||
|
}
|
||||||
|
setDefaultTimeout(timeout) {
|
||||||
|
this._timeoutSettings.setDefaultTimeout(timeout);
|
||||||
|
this._channel.setDefaultTimeoutNoReply({
|
||||||
|
timeout
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async devices(options = {}) {
|
||||||
|
const {
|
||||||
|
devices
|
||||||
|
} = await this._channel.devices(options);
|
||||||
|
return devices.map(d => AndroidDevice.from(d));
|
||||||
|
}
|
||||||
|
async launchServer(options = {}) {
|
||||||
|
if (!this._serverLauncher) throw new Error('Launching server is not supported');
|
||||||
|
return await this._serverLauncher.launchServer(options);
|
||||||
|
}
|
||||||
|
async connect(wsEndpoint, options = {}) {
|
||||||
|
return await this._wrapApiCall(async () => {
|
||||||
|
const deadline = options.timeout ? (0, _utils.monotonicTime)() + options.timeout : 0;
|
||||||
|
const headers = {
|
||||||
|
'x-playwright-browser': 'android',
|
||||||
|
...options.headers
|
||||||
|
};
|
||||||
|
const localUtils = this._connection.localUtils();
|
||||||
|
const connectParams = {
|
||||||
|
wsEndpoint,
|
||||||
|
headers,
|
||||||
|
slowMo: options.slowMo,
|
||||||
|
timeout: options.timeout
|
||||||
|
};
|
||||||
|
const {
|
||||||
|
pipe
|
||||||
|
} = await localUtils._channel.connect(connectParams);
|
||||||
|
const closePipe = () => pipe.close().catch(() => {});
|
||||||
|
const connection = new _connection.Connection(localUtils, this._instrumentation);
|
||||||
|
connection.markAsRemote();
|
||||||
|
connection.on('close', closePipe);
|
||||||
|
let device;
|
||||||
|
let closeError;
|
||||||
|
const onPipeClosed = () => {
|
||||||
|
var _device;
|
||||||
|
(_device = device) === null || _device === void 0 || _device._didClose();
|
||||||
|
connection.close(closeError);
|
||||||
|
};
|
||||||
|
pipe.on('closed', onPipeClosed);
|
||||||
|
connection.onmessage = message => pipe.send({
|
||||||
|
message
|
||||||
|
}).catch(onPipeClosed);
|
||||||
|
pipe.on('message', ({
|
||||||
|
message
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
connection.dispatch(message);
|
||||||
|
} catch (e) {
|
||||||
|
closeError = String(e);
|
||||||
|
closePipe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const result = await (0, _timeoutRunner.raceAgainstDeadline)(async () => {
|
||||||
|
const playwright = await connection.initializePlaywright();
|
||||||
|
if (!playwright._initializer.preConnectedAndroidDevice) {
|
||||||
|
closePipe();
|
||||||
|
throw new Error('Malformed endpoint. Did you use Android.launchServer method?');
|
||||||
|
}
|
||||||
|
device = AndroidDevice.from(playwright._initializer.preConnectedAndroidDevice);
|
||||||
|
device._shouldCloseConnectionOnClose = true;
|
||||||
|
device.on(_events.Events.AndroidDevice.Close, closePipe);
|
||||||
|
return device;
|
||||||
|
}, deadline);
|
||||||
|
if (!result.timedOut) {
|
||||||
|
return result.result;
|
||||||
|
} else {
|
||||||
|
closePipe();
|
||||||
|
throw new Error(`Timeout ${options.timeout}ms exceeded`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Android = Android;
|
||||||
|
_Symbol$asyncDispose = Symbol.asyncDispose;
|
||||||
|
class AndroidDevice extends _channelOwner.ChannelOwner {
|
||||||
|
static from(androidDevice) {
|
||||||
|
return androidDevice._object;
|
||||||
|
}
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
|
this._timeoutSettings = void 0;
|
||||||
|
this._webViews = new Map();
|
||||||
|
this._shouldCloseConnectionOnClose = false;
|
||||||
|
this.input = void 0;
|
||||||
|
this.input = new AndroidInput(this);
|
||||||
|
this._timeoutSettings = new _timeoutSettings.TimeoutSettings(parent._timeoutSettings);
|
||||||
|
this._channel.on('webViewAdded', ({
|
||||||
|
webView
|
||||||
|
}) => this._onWebViewAdded(webView));
|
||||||
|
this._channel.on('webViewRemoved', ({
|
||||||
|
socketName
|
||||||
|
}) => this._onWebViewRemoved(socketName));
|
||||||
|
this._channel.on('close', () => this._didClose());
|
||||||
|
}
|
||||||
|
_onWebViewAdded(webView) {
|
||||||
|
const view = new AndroidWebView(this, webView);
|
||||||
|
this._webViews.set(webView.socketName, view);
|
||||||
|
this.emit(_events.Events.AndroidDevice.WebView, view);
|
||||||
|
}
|
||||||
|
_onWebViewRemoved(socketName) {
|
||||||
|
const view = this._webViews.get(socketName);
|
||||||
|
this._webViews.delete(socketName);
|
||||||
|
if (view) view.emit(_events.Events.AndroidWebView.Close);
|
||||||
|
}
|
||||||
|
setDefaultTimeout(timeout) {
|
||||||
|
this._timeoutSettings.setDefaultTimeout(timeout);
|
||||||
|
this._channel.setDefaultTimeoutNoReply({
|
||||||
|
timeout
|
||||||
|
});
|
||||||
|
}
|
||||||
|
serial() {
|
||||||
|
return this._initializer.serial;
|
||||||
|
}
|
||||||
|
model() {
|
||||||
|
return this._initializer.model;
|
||||||
|
}
|
||||||
|
webViews() {
|
||||||
|
return [...this._webViews.values()];
|
||||||
|
}
|
||||||
|
async webView(selector, options) {
|
||||||
|
const predicate = v => {
|
||||||
|
if (selector.pkg) return v.pkg() === selector.pkg;
|
||||||
|
if (selector.socketName) return v._socketName() === selector.socketName;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
const webView = [...this._webViews.values()].find(predicate);
|
||||||
|
if (webView) return webView;
|
||||||
|
return await this.waitForEvent('webview', {
|
||||||
|
...options,
|
||||||
|
predicate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async wait(selector, options) {
|
||||||
|
await this._channel.wait({
|
||||||
|
selector: toSelectorChannel(selector),
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async fill(selector, text, options) {
|
||||||
|
await this._channel.fill({
|
||||||
|
selector: toSelectorChannel(selector),
|
||||||
|
text,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async press(selector, key, options) {
|
||||||
|
await this.tap(selector, options);
|
||||||
|
await this.input.press(key);
|
||||||
|
}
|
||||||
|
async tap(selector, options) {
|
||||||
|
await this._channel.tap({
|
||||||
|
selector: toSelectorChannel(selector),
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async drag(selector, dest, options) {
|
||||||
|
await this._channel.drag({
|
||||||
|
selector: toSelectorChannel(selector),
|
||||||
|
dest,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async fling(selector, direction, options) {
|
||||||
|
await this._channel.fling({
|
||||||
|
selector: toSelectorChannel(selector),
|
||||||
|
direction,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async longTap(selector, options) {
|
||||||
|
await this._channel.longTap({
|
||||||
|
selector: toSelectorChannel(selector),
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async pinchClose(selector, percent, options) {
|
||||||
|
await this._channel.pinchClose({
|
||||||
|
selector: toSelectorChannel(selector),
|
||||||
|
percent,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async pinchOpen(selector, percent, options) {
|
||||||
|
await this._channel.pinchOpen({
|
||||||
|
selector: toSelectorChannel(selector),
|
||||||
|
percent,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async scroll(selector, direction, percent, options) {
|
||||||
|
await this._channel.scroll({
|
||||||
|
selector: toSelectorChannel(selector),
|
||||||
|
direction,
|
||||||
|
percent,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async swipe(selector, direction, percent, options) {
|
||||||
|
await this._channel.swipe({
|
||||||
|
selector: toSelectorChannel(selector),
|
||||||
|
direction,
|
||||||
|
percent,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async info(selector) {
|
||||||
|
return (await this._channel.info({
|
||||||
|
selector: toSelectorChannel(selector)
|
||||||
|
})).info;
|
||||||
|
}
|
||||||
|
async screenshot(options = {}) {
|
||||||
|
const {
|
||||||
|
binary
|
||||||
|
} = await this._channel.screenshot();
|
||||||
|
if (options.path) await _fs.default.promises.writeFile(options.path, binary);
|
||||||
|
return binary;
|
||||||
|
}
|
||||||
|
async [_Symbol$asyncDispose]() {
|
||||||
|
await this.close();
|
||||||
|
}
|
||||||
|
async close() {
|
||||||
|
try {
|
||||||
|
if (this._shouldCloseConnectionOnClose) this._connection.close();else await this._channel.close();
|
||||||
|
} catch (e) {
|
||||||
|
if ((0, _errors.isTargetClosedError)(e)) return;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_didClose() {
|
||||||
|
this.emit(_events.Events.AndroidDevice.Close, this);
|
||||||
|
}
|
||||||
|
async shell(command) {
|
||||||
|
const {
|
||||||
|
result
|
||||||
|
} = await this._channel.shell({
|
||||||
|
command
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
async open(command) {
|
||||||
|
return AndroidSocket.from((await this._channel.open({
|
||||||
|
command
|
||||||
|
})).socket);
|
||||||
|
}
|
||||||
|
async installApk(file, options) {
|
||||||
|
await this._channel.installApk({
|
||||||
|
file: await loadFile(file),
|
||||||
|
args: options && options.args
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async push(file, path, options) {
|
||||||
|
await this._channel.push({
|
||||||
|
file: await loadFile(file),
|
||||||
|
path,
|
||||||
|
mode: options ? options.mode : undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async launchBrowser(options = {}) {
|
||||||
|
const contextOptions = await (0, _browserContext.prepareBrowserContextParams)(options);
|
||||||
|
const result = await this._channel.launchBrowser(contextOptions);
|
||||||
|
const context = _browserContext.BrowserContext.from(result.context);
|
||||||
|
context._setOptions(contextOptions, {});
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
async waitForEvent(event, optionsOrPredicate = {}) {
|
||||||
|
return await this._wrapApiCall(async () => {
|
||||||
|
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
|
||||||
|
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
|
||||||
|
const waiter = _waiter.Waiter.createForEvent(this, event);
|
||||||
|
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
|
||||||
|
if (event !== _events.Events.AndroidDevice.Close) waiter.rejectOnEvent(this, _events.Events.AndroidDevice.Close, () => new _errors.TargetClosedError());
|
||||||
|
const result = await waiter.waitForEvent(this, event, predicate);
|
||||||
|
waiter.dispose();
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.AndroidDevice = AndroidDevice;
|
||||||
|
class AndroidSocket extends _channelOwner.ChannelOwner {
|
||||||
|
static from(androidDevice) {
|
||||||
|
return androidDevice._object;
|
||||||
|
}
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
|
this._channel.on('data', ({
|
||||||
|
data
|
||||||
|
}) => this.emit(_events.Events.AndroidSocket.Data, data));
|
||||||
|
this._channel.on('close', () => this.emit(_events.Events.AndroidSocket.Close));
|
||||||
|
}
|
||||||
|
async write(data) {
|
||||||
|
await this._channel.write({
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async close() {
|
||||||
|
await this._channel.close();
|
||||||
|
}
|
||||||
|
async [Symbol.asyncDispose]() {
|
||||||
|
await this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.AndroidSocket = AndroidSocket;
|
||||||
|
async function loadFile(file) {
|
||||||
|
if ((0, _utils.isString)(file)) return await _fs.default.promises.readFile(file);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
class AndroidInput {
|
||||||
|
constructor(device) {
|
||||||
|
this._device = void 0;
|
||||||
|
this._device = device;
|
||||||
|
}
|
||||||
|
async type(text) {
|
||||||
|
await this._device._channel.inputType({
|
||||||
|
text
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async press(key) {
|
||||||
|
await this._device._channel.inputPress({
|
||||||
|
key
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async tap(point) {
|
||||||
|
await this._device._channel.inputTap({
|
||||||
|
point
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async swipe(from, segments, steps) {
|
||||||
|
await this._device._channel.inputSwipe({
|
||||||
|
segments,
|
||||||
|
steps
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async drag(from, to, steps) {
|
||||||
|
await this._device._channel.inputDrag({
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
steps
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.AndroidInput = AndroidInput;
|
||||||
|
function toSelectorChannel(selector) {
|
||||||
|
const {
|
||||||
|
checkable,
|
||||||
|
checked,
|
||||||
|
clazz,
|
||||||
|
clickable,
|
||||||
|
depth,
|
||||||
|
desc,
|
||||||
|
enabled,
|
||||||
|
focusable,
|
||||||
|
focused,
|
||||||
|
hasChild,
|
||||||
|
hasDescendant,
|
||||||
|
longClickable,
|
||||||
|
pkg,
|
||||||
|
res,
|
||||||
|
scrollable,
|
||||||
|
selected,
|
||||||
|
text
|
||||||
|
} = selector;
|
||||||
|
const toRegex = value => {
|
||||||
|
if (value === undefined) return undefined;
|
||||||
|
if ((0, _utils.isRegExp)(value)) return value.source;
|
||||||
|
return '^' + value.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d') + '$';
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
checkable,
|
||||||
|
checked,
|
||||||
|
clazz: toRegex(clazz),
|
||||||
|
pkg: toRegex(pkg),
|
||||||
|
desc: toRegex(desc),
|
||||||
|
res: toRegex(res),
|
||||||
|
text: toRegex(text),
|
||||||
|
clickable,
|
||||||
|
depth,
|
||||||
|
enabled,
|
||||||
|
focusable,
|
||||||
|
focused,
|
||||||
|
hasChild: hasChild ? {
|
||||||
|
selector: toSelectorChannel(hasChild.selector)
|
||||||
|
} : undefined,
|
||||||
|
hasDescendant: hasDescendant ? {
|
||||||
|
selector: toSelectorChannel(hasDescendant.selector),
|
||||||
|
maxDepth: hasDescendant.maxDepth
|
||||||
|
} : undefined,
|
||||||
|
longClickable,
|
||||||
|
scrollable,
|
||||||
|
selected
|
||||||
|
};
|
||||||
|
}
|
||||||
|
class AndroidWebView extends _events2.EventEmitter {
|
||||||
|
constructor(device, data) {
|
||||||
|
super();
|
||||||
|
this._device = void 0;
|
||||||
|
this._data = void 0;
|
||||||
|
this._pagePromise = void 0;
|
||||||
|
this._device = device;
|
||||||
|
this._data = data;
|
||||||
|
}
|
||||||
|
pid() {
|
||||||
|
return this._data.pid;
|
||||||
|
}
|
||||||
|
pkg() {
|
||||||
|
return this._data.pkg;
|
||||||
|
}
|
||||||
|
_socketName() {
|
||||||
|
return this._data.socketName;
|
||||||
|
}
|
||||||
|
async page() {
|
||||||
|
if (!this._pagePromise) this._pagePromise = this._fetchPage();
|
||||||
|
return await this._pagePromise;
|
||||||
|
}
|
||||||
|
async _fetchPage() {
|
||||||
|
const {
|
||||||
|
context
|
||||||
|
} = await this._device._channel.connectToWebView({
|
||||||
|
socketName: this._data.socketName
|
||||||
|
});
|
||||||
|
return _browserContext.BrowserContext.from(context).pages()[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.AndroidWebView = AndroidWebView;
|
||||||
285
node_modules/playwright-core/lib/client/api.js
generated
vendored
Normal file
285
node_modules/playwright-core/lib/client/api.js
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "APIRequest", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _fetch.APIRequest;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "APIRequestContext", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _fetch.APIRequestContext;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "APIResponse", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _fetch.APIResponse;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Accessibility", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _accessibility.Accessibility;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Android", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _android.Android;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "AndroidDevice", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _android.AndroidDevice;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "AndroidInput", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _android.AndroidInput;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "AndroidSocket", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _android.AndroidSocket;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "AndroidWebView", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _android.AndroidWebView;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Browser", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _browser.Browser;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "BrowserContext", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _browserContext.BrowserContext;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "BrowserType", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _browserType.BrowserType;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "CDPSession", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _cdpSession.CDPSession;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Clock", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _clock.Clock;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "ConsoleMessage", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _consoleMessage.ConsoleMessage;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Coverage", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _coverage.Coverage;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Dialog", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _dialog.Dialog;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Download", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _download.Download;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Electron", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _electron.Electron;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "ElectronApplication", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _electron.ElectronApplication;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "ElementHandle", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _elementHandle.ElementHandle;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "FileChooser", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _fileChooser.FileChooser;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Frame", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _frame.Frame;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "FrameLocator", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _locator.FrameLocator;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "JSHandle", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _jsHandle.JSHandle;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Keyboard", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _input.Keyboard;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Locator", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _locator.Locator;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Mouse", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _input.Mouse;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Page", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _page.Page;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Playwright", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _playwright.Playwright;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Request", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _network.Request;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Response", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _network.Response;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Route", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _network.Route;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Selectors", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _selectors.Selectors;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "TimeoutError", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _errors.TimeoutError;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Touchscreen", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _input.Touchscreen;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Tracing", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _tracing.Tracing;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Video", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _video.Video;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "WebError", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _webError.WebError;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "WebSocket", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _network.WebSocket;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "WebSocketRoute", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _network.WebSocketRoute;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "Worker", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _worker.Worker;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var _accessibility = require("./accessibility");
|
||||||
|
var _android = require("./android");
|
||||||
|
var _browser = require("./browser");
|
||||||
|
var _browserContext = require("./browserContext");
|
||||||
|
var _browserType = require("./browserType");
|
||||||
|
var _clock = require("./clock");
|
||||||
|
var _consoleMessage = require("./consoleMessage");
|
||||||
|
var _coverage = require("./coverage");
|
||||||
|
var _dialog = require("./dialog");
|
||||||
|
var _download = require("./download");
|
||||||
|
var _electron = require("./electron");
|
||||||
|
var _locator = require("./locator");
|
||||||
|
var _elementHandle = require("./elementHandle");
|
||||||
|
var _fileChooser = require("./fileChooser");
|
||||||
|
var _errors = require("./errors");
|
||||||
|
var _frame = require("./frame");
|
||||||
|
var _input = require("./input");
|
||||||
|
var _jsHandle = require("./jsHandle");
|
||||||
|
var _network = require("./network");
|
||||||
|
var _fetch = require("./fetch");
|
||||||
|
var _page = require("./page");
|
||||||
|
var _selectors = require("./selectors");
|
||||||
|
var _tracing = require("./tracing");
|
||||||
|
var _video = require("./video");
|
||||||
|
var _worker = require("./worker");
|
||||||
|
var _cdpSession = require("./cdpSession");
|
||||||
|
var _playwright = require("./playwright");
|
||||||
|
var _webError = require("./webError");
|
||||||
79
node_modules/playwright-core/lib/client/artifact.js
generated
vendored
Normal file
79
node_modules/playwright-core/lib/client/artifact.js
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.Artifact = void 0;
|
||||||
|
var fs = _interopRequireWildcard(require("fs"));
|
||||||
|
var _stream = require("./stream");
|
||||||
|
var _fileUtils = require("../utils/fileUtils");
|
||||||
|
var _channelOwner = require("./channelOwner");
|
||||||
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||||
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Artifact extends _channelOwner.ChannelOwner {
|
||||||
|
static from(channel) {
|
||||||
|
return channel._object;
|
||||||
|
}
|
||||||
|
async pathAfterFinished() {
|
||||||
|
if (this._connection.isRemote()) throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`);
|
||||||
|
return (await this._channel.pathAfterFinished()).value;
|
||||||
|
}
|
||||||
|
async saveAs(path) {
|
||||||
|
if (!this._connection.isRemote()) {
|
||||||
|
await this._channel.saveAs({
|
||||||
|
path
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = await this._channel.saveAsStream();
|
||||||
|
const stream = _stream.Stream.from(result.stream);
|
||||||
|
await (0, _fileUtils.mkdirIfNeeded)(path);
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
stream.stream().pipe(fs.createWriteStream(path)).on('finish', resolve).on('error', reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async failure() {
|
||||||
|
return (await this._channel.failure()).error || null;
|
||||||
|
}
|
||||||
|
async createReadStream() {
|
||||||
|
const result = await this._channel.stream();
|
||||||
|
const stream = _stream.Stream.from(result.stream);
|
||||||
|
return stream.stream();
|
||||||
|
}
|
||||||
|
async readIntoBuffer() {
|
||||||
|
const stream = await this.createReadStream();
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
const chunks = [];
|
||||||
|
stream.on('data', chunk => {
|
||||||
|
chunks.push(chunk);
|
||||||
|
});
|
||||||
|
stream.on('end', () => {
|
||||||
|
resolve(Buffer.concat(chunks));
|
||||||
|
});
|
||||||
|
stream.on('error', reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async cancel() {
|
||||||
|
return await this._channel.cancel();
|
||||||
|
}
|
||||||
|
async delete() {
|
||||||
|
return await this._channel.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Artifact = Artifact;
|
||||||
145
node_modules/playwright-core/lib/client/browser.js
generated
vendored
Normal file
145
node_modules/playwright-core/lib/client/browser.js
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.Browser = void 0;
|
||||||
|
var _fs = _interopRequireDefault(require("fs"));
|
||||||
|
var _browserContext = require("./browserContext");
|
||||||
|
var _channelOwner = require("./channelOwner");
|
||||||
|
var _events = require("./events");
|
||||||
|
var _errors = require("./errors");
|
||||||
|
var _cdpSession = require("./cdpSession");
|
||||||
|
var _artifact = require("./artifact");
|
||||||
|
var _utils = require("../utils");
|
||||||
|
let _Symbol$asyncDispose;
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
_Symbol$asyncDispose = Symbol.asyncDispose;
|
||||||
|
class Browser extends _channelOwner.ChannelOwner {
|
||||||
|
static from(browser) {
|
||||||
|
return browser._object;
|
||||||
|
}
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
|
this._contexts = new Set();
|
||||||
|
this._isConnected = true;
|
||||||
|
this._closedPromise = void 0;
|
||||||
|
this._shouldCloseConnectionOnClose = false;
|
||||||
|
this._browserType = void 0;
|
||||||
|
this._options = {};
|
||||||
|
this._name = void 0;
|
||||||
|
this._path = void 0;
|
||||||
|
// Used from @playwright/test fixtures.
|
||||||
|
this._connectHeaders = void 0;
|
||||||
|
this._closeReason = void 0;
|
||||||
|
this._name = initializer.name;
|
||||||
|
this._channel.on('close', () => this._didClose());
|
||||||
|
this._closedPromise = new Promise(f => this.once(_events.Events.Browser.Disconnected, f));
|
||||||
|
}
|
||||||
|
browserType() {
|
||||||
|
return this._browserType;
|
||||||
|
}
|
||||||
|
async newContext(options = {}) {
|
||||||
|
return await this._innerNewContext(options, false);
|
||||||
|
}
|
||||||
|
async _newContextForReuse(options = {}) {
|
||||||
|
return await this._wrapApiCall(async () => {
|
||||||
|
for (const context of this._contexts) {
|
||||||
|
await this._browserType._willCloseContext(context);
|
||||||
|
for (const page of context.pages()) page._onClose();
|
||||||
|
context._onClose();
|
||||||
|
}
|
||||||
|
return await this._innerNewContext(options, true);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
async _stopPendingOperations(reason) {
|
||||||
|
return await this._wrapApiCall(async () => {
|
||||||
|
await this._channel.stopPendingOperations({
|
||||||
|
reason
|
||||||
|
});
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
async _innerNewContext(options = {}, forReuse) {
|
||||||
|
options = {
|
||||||
|
...this._browserType._defaultContextOptions,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
const contextOptions = await (0, _browserContext.prepareBrowserContextParams)(options);
|
||||||
|
const response = forReuse ? await this._channel.newContextForReuse(contextOptions) : await this._channel.newContext(contextOptions);
|
||||||
|
const context = _browserContext.BrowserContext.from(response.context);
|
||||||
|
await this._browserType._didCreateContext(context, contextOptions, this._options, options.logger || this._logger);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
contexts() {
|
||||||
|
return [...this._contexts];
|
||||||
|
}
|
||||||
|
version() {
|
||||||
|
return this._initializer.version;
|
||||||
|
}
|
||||||
|
async newPage(options = {}) {
|
||||||
|
return await this._wrapApiCall(async () => {
|
||||||
|
const context = await this.newContext(options);
|
||||||
|
const page = await context.newPage();
|
||||||
|
page._ownedContext = context;
|
||||||
|
context._ownerPage = page;
|
||||||
|
return page;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
isConnected() {
|
||||||
|
return this._isConnected;
|
||||||
|
}
|
||||||
|
async newBrowserCDPSession() {
|
||||||
|
return _cdpSession.CDPSession.from((await this._channel.newBrowserCDPSession()).session);
|
||||||
|
}
|
||||||
|
async startTracing(page, options = {}) {
|
||||||
|
this._path = options.path;
|
||||||
|
await this._channel.startTracing({
|
||||||
|
...options,
|
||||||
|
page: page ? page._channel : undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async stopTracing() {
|
||||||
|
const artifact = _artifact.Artifact.from((await this._channel.stopTracing()).artifact);
|
||||||
|
const buffer = await artifact.readIntoBuffer();
|
||||||
|
await artifact.delete();
|
||||||
|
if (this._path) {
|
||||||
|
await (0, _utils.mkdirIfNeeded)(this._path);
|
||||||
|
await _fs.default.promises.writeFile(this._path, buffer);
|
||||||
|
this._path = undefined;
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
async [_Symbol$asyncDispose]() {
|
||||||
|
await this.close();
|
||||||
|
}
|
||||||
|
async close(options = {}) {
|
||||||
|
this._closeReason = options.reason;
|
||||||
|
try {
|
||||||
|
if (this._shouldCloseConnectionOnClose) this._connection.close();else await this._channel.close(options);
|
||||||
|
await this._closedPromise;
|
||||||
|
} catch (e) {
|
||||||
|
if ((0, _errors.isTargetClosedError)(e)) return;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_didClose() {
|
||||||
|
this._isConnected = false;
|
||||||
|
this.emit(_events.Events.Browser.Disconnected, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Browser = Browser;
|
||||||
559
node_modules/playwright-core/lib/client/browserContext.js
generated
vendored
Normal file
559
node_modules/playwright-core/lib/client/browserContext.js
generated
vendored
Normal file
@@ -0,0 +1,559 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.BrowserContext = void 0;
|
||||||
|
exports.prepareBrowserContextParams = prepareBrowserContextParams;
|
||||||
|
exports.toClientCertificatesProtocol = toClientCertificatesProtocol;
|
||||||
|
var _page = require("./page");
|
||||||
|
var _frame = require("./frame");
|
||||||
|
var network = _interopRequireWildcard(require("./network"));
|
||||||
|
var _fs = _interopRequireDefault(require("fs"));
|
||||||
|
var _path = _interopRequireDefault(require("path"));
|
||||||
|
var _channelOwner = require("./channelOwner");
|
||||||
|
var _clientHelper = require("./clientHelper");
|
||||||
|
var _browser = require("./browser");
|
||||||
|
var _worker = require("./worker");
|
||||||
|
var _events = require("./events");
|
||||||
|
var _timeoutSettings = require("../common/timeoutSettings");
|
||||||
|
var _waiter = require("./waiter");
|
||||||
|
var _utils = require("../utils");
|
||||||
|
var _cdpSession = require("./cdpSession");
|
||||||
|
var _tracing = require("./tracing");
|
||||||
|
var _artifact = require("./artifact");
|
||||||
|
var _fetch = require("./fetch");
|
||||||
|
var _stackTrace = require("../utils/stackTrace");
|
||||||
|
var _harRouter = require("./harRouter");
|
||||||
|
var _consoleMessage = require("./consoleMessage");
|
||||||
|
var _dialog = require("./dialog");
|
||||||
|
var _webError = require("./webError");
|
||||||
|
var _errors = require("./errors");
|
||||||
|
var _clock = require("./clock");
|
||||||
|
let _Symbol$asyncDispose;
|
||||||
|
/**
|
||||||
|
* Copyright 2017 Google Inc. All rights reserved.
|
||||||
|
* Modifications copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||||
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||||
|
_Symbol$asyncDispose = Symbol.asyncDispose;
|
||||||
|
class BrowserContext extends _channelOwner.ChannelOwner {
|
||||||
|
static from(context) {
|
||||||
|
return context._object;
|
||||||
|
}
|
||||||
|
static fromNullable(context) {
|
||||||
|
return context ? BrowserContext.from(context) : null;
|
||||||
|
}
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
var _this$_browser, _this$_browser2;
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
|
this._pages = new Set();
|
||||||
|
this._routes = [];
|
||||||
|
this._webSocketRoutes = [];
|
||||||
|
this._browser = null;
|
||||||
|
this._browserType = void 0;
|
||||||
|
this._bindings = new Map();
|
||||||
|
this._timeoutSettings = new _timeoutSettings.TimeoutSettings();
|
||||||
|
this._ownerPage = void 0;
|
||||||
|
this._closedPromise = void 0;
|
||||||
|
this._options = {};
|
||||||
|
this.request = void 0;
|
||||||
|
this.tracing = void 0;
|
||||||
|
this.clock = void 0;
|
||||||
|
this._backgroundPages = new Set();
|
||||||
|
this._serviceWorkers = new Set();
|
||||||
|
this._isChromium = void 0;
|
||||||
|
this._harRecorders = new Map();
|
||||||
|
this._closeWasCalled = false;
|
||||||
|
this._closeReason = void 0;
|
||||||
|
this._harRouters = [];
|
||||||
|
if (parent instanceof _browser.Browser) this._browser = parent;
|
||||||
|
(_this$_browser = this._browser) === null || _this$_browser === void 0 || _this$_browser._contexts.add(this);
|
||||||
|
this._isChromium = ((_this$_browser2 = this._browser) === null || _this$_browser2 === void 0 ? void 0 : _this$_browser2._name) === 'chromium';
|
||||||
|
this.tracing = _tracing.Tracing.from(initializer.tracing);
|
||||||
|
this.request = _fetch.APIRequestContext.from(initializer.requestContext);
|
||||||
|
this.clock = new _clock.Clock(this);
|
||||||
|
this._channel.on('bindingCall', ({
|
||||||
|
binding
|
||||||
|
}) => this._onBinding(_page.BindingCall.from(binding)));
|
||||||
|
this._channel.on('close', () => this._onClose());
|
||||||
|
this._channel.on('page', ({
|
||||||
|
page
|
||||||
|
}) => this._onPage(_page.Page.from(page)));
|
||||||
|
this._channel.on('route', ({
|
||||||
|
route
|
||||||
|
}) => this._onRoute(network.Route.from(route)));
|
||||||
|
this._channel.on('webSocketRoute', ({
|
||||||
|
webSocketRoute
|
||||||
|
}) => this._onWebSocketRoute(network.WebSocketRoute.from(webSocketRoute)));
|
||||||
|
this._channel.on('backgroundPage', ({
|
||||||
|
page
|
||||||
|
}) => {
|
||||||
|
const backgroundPage = _page.Page.from(page);
|
||||||
|
this._backgroundPages.add(backgroundPage);
|
||||||
|
this.emit(_events.Events.BrowserContext.BackgroundPage, backgroundPage);
|
||||||
|
});
|
||||||
|
this._channel.on('serviceWorker', ({
|
||||||
|
worker
|
||||||
|
}) => {
|
||||||
|
const serviceWorker = _worker.Worker.from(worker);
|
||||||
|
serviceWorker._context = this;
|
||||||
|
this._serviceWorkers.add(serviceWorker);
|
||||||
|
this.emit(_events.Events.BrowserContext.ServiceWorker, serviceWorker);
|
||||||
|
});
|
||||||
|
this._channel.on('console', event => {
|
||||||
|
const consoleMessage = new _consoleMessage.ConsoleMessage(event);
|
||||||
|
this.emit(_events.Events.BrowserContext.Console, consoleMessage);
|
||||||
|
const page = consoleMessage.page();
|
||||||
|
if (page) page.emit(_events.Events.Page.Console, consoleMessage);
|
||||||
|
});
|
||||||
|
this._channel.on('pageError', ({
|
||||||
|
error,
|
||||||
|
page
|
||||||
|
}) => {
|
||||||
|
const pageObject = _page.Page.from(page);
|
||||||
|
const parsedError = (0, _errors.parseError)(error);
|
||||||
|
this.emit(_events.Events.BrowserContext.WebError, new _webError.WebError(pageObject, parsedError));
|
||||||
|
if (pageObject) pageObject.emit(_events.Events.Page.PageError, parsedError);
|
||||||
|
});
|
||||||
|
this._channel.on('dialog', ({
|
||||||
|
dialog
|
||||||
|
}) => {
|
||||||
|
const dialogObject = _dialog.Dialog.from(dialog);
|
||||||
|
let hasListeners = this.emit(_events.Events.BrowserContext.Dialog, dialogObject);
|
||||||
|
const page = dialogObject.page();
|
||||||
|
if (page) hasListeners = page.emit(_events.Events.Page.Dialog, dialogObject) || hasListeners;
|
||||||
|
if (!hasListeners) {
|
||||||
|
// Although we do similar handling on the server side, we still need this logic
|
||||||
|
// on the client side due to a possible race condition between two async calls:
|
||||||
|
// a) removing "dialog" listener subscription (client->server)
|
||||||
|
// b) actual "dialog" event (server->client)
|
||||||
|
if (dialogObject.type() === 'beforeunload') dialog.accept({}).catch(() => {});else dialog.dismiss().catch(() => {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._channel.on('request', ({
|
||||||
|
request,
|
||||||
|
page
|
||||||
|
}) => this._onRequest(network.Request.from(request), _page.Page.fromNullable(page)));
|
||||||
|
this._channel.on('requestFailed', ({
|
||||||
|
request,
|
||||||
|
failureText,
|
||||||
|
responseEndTiming,
|
||||||
|
page
|
||||||
|
}) => this._onRequestFailed(network.Request.from(request), responseEndTiming, failureText, _page.Page.fromNullable(page)));
|
||||||
|
this._channel.on('requestFinished', params => this._onRequestFinished(params));
|
||||||
|
this._channel.on('response', ({
|
||||||
|
response,
|
||||||
|
page
|
||||||
|
}) => this._onResponse(network.Response.from(response), _page.Page.fromNullable(page)));
|
||||||
|
this._closedPromise = new Promise(f => this.once(_events.Events.BrowserContext.Close, f));
|
||||||
|
this._setEventToSubscriptionMapping(new Map([[_events.Events.BrowserContext.Console, 'console'], [_events.Events.BrowserContext.Dialog, 'dialog'], [_events.Events.BrowserContext.Request, 'request'], [_events.Events.BrowserContext.Response, 'response'], [_events.Events.BrowserContext.RequestFinished, 'requestFinished'], [_events.Events.BrowserContext.RequestFailed, 'requestFailed']]));
|
||||||
|
}
|
||||||
|
_setOptions(contextOptions, browserOptions) {
|
||||||
|
this._options = contextOptions;
|
||||||
|
if (this._options.recordHar) this._harRecorders.set('', {
|
||||||
|
path: this._options.recordHar.path,
|
||||||
|
content: this._options.recordHar.content
|
||||||
|
});
|
||||||
|
this.tracing._tracesDir = browserOptions.tracesDir;
|
||||||
|
}
|
||||||
|
_onPage(page) {
|
||||||
|
this._pages.add(page);
|
||||||
|
this.emit(_events.Events.BrowserContext.Page, page);
|
||||||
|
if (page._opener && !page._opener.isClosed()) page._opener.emit(_events.Events.Page.Popup, page);
|
||||||
|
}
|
||||||
|
_onRequest(request, page) {
|
||||||
|
this.emit(_events.Events.BrowserContext.Request, request);
|
||||||
|
if (page) page.emit(_events.Events.Page.Request, request);
|
||||||
|
}
|
||||||
|
_onResponse(response, page) {
|
||||||
|
this.emit(_events.Events.BrowserContext.Response, response);
|
||||||
|
if (page) page.emit(_events.Events.Page.Response, response);
|
||||||
|
}
|
||||||
|
_onRequestFailed(request, responseEndTiming, failureText, page) {
|
||||||
|
request._failureText = failureText || null;
|
||||||
|
request._setResponseEndTiming(responseEndTiming);
|
||||||
|
this.emit(_events.Events.BrowserContext.RequestFailed, request);
|
||||||
|
if (page) page.emit(_events.Events.Page.RequestFailed, request);
|
||||||
|
}
|
||||||
|
_onRequestFinished(params) {
|
||||||
|
const {
|
||||||
|
responseEndTiming
|
||||||
|
} = params;
|
||||||
|
const request = network.Request.from(params.request);
|
||||||
|
const response = network.Response.fromNullable(params.response);
|
||||||
|
const page = _page.Page.fromNullable(params.page);
|
||||||
|
request._setResponseEndTiming(responseEndTiming);
|
||||||
|
this.emit(_events.Events.BrowserContext.RequestFinished, request);
|
||||||
|
if (page) page.emit(_events.Events.Page.RequestFinished, request);
|
||||||
|
if (response) response._finishedPromise.resolve(null);
|
||||||
|
}
|
||||||
|
async _onRoute(route) {
|
||||||
|
route._context = this;
|
||||||
|
const page = route.request()._safePage();
|
||||||
|
const routeHandlers = this._routes.slice();
|
||||||
|
for (const routeHandler of routeHandlers) {
|
||||||
|
// If the page or the context was closed we stall all requests right away.
|
||||||
|
if (page !== null && page !== void 0 && page._closeWasCalled || this._closeWasCalled) return;
|
||||||
|
if (!routeHandler.matches(route.request().url())) continue;
|
||||||
|
const index = this._routes.indexOf(routeHandler);
|
||||||
|
if (index === -1) continue;
|
||||||
|
if (routeHandler.willExpire()) this._routes.splice(index, 1);
|
||||||
|
const handled = await routeHandler.handle(route);
|
||||||
|
if (!this._routes.length) this._wrapApiCall(() => this._updateInterceptionPatterns(), true).catch(() => {});
|
||||||
|
if (handled) return;
|
||||||
|
}
|
||||||
|
// If the page is closed or unrouteAll() was called without waiting and interception disabled,
|
||||||
|
// the method will throw an error - silence it.
|
||||||
|
await route._innerContinue(true /* isFallback */).catch(() => {});
|
||||||
|
}
|
||||||
|
async _onWebSocketRoute(webSocketRoute) {
|
||||||
|
const routeHandler = this._webSocketRoutes.find(route => route.matches(webSocketRoute.url()));
|
||||||
|
if (routeHandler) await routeHandler.handle(webSocketRoute);else webSocketRoute.connectToServer();
|
||||||
|
}
|
||||||
|
async _onBinding(bindingCall) {
|
||||||
|
const func = this._bindings.get(bindingCall._initializer.name);
|
||||||
|
if (!func) return;
|
||||||
|
await bindingCall.call(func);
|
||||||
|
}
|
||||||
|
setDefaultNavigationTimeout(timeout) {
|
||||||
|
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||||
|
this._wrapApiCall(async () => {
|
||||||
|
this._channel.setDefaultNavigationTimeoutNoReply({
|
||||||
|
timeout
|
||||||
|
}).catch(() => {});
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
setDefaultTimeout(timeout) {
|
||||||
|
this._timeoutSettings.setDefaultTimeout(timeout);
|
||||||
|
this._wrapApiCall(async () => {
|
||||||
|
this._channel.setDefaultTimeoutNoReply({
|
||||||
|
timeout
|
||||||
|
}).catch(() => {});
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
browser() {
|
||||||
|
return this._browser;
|
||||||
|
}
|
||||||
|
pages() {
|
||||||
|
return [...this._pages];
|
||||||
|
}
|
||||||
|
async newPage() {
|
||||||
|
if (this._ownerPage) throw new Error('Please use browser.newContext()');
|
||||||
|
return _page.Page.from((await this._channel.newPage()).page);
|
||||||
|
}
|
||||||
|
async cookies(urls) {
|
||||||
|
if (!urls) urls = [];
|
||||||
|
if (urls && typeof urls === 'string') urls = [urls];
|
||||||
|
return (await this._channel.cookies({
|
||||||
|
urls: urls
|
||||||
|
})).cookies;
|
||||||
|
}
|
||||||
|
async addCookies(cookies) {
|
||||||
|
await this._channel.addCookies({
|
||||||
|
cookies
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async clearCookies(options = {}) {
|
||||||
|
await this._channel.clearCookies({
|
||||||
|
name: (0, _utils.isString)(options.name) ? options.name : undefined,
|
||||||
|
nameRegexSource: (0, _utils.isRegExp)(options.name) ? options.name.source : undefined,
|
||||||
|
nameRegexFlags: (0, _utils.isRegExp)(options.name) ? options.name.flags : undefined,
|
||||||
|
domain: (0, _utils.isString)(options.domain) ? options.domain : undefined,
|
||||||
|
domainRegexSource: (0, _utils.isRegExp)(options.domain) ? options.domain.source : undefined,
|
||||||
|
domainRegexFlags: (0, _utils.isRegExp)(options.domain) ? options.domain.flags : undefined,
|
||||||
|
path: (0, _utils.isString)(options.path) ? options.path : undefined,
|
||||||
|
pathRegexSource: (0, _utils.isRegExp)(options.path) ? options.path.source : undefined,
|
||||||
|
pathRegexFlags: (0, _utils.isRegExp)(options.path) ? options.path.flags : undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async grantPermissions(permissions, options) {
|
||||||
|
await this._channel.grantPermissions({
|
||||||
|
permissions,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async clearPermissions() {
|
||||||
|
await this._channel.clearPermissions();
|
||||||
|
}
|
||||||
|
async setGeolocation(geolocation) {
|
||||||
|
await this._channel.setGeolocation({
|
||||||
|
geolocation: geolocation || undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async setExtraHTTPHeaders(headers) {
|
||||||
|
network.validateHeaders(headers);
|
||||||
|
await this._channel.setExtraHTTPHeaders({
|
||||||
|
headers: (0, _utils.headersObjectToArray)(headers)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async setOffline(offline) {
|
||||||
|
await this._channel.setOffline({
|
||||||
|
offline
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async setHTTPCredentials(httpCredentials) {
|
||||||
|
await this._channel.setHTTPCredentials({
|
||||||
|
httpCredentials: httpCredentials || undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async addInitScript(script, arg) {
|
||||||
|
const source = await (0, _clientHelper.evaluationScript)(script, arg);
|
||||||
|
await this._channel.addInitScript({
|
||||||
|
source
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async exposeBinding(name, callback, options = {}) {
|
||||||
|
await this._channel.exposeBinding({
|
||||||
|
name,
|
||||||
|
needsHandle: options.handle
|
||||||
|
});
|
||||||
|
this._bindings.set(name, callback);
|
||||||
|
}
|
||||||
|
async exposeFunction(name, callback) {
|
||||||
|
await this._channel.exposeBinding({
|
||||||
|
name
|
||||||
|
});
|
||||||
|
const binding = (source, ...args) => callback(...args);
|
||||||
|
this._bindings.set(name, binding);
|
||||||
|
}
|
||||||
|
async route(url, handler, options = {}) {
|
||||||
|
this._routes.unshift(new network.RouteHandler(this._options.baseURL, url, handler, options.times));
|
||||||
|
await this._updateInterceptionPatterns();
|
||||||
|
}
|
||||||
|
async routeWebSocket(url, handler) {
|
||||||
|
this._webSocketRoutes.unshift(new network.WebSocketRouteHandler(this._options.baseURL, url, handler));
|
||||||
|
await this._updateWebSocketInterceptionPatterns();
|
||||||
|
}
|
||||||
|
async _recordIntoHAR(har, page, options = {}) {
|
||||||
|
var _options$updateConten, _options$updateMode, _options$updateConten2;
|
||||||
|
const {
|
||||||
|
harId
|
||||||
|
} = await this._channel.harStart({
|
||||||
|
page: page === null || page === void 0 ? void 0 : page._channel,
|
||||||
|
options: prepareRecordHarOptions({
|
||||||
|
path: har,
|
||||||
|
content: (_options$updateConten = options.updateContent) !== null && _options$updateConten !== void 0 ? _options$updateConten : 'attach',
|
||||||
|
mode: (_options$updateMode = options.updateMode) !== null && _options$updateMode !== void 0 ? _options$updateMode : 'minimal',
|
||||||
|
urlFilter: options.url
|
||||||
|
})
|
||||||
|
});
|
||||||
|
this._harRecorders.set(harId, {
|
||||||
|
path: har,
|
||||||
|
content: (_options$updateConten2 = options.updateContent) !== null && _options$updateConten2 !== void 0 ? _options$updateConten2 : 'attach'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async routeFromHAR(har, options = {}) {
|
||||||
|
if (options.update) {
|
||||||
|
await this._recordIntoHAR(har, null, options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const harRouter = await _harRouter.HarRouter.create(this._connection.localUtils(), har, options.notFound || 'abort', {
|
||||||
|
urlMatch: options.url
|
||||||
|
});
|
||||||
|
this._harRouters.push(harRouter);
|
||||||
|
await harRouter.addContextRoute(this);
|
||||||
|
}
|
||||||
|
_disposeHarRouters() {
|
||||||
|
this._harRouters.forEach(router => router.dispose());
|
||||||
|
this._harRouters = [];
|
||||||
|
}
|
||||||
|
async unrouteAll(options) {
|
||||||
|
await this._unrouteInternal(this._routes, [], options === null || options === void 0 ? void 0 : options.behavior);
|
||||||
|
this._disposeHarRouters();
|
||||||
|
}
|
||||||
|
async unroute(url, handler) {
|
||||||
|
const removed = [];
|
||||||
|
const remaining = [];
|
||||||
|
for (const route of this._routes) {
|
||||||
|
if ((0, _utils.urlMatchesEqual)(route.url, url) && (!handler || route.handler === handler)) removed.push(route);else remaining.push(route);
|
||||||
|
}
|
||||||
|
await this._unrouteInternal(removed, remaining, 'default');
|
||||||
|
}
|
||||||
|
async _unrouteInternal(removed, remaining, behavior) {
|
||||||
|
this._routes = remaining;
|
||||||
|
await this._updateInterceptionPatterns();
|
||||||
|
if (!behavior || behavior === 'default') return;
|
||||||
|
const promises = removed.map(routeHandler => routeHandler.stop(behavior));
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
async _updateInterceptionPatterns() {
|
||||||
|
const patterns = network.RouteHandler.prepareInterceptionPatterns(this._routes);
|
||||||
|
await this._channel.setNetworkInterceptionPatterns({
|
||||||
|
patterns
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async _updateWebSocketInterceptionPatterns() {
|
||||||
|
const patterns = network.WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes);
|
||||||
|
await this._channel.setWebSocketInterceptionPatterns({
|
||||||
|
patterns
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_effectiveCloseReason() {
|
||||||
|
var _this$_browser3;
|
||||||
|
return this._closeReason || ((_this$_browser3 = this._browser) === null || _this$_browser3 === void 0 ? void 0 : _this$_browser3._closeReason);
|
||||||
|
}
|
||||||
|
async waitForEvent(event, optionsOrPredicate = {}) {
|
||||||
|
return await this._wrapApiCall(async () => {
|
||||||
|
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
|
||||||
|
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
|
||||||
|
const waiter = _waiter.Waiter.createForEvent(this, event);
|
||||||
|
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
|
||||||
|
if (event !== _events.Events.BrowserContext.Close) waiter.rejectOnEvent(this, _events.Events.BrowserContext.Close, () => new _errors.TargetClosedError(this._effectiveCloseReason()));
|
||||||
|
const result = await waiter.waitForEvent(this, event, predicate);
|
||||||
|
waiter.dispose();
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async storageState(options = {}) {
|
||||||
|
const state = await this._channel.storageState();
|
||||||
|
if (options.path) {
|
||||||
|
await (0, _utils.mkdirIfNeeded)(options.path);
|
||||||
|
await _fs.default.promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8');
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
backgroundPages() {
|
||||||
|
return [...this._backgroundPages];
|
||||||
|
}
|
||||||
|
serviceWorkers() {
|
||||||
|
return [...this._serviceWorkers];
|
||||||
|
}
|
||||||
|
async newCDPSession(page) {
|
||||||
|
// channelOwner.ts's validation messages don't handle the pseudo-union type, so we're explicit here
|
||||||
|
if (!(page instanceof _page.Page) && !(page instanceof _frame.Frame)) throw new Error('page: expected Page or Frame');
|
||||||
|
const result = await this._channel.newCDPSession(page instanceof _page.Page ? {
|
||||||
|
page: page._channel
|
||||||
|
} : {
|
||||||
|
frame: page._channel
|
||||||
|
});
|
||||||
|
return _cdpSession.CDPSession.from(result.session);
|
||||||
|
}
|
||||||
|
_onClose() {
|
||||||
|
var _this$_browserType;
|
||||||
|
if (this._browser) this._browser._contexts.delete(this);
|
||||||
|
(_this$_browserType = this._browserType) === null || _this$_browserType === void 0 || (_this$_browserType = _this$_browserType._contexts) === null || _this$_browserType === void 0 || _this$_browserType.delete(this);
|
||||||
|
this._disposeHarRouters();
|
||||||
|
this.tracing._resetStackCounter();
|
||||||
|
this.emit(_events.Events.BrowserContext.Close, this);
|
||||||
|
}
|
||||||
|
async [_Symbol$asyncDispose]() {
|
||||||
|
await this.close();
|
||||||
|
}
|
||||||
|
async close(options = {}) {
|
||||||
|
if (this._closeWasCalled) return;
|
||||||
|
this._closeReason = options.reason;
|
||||||
|
this._closeWasCalled = true;
|
||||||
|
await this._wrapApiCall(async () => {
|
||||||
|
await this.request.dispose(options);
|
||||||
|
}, true);
|
||||||
|
await this._wrapApiCall(async () => {
|
||||||
|
var _this$_browserType2;
|
||||||
|
await ((_this$_browserType2 = this._browserType) === null || _this$_browserType2 === void 0 ? void 0 : _this$_browserType2._willCloseContext(this));
|
||||||
|
for (const [harId, harParams] of this._harRecorders) {
|
||||||
|
const har = await this._channel.harExport({
|
||||||
|
harId
|
||||||
|
});
|
||||||
|
const artifact = _artifact.Artifact.from(har.artifact);
|
||||||
|
// Server side will compress artifact if content is attach or if file is .zip.
|
||||||
|
const isCompressed = harParams.content === 'attach' || harParams.path.endsWith('.zip');
|
||||||
|
const needCompressed = harParams.path.endsWith('.zip');
|
||||||
|
if (isCompressed && !needCompressed) {
|
||||||
|
await artifact.saveAs(harParams.path + '.tmp');
|
||||||
|
await this._connection.localUtils()._channel.harUnzip({
|
||||||
|
zipFile: harParams.path + '.tmp',
|
||||||
|
harFile: harParams.path
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await artifact.saveAs(harParams.path);
|
||||||
|
}
|
||||||
|
await artifact.delete();
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
await this._channel.close(options);
|
||||||
|
await this._closedPromise;
|
||||||
|
}
|
||||||
|
async _enableRecorder(params) {
|
||||||
|
await this._channel.enableRecorder(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.BrowserContext = BrowserContext;
|
||||||
|
async function prepareStorageState(options) {
|
||||||
|
if (typeof options.storageState !== 'string') return options.storageState;
|
||||||
|
try {
|
||||||
|
return JSON.parse(await _fs.default.promises.readFile(options.storageState, 'utf8'));
|
||||||
|
} catch (e) {
|
||||||
|
(0, _stackTrace.rewriteErrorMessage)(e, `Error reading storage state from ${options.storageState}:\n` + e.message);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function prepareRecordHarOptions(options) {
|
||||||
|
if (!options) return;
|
||||||
|
return {
|
||||||
|
path: options.path,
|
||||||
|
content: options.content || (options.omitContent ? 'omit' : undefined),
|
||||||
|
urlGlob: (0, _utils.isString)(options.urlFilter) ? options.urlFilter : undefined,
|
||||||
|
urlRegexSource: (0, _utils.isRegExp)(options.urlFilter) ? options.urlFilter.source : undefined,
|
||||||
|
urlRegexFlags: (0, _utils.isRegExp)(options.urlFilter) ? options.urlFilter.flags : undefined,
|
||||||
|
mode: options.mode
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async function prepareBrowserContextParams(options) {
|
||||||
|
if (options.videoSize && !options.videosPath) throw new Error(`"videoSize" option requires "videosPath" to be specified`);
|
||||||
|
if (options.extraHTTPHeaders) network.validateHeaders(options.extraHTTPHeaders);
|
||||||
|
const contextParams = {
|
||||||
|
...options,
|
||||||
|
viewport: options.viewport === null ? undefined : options.viewport,
|
||||||
|
noDefaultViewport: options.viewport === null,
|
||||||
|
extraHTTPHeaders: options.extraHTTPHeaders ? (0, _utils.headersObjectToArray)(options.extraHTTPHeaders) : undefined,
|
||||||
|
storageState: await prepareStorageState(options),
|
||||||
|
serviceWorkers: options.serviceWorkers,
|
||||||
|
recordHar: prepareRecordHarOptions(options.recordHar),
|
||||||
|
colorScheme: options.colorScheme === null ? 'no-override' : options.colorScheme,
|
||||||
|
reducedMotion: options.reducedMotion === null ? 'no-override' : options.reducedMotion,
|
||||||
|
forcedColors: options.forcedColors === null ? 'no-override' : options.forcedColors,
|
||||||
|
acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads),
|
||||||
|
clientCertificates: await toClientCertificatesProtocol(options.clientCertificates)
|
||||||
|
};
|
||||||
|
if (!contextParams.recordVideo && options.videosPath) {
|
||||||
|
contextParams.recordVideo = {
|
||||||
|
dir: options.videosPath,
|
||||||
|
size: options.videoSize
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (contextParams.recordVideo && contextParams.recordVideo.dir) contextParams.recordVideo.dir = _path.default.resolve(process.cwd(), contextParams.recordVideo.dir);
|
||||||
|
return contextParams;
|
||||||
|
}
|
||||||
|
function toAcceptDownloadsProtocol(acceptDownloads) {
|
||||||
|
if (acceptDownloads === undefined) return undefined;
|
||||||
|
if (acceptDownloads) return 'accept';
|
||||||
|
return 'deny';
|
||||||
|
}
|
||||||
|
async function toClientCertificatesProtocol(certs) {
|
||||||
|
if (!certs) return undefined;
|
||||||
|
const bufferizeContent = async (value, path) => {
|
||||||
|
if (value) return value;
|
||||||
|
if (path) return await _fs.default.promises.readFile(path);
|
||||||
|
};
|
||||||
|
return await Promise.all(certs.map(async cert => ({
|
||||||
|
origin: cert.origin,
|
||||||
|
cert: await bufferizeContent(cert.cert, cert.certPath),
|
||||||
|
key: await bufferizeContent(cert.key, cert.keyPath),
|
||||||
|
pfx: await bufferizeContent(cert.pfx, cert.pfxPath),
|
||||||
|
passphrase: cert.passphrase
|
||||||
|
})));
|
||||||
|
}
|
||||||
241
node_modules/playwright-core/lib/client/browserType.js
generated
vendored
Normal file
241
node_modules/playwright-core/lib/client/browserType.js
generated
vendored
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.BrowserType = void 0;
|
||||||
|
var _browser3 = require("./browser");
|
||||||
|
var _browserContext = require("./browserContext");
|
||||||
|
var _channelOwner = require("./channelOwner");
|
||||||
|
var _connection = require("./connection");
|
||||||
|
var _events = require("./events");
|
||||||
|
var _clientHelper = require("./clientHelper");
|
||||||
|
var _utils = require("../utils");
|
||||||
|
var _timeoutRunner = require("../utils/timeoutRunner");
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This is here just for api generation and checking.
|
||||||
|
|
||||||
|
class BrowserType extends _channelOwner.ChannelOwner {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
this._serverLauncher = void 0;
|
||||||
|
this._contexts = new Set();
|
||||||
|
this._playwright = void 0;
|
||||||
|
// Instrumentation.
|
||||||
|
this._defaultContextOptions = void 0;
|
||||||
|
this._defaultContextTimeout = void 0;
|
||||||
|
this._defaultContextNavigationTimeout = void 0;
|
||||||
|
this._defaultLaunchOptions = void 0;
|
||||||
|
}
|
||||||
|
static from(browserType) {
|
||||||
|
return browserType._object;
|
||||||
|
}
|
||||||
|
executablePath() {
|
||||||
|
if (!this._initializer.executablePath) throw new Error('Browser is not supported on current platform');
|
||||||
|
return this._initializer.executablePath;
|
||||||
|
}
|
||||||
|
name() {
|
||||||
|
return this._initializer.name;
|
||||||
|
}
|
||||||
|
async launch(options = {}) {
|
||||||
|
var _this$_defaultLaunchO;
|
||||||
|
(0, _utils.assert)(!options.userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
|
||||||
|
(0, _utils.assert)(!options.port, 'Cannot specify a port without launching as a server.');
|
||||||
|
const logger = options.logger || ((_this$_defaultLaunchO = this._defaultLaunchOptions) === null || _this$_defaultLaunchO === void 0 ? void 0 : _this$_defaultLaunchO.logger);
|
||||||
|
options = {
|
||||||
|
...this._defaultLaunchOptions,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
const launchOptions = {
|
||||||
|
...options,
|
||||||
|
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined,
|
||||||
|
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
|
||||||
|
env: options.env ? (0, _clientHelper.envObjectToArray)(options.env) : undefined
|
||||||
|
};
|
||||||
|
return await this._wrapApiCall(async () => {
|
||||||
|
const browser = _browser3.Browser.from((await this._channel.launch(launchOptions)).browser);
|
||||||
|
this._didLaunchBrowser(browser, options, logger);
|
||||||
|
return browser;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async launchServer(options = {}) {
|
||||||
|
if (!this._serverLauncher) throw new Error('Launching server is not supported');
|
||||||
|
options = {
|
||||||
|
...this._defaultLaunchOptions,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
return await this._serverLauncher.launchServer(options);
|
||||||
|
}
|
||||||
|
async launchPersistentContext(userDataDir, options = {}) {
|
||||||
|
var _this$_defaultLaunchO2;
|
||||||
|
const logger = options.logger || ((_this$_defaultLaunchO2 = this._defaultLaunchOptions) === null || _this$_defaultLaunchO2 === void 0 ? void 0 : _this$_defaultLaunchO2.logger);
|
||||||
|
(0, _utils.assert)(!options.port, 'Cannot specify a port without launching as a server.');
|
||||||
|
options = {
|
||||||
|
...this._defaultLaunchOptions,
|
||||||
|
...this._defaultContextOptions,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
const contextParams = await (0, _browserContext.prepareBrowserContextParams)(options);
|
||||||
|
const persistentParams = {
|
||||||
|
...contextParams,
|
||||||
|
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined,
|
||||||
|
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
|
||||||
|
env: options.env ? (0, _clientHelper.envObjectToArray)(options.env) : undefined,
|
||||||
|
channel: options.channel,
|
||||||
|
userDataDir
|
||||||
|
};
|
||||||
|
return await this._wrapApiCall(async () => {
|
||||||
|
const result = await this._channel.launchPersistentContext(persistentParams);
|
||||||
|
const context = _browserContext.BrowserContext.from(result.context);
|
||||||
|
await this._didCreateContext(context, contextParams, options, logger);
|
||||||
|
return context;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async connect(optionsOrWsEndpoint, options) {
|
||||||
|
if (typeof optionsOrWsEndpoint === 'string') return await this._connect({
|
||||||
|
...options,
|
||||||
|
wsEndpoint: optionsOrWsEndpoint
|
||||||
|
});
|
||||||
|
(0, _utils.assert)(optionsOrWsEndpoint.wsEndpoint, 'options.wsEndpoint is required');
|
||||||
|
return await this._connect(optionsOrWsEndpoint);
|
||||||
|
}
|
||||||
|
async _connect(params) {
|
||||||
|
const logger = params.logger;
|
||||||
|
return await this._wrapApiCall(async () => {
|
||||||
|
var _params$exposeNetwork;
|
||||||
|
const deadline = params.timeout ? (0, _utils.monotonicTime)() + params.timeout : 0;
|
||||||
|
const headers = {
|
||||||
|
'x-playwright-browser': this.name(),
|
||||||
|
...params.headers
|
||||||
|
};
|
||||||
|
const localUtils = this._connection.localUtils();
|
||||||
|
const connectParams = {
|
||||||
|
wsEndpoint: params.wsEndpoint,
|
||||||
|
headers,
|
||||||
|
exposeNetwork: (_params$exposeNetwork = params.exposeNetwork) !== null && _params$exposeNetwork !== void 0 ? _params$exposeNetwork : params._exposeNetwork,
|
||||||
|
slowMo: params.slowMo,
|
||||||
|
timeout: params.timeout
|
||||||
|
};
|
||||||
|
if (params.__testHookRedirectPortForwarding) connectParams.socksProxyRedirectPortForTest = params.__testHookRedirectPortForwarding;
|
||||||
|
const {
|
||||||
|
pipe,
|
||||||
|
headers: connectHeaders
|
||||||
|
} = await localUtils._channel.connect(connectParams);
|
||||||
|
const closePipe = () => pipe.close().catch(() => {});
|
||||||
|
const connection = new _connection.Connection(localUtils, this._instrumentation);
|
||||||
|
connection.markAsRemote();
|
||||||
|
connection.on('close', closePipe);
|
||||||
|
let browser;
|
||||||
|
let closeError;
|
||||||
|
const onPipeClosed = reason => {
|
||||||
|
// Emulate all pages, contexts and the browser closing upon disconnect.
|
||||||
|
for (const context of ((_browser = browser) === null || _browser === void 0 ? void 0 : _browser.contexts()) || []) {
|
||||||
|
var _browser;
|
||||||
|
for (const page of context.pages()) page._onClose();
|
||||||
|
context._onClose();
|
||||||
|
}
|
||||||
|
connection.close(reason || closeError);
|
||||||
|
// Give a chance to any API call promises to reject upon page/context closure.
|
||||||
|
// This happens naturally when we receive page.onClose and browser.onClose from the server
|
||||||
|
// in separate tasks. However, upon pipe closure we used to dispatch them all synchronously
|
||||||
|
// here and promises did not have a chance to reject.
|
||||||
|
// The order of rejects vs closure is a part of the API contract and our test runner
|
||||||
|
// relies on it to attribute rejections to the right test.
|
||||||
|
setTimeout(() => {
|
||||||
|
var _browser2;
|
||||||
|
return (_browser2 = browser) === null || _browser2 === void 0 ? void 0 : _browser2._didClose();
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
pipe.on('closed', params => onPipeClosed(params.reason));
|
||||||
|
connection.onmessage = message => this._wrapApiCall(() => pipe.send({
|
||||||
|
message
|
||||||
|
}).catch(() => onPipeClosed()), /* isInternal */true);
|
||||||
|
pipe.on('message', ({
|
||||||
|
message
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
connection.dispatch(message);
|
||||||
|
} catch (e) {
|
||||||
|
closeError = String(e);
|
||||||
|
closePipe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const result = await (0, _timeoutRunner.raceAgainstDeadline)(async () => {
|
||||||
|
// For tests.
|
||||||
|
if (params.__testHookBeforeCreateBrowser) await params.__testHookBeforeCreateBrowser();
|
||||||
|
const playwright = await connection.initializePlaywright();
|
||||||
|
if (!playwright._initializer.preLaunchedBrowser) {
|
||||||
|
closePipe();
|
||||||
|
throw new Error('Malformed endpoint. Did you use BrowserType.launchServer method?');
|
||||||
|
}
|
||||||
|
playwright._setSelectors(this._playwright.selectors);
|
||||||
|
browser = _browser3.Browser.from(playwright._initializer.preLaunchedBrowser);
|
||||||
|
this._didLaunchBrowser(browser, {}, logger);
|
||||||
|
browser._shouldCloseConnectionOnClose = true;
|
||||||
|
browser._connectHeaders = connectHeaders;
|
||||||
|
browser.on(_events.Events.Browser.Disconnected, () => this._wrapApiCall(() => closePipe(), /* isInternal */true));
|
||||||
|
return browser;
|
||||||
|
}, deadline);
|
||||||
|
if (!result.timedOut) {
|
||||||
|
return result.result;
|
||||||
|
} else {
|
||||||
|
closePipe();
|
||||||
|
throw new Error(`Timeout ${params.timeout}ms exceeded`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async connectOverCDP(endpointURLOrOptions, options) {
|
||||||
|
if (typeof endpointURLOrOptions === 'string') return await this._connectOverCDP(endpointURLOrOptions, options);
|
||||||
|
const endpointURL = 'endpointURL' in endpointURLOrOptions ? endpointURLOrOptions.endpointURL : endpointURLOrOptions.wsEndpoint;
|
||||||
|
(0, _utils.assert)(endpointURL, 'Cannot connect over CDP without wsEndpoint.');
|
||||||
|
return await this.connectOverCDP(endpointURL, endpointURLOrOptions);
|
||||||
|
}
|
||||||
|
async _connectOverCDP(endpointURL, params = {}) {
|
||||||
|
if (this.name() !== 'chromium') throw new Error('Connecting over CDP is only supported in Chromium.');
|
||||||
|
const headers = params.headers ? (0, _utils.headersObjectToArray)(params.headers) : undefined;
|
||||||
|
const result = await this._channel.connectOverCDP({
|
||||||
|
endpointURL,
|
||||||
|
headers,
|
||||||
|
slowMo: params.slowMo,
|
||||||
|
timeout: params.timeout
|
||||||
|
});
|
||||||
|
const browser = _browser3.Browser.from(result.browser);
|
||||||
|
this._didLaunchBrowser(browser, {}, params.logger);
|
||||||
|
if (result.defaultContext) await this._didCreateContext(_browserContext.BrowserContext.from(result.defaultContext), {}, {}, params.logger);
|
||||||
|
return browser;
|
||||||
|
}
|
||||||
|
_didLaunchBrowser(browser, browserOptions, logger) {
|
||||||
|
browser._browserType = this;
|
||||||
|
browser._options = browserOptions;
|
||||||
|
browser._logger = logger;
|
||||||
|
}
|
||||||
|
async _didCreateContext(context, contextOptions, browserOptions, logger) {
|
||||||
|
context._logger = logger;
|
||||||
|
context._browserType = this;
|
||||||
|
this._contexts.add(context);
|
||||||
|
context._setOptions(contextOptions, browserOptions);
|
||||||
|
if (this._defaultContextTimeout !== undefined) context.setDefaultTimeout(this._defaultContextTimeout);
|
||||||
|
if (this._defaultContextNavigationTimeout !== undefined) context.setDefaultNavigationTimeout(this._defaultContextNavigationTimeout);
|
||||||
|
await this._instrumentation.runAfterCreateBrowserContext(context);
|
||||||
|
}
|
||||||
|
async _willCloseContext(context) {
|
||||||
|
this._contexts.delete(context);
|
||||||
|
await this._instrumentation.runBeforeCloseBrowserContext(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.BrowserType = BrowserType;
|
||||||
53
node_modules/playwright-core/lib/client/cdpSession.js
generated
vendored
Normal file
53
node_modules/playwright-core/lib/client/cdpSession.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.CDPSession = void 0;
|
||||||
|
var _channelOwner = require("./channelOwner");
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CDPSession extends _channelOwner.ChannelOwner {
|
||||||
|
static from(cdpSession) {
|
||||||
|
return cdpSession._object;
|
||||||
|
}
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
|
this._channel.on('event', ({
|
||||||
|
method,
|
||||||
|
params
|
||||||
|
}) => {
|
||||||
|
this.emit(method, params);
|
||||||
|
});
|
||||||
|
this.on = super.on;
|
||||||
|
this.addListener = super.addListener;
|
||||||
|
this.off = super.removeListener;
|
||||||
|
this.removeListener = super.removeListener;
|
||||||
|
this.once = super.once;
|
||||||
|
}
|
||||||
|
async send(method, params) {
|
||||||
|
const result = await this._channel.send({
|
||||||
|
method,
|
||||||
|
params
|
||||||
|
});
|
||||||
|
return result.result;
|
||||||
|
}
|
||||||
|
async detach() {
|
||||||
|
return await this._channel.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.CDPSession = CDPSession;
|
||||||
235
node_modules/playwright-core/lib/client/channelOwner.js
generated
vendored
Normal file
235
node_modules/playwright-core/lib/client/channelOwner.js
generated
vendored
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.ChannelOwner = void 0;
|
||||||
|
var _eventEmitter = require("./eventEmitter");
|
||||||
|
var _validator = require("../protocol/validator");
|
||||||
|
var _debugLogger = require("../utils/debugLogger");
|
||||||
|
var _stackTrace = require("../utils/stackTrace");
|
||||||
|
var _utils = require("../utils");
|
||||||
|
var _zones = require("../utils/zones");
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ChannelOwner extends _eventEmitter.EventEmitter {
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
super();
|
||||||
|
this._connection = void 0;
|
||||||
|
this._parent = void 0;
|
||||||
|
this._objects = new Map();
|
||||||
|
this._type = void 0;
|
||||||
|
this._guid = void 0;
|
||||||
|
this._channel = void 0;
|
||||||
|
this._initializer = void 0;
|
||||||
|
this._logger = void 0;
|
||||||
|
this._instrumentation = void 0;
|
||||||
|
this._eventToSubscriptionMapping = new Map();
|
||||||
|
this._isInternalType = false;
|
||||||
|
this._wasCollected = false;
|
||||||
|
this.setMaxListeners(0);
|
||||||
|
this._connection = parent instanceof ChannelOwner ? parent._connection : parent;
|
||||||
|
this._type = type;
|
||||||
|
this._guid = guid;
|
||||||
|
this._parent = parent instanceof ChannelOwner ? parent : undefined;
|
||||||
|
this._instrumentation = this._connection._instrumentation;
|
||||||
|
this._connection._objects.set(guid, this);
|
||||||
|
if (this._parent) {
|
||||||
|
this._parent._objects.set(guid, this);
|
||||||
|
this._logger = this._parent._logger;
|
||||||
|
}
|
||||||
|
this._channel = this._createChannel(new _eventEmitter.EventEmitter());
|
||||||
|
this._initializer = initializer;
|
||||||
|
}
|
||||||
|
markAsInternalType() {
|
||||||
|
this._isInternalType = true;
|
||||||
|
}
|
||||||
|
_setEventToSubscriptionMapping(mapping) {
|
||||||
|
this._eventToSubscriptionMapping = mapping;
|
||||||
|
}
|
||||||
|
_updateSubscription(event, enabled) {
|
||||||
|
const protocolEvent = this._eventToSubscriptionMapping.get(String(event));
|
||||||
|
if (protocolEvent) {
|
||||||
|
this._wrapApiCall(async () => {
|
||||||
|
await this._channel.updateSubscription({
|
||||||
|
event: protocolEvent,
|
||||||
|
enabled
|
||||||
|
});
|
||||||
|
}, true).catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
on(event, listener) {
|
||||||
|
if (!this.listenerCount(event)) this._updateSubscription(event, true);
|
||||||
|
super.on(event, listener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
addListener(event, listener) {
|
||||||
|
if (!this.listenerCount(event)) this._updateSubscription(event, true);
|
||||||
|
super.addListener(event, listener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
prependListener(event, listener) {
|
||||||
|
if (!this.listenerCount(event)) this._updateSubscription(event, true);
|
||||||
|
super.prependListener(event, listener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
off(event, listener) {
|
||||||
|
super.off(event, listener);
|
||||||
|
if (!this.listenerCount(event)) this._updateSubscription(event, false);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
removeListener(event, listener) {
|
||||||
|
super.removeListener(event, listener);
|
||||||
|
if (!this.listenerCount(event)) this._updateSubscription(event, false);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
_adopt(child) {
|
||||||
|
child._parent._objects.delete(child._guid);
|
||||||
|
this._objects.set(child._guid, child);
|
||||||
|
child._parent = this;
|
||||||
|
}
|
||||||
|
_dispose(reason) {
|
||||||
|
// Clean up from parent and connection.
|
||||||
|
if (this._parent) this._parent._objects.delete(this._guid);
|
||||||
|
this._connection._objects.delete(this._guid);
|
||||||
|
this._wasCollected = reason === 'gc';
|
||||||
|
|
||||||
|
// Dispose all children.
|
||||||
|
for (const object of [...this._objects.values()]) object._dispose(reason);
|
||||||
|
this._objects.clear();
|
||||||
|
}
|
||||||
|
_debugScopeState() {
|
||||||
|
return {
|
||||||
|
_guid: this._guid,
|
||||||
|
objects: Array.from(this._objects.values()).map(o => o._debugScopeState())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_createChannel(base) {
|
||||||
|
const channel = new Proxy(base, {
|
||||||
|
get: (obj, prop) => {
|
||||||
|
if (typeof prop === 'string') {
|
||||||
|
const validator = (0, _validator.maybeFindValidator)(this._type, prop, 'Params');
|
||||||
|
if (validator) {
|
||||||
|
return async params => {
|
||||||
|
return await this._wrapApiCall(async apiZone => {
|
||||||
|
const {
|
||||||
|
apiName,
|
||||||
|
frames,
|
||||||
|
csi,
|
||||||
|
callCookie,
|
||||||
|
stepId
|
||||||
|
} = apiZone.reported ? {
|
||||||
|
apiName: undefined,
|
||||||
|
csi: undefined,
|
||||||
|
callCookie: undefined,
|
||||||
|
frames: [],
|
||||||
|
stepId: undefined
|
||||||
|
} : apiZone;
|
||||||
|
apiZone.reported = true;
|
||||||
|
let currentStepId = stepId;
|
||||||
|
if (csi && apiName) {
|
||||||
|
const out = {};
|
||||||
|
csi.onApiCallBegin(apiName, params, frames, callCookie, out);
|
||||||
|
currentStepId = out.stepId;
|
||||||
|
}
|
||||||
|
return await this._connection.sendMessageToServer(this, prop, validator(params, '', {
|
||||||
|
tChannelImpl: tChannelImplToWire,
|
||||||
|
binary: this._connection.rawBuffers() ? 'buffer' : 'toBase64'
|
||||||
|
}), apiName, frames, currentStepId);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj[prop];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
channel._object = this;
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
async _wrapApiCall(func, isInternal) {
|
||||||
|
const logger = this._logger;
|
||||||
|
const apiZone = _zones.zones.zoneData('apiZone');
|
||||||
|
if (apiZone) return await func(apiZone);
|
||||||
|
const stackTrace = (0, _stackTrace.captureLibraryStackTrace)();
|
||||||
|
let apiName = stackTrace.apiName;
|
||||||
|
const frames = stackTrace.frames;
|
||||||
|
if (isInternal === undefined) isInternal = this._isInternalType;
|
||||||
|
if (isInternal) apiName = undefined;
|
||||||
|
|
||||||
|
// Enclosing zone could have provided the apiName and wallTime.
|
||||||
|
const expectZone = _zones.zones.zoneData('expectZone');
|
||||||
|
const stepId = expectZone === null || expectZone === void 0 ? void 0 : expectZone.stepId;
|
||||||
|
if (!isInternal && expectZone) apiName = expectZone.title;
|
||||||
|
|
||||||
|
// If we are coming from the expectZone, there is no need to generate a new
|
||||||
|
// step for the API call, since it will be generated by the expect itself.
|
||||||
|
const csi = isInternal || expectZone ? undefined : this._instrumentation;
|
||||||
|
const callCookie = {};
|
||||||
|
try {
|
||||||
|
logApiCall(logger, `=> ${apiName} started`, isInternal);
|
||||||
|
const apiZone = {
|
||||||
|
apiName,
|
||||||
|
frames,
|
||||||
|
isInternal,
|
||||||
|
reported: false,
|
||||||
|
csi,
|
||||||
|
callCookie,
|
||||||
|
stepId
|
||||||
|
};
|
||||||
|
const result = await _zones.zones.run('apiZone', apiZone, async () => await func(apiZone));
|
||||||
|
csi === null || csi === void 0 || csi.onApiCallEnd(callCookie);
|
||||||
|
logApiCall(logger, `<= ${apiName} succeeded`, isInternal);
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
const innerError = (process.env.PWDEBUGIMPL || (0, _utils.isUnderTest)()) && e.stack ? '\n<inner error>\n' + e.stack : '';
|
||||||
|
if (apiName && !apiName.includes('<anonymous>')) e.message = apiName + ': ' + e.message;
|
||||||
|
const stackFrames = '\n' + (0, _stackTrace.stringifyStackFrames)(stackTrace.frames).join('\n') + innerError;
|
||||||
|
if (stackFrames.trim()) e.stack = e.message + stackFrames;else e.stack = '';
|
||||||
|
csi === null || csi === void 0 || csi.onApiCallEnd(callCookie, e);
|
||||||
|
logApiCall(logger, `<= ${apiName} failed`, isInternal);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_toImpl() {
|
||||||
|
var _this$_connection$toI, _this$_connection;
|
||||||
|
return (_this$_connection$toI = (_this$_connection = this._connection).toImpl) === null || _this$_connection$toI === void 0 ? void 0 : _this$_connection$toI.call(_this$_connection, this);
|
||||||
|
}
|
||||||
|
toJSON() {
|
||||||
|
// Jest's expect library tries to print objects sometimes.
|
||||||
|
// RPC objects can contain links to lots of other objects,
|
||||||
|
// which can cause jest to crash. Let's help it out
|
||||||
|
// by just returning the important values.
|
||||||
|
return {
|
||||||
|
_type: this._type,
|
||||||
|
_guid: this._guid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.ChannelOwner = ChannelOwner;
|
||||||
|
function logApiCall(logger, message, isNested) {
|
||||||
|
if (isNested) return;
|
||||||
|
if (logger && logger.isEnabled('api', 'info')) logger.log('api', 'info', message, [], {
|
||||||
|
color: 'cyan'
|
||||||
|
});
|
||||||
|
_debugLogger.debugLogger.log('api', message);
|
||||||
|
}
|
||||||
|
function tChannelImplToWire(names, arg, path, context) {
|
||||||
|
if (arg._object instanceof ChannelOwner && (names === '*' || names.includes(arg._object._type))) return {
|
||||||
|
guid: arg._object._guid
|
||||||
|
};
|
||||||
|
throw new _validator.ValidationError(`${path}: expected channel ${names.toString()}`);
|
||||||
|
}
|
||||||
57
node_modules/playwright-core/lib/client/clientHelper.js
generated
vendored
Normal file
57
node_modules/playwright-core/lib/client/clientHelper.js
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.addSourceUrlToScript = addSourceUrlToScript;
|
||||||
|
exports.envObjectToArray = envObjectToArray;
|
||||||
|
exports.evaluationScript = evaluationScript;
|
||||||
|
var _fs = _interopRequireDefault(require("fs"));
|
||||||
|
var _utils = require("../utils");
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
/**
|
||||||
|
* Copyright 2017 Google Inc. All rights reserved.
|
||||||
|
* Modifications copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function envObjectToArray(env) {
|
||||||
|
const result = [];
|
||||||
|
for (const name in env) {
|
||||||
|
if (!Object.is(env[name], undefined)) result.push({
|
||||||
|
name,
|
||||||
|
value: String(env[name])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
async function evaluationScript(fun, arg, addSourceUrl = true) {
|
||||||
|
if (typeof fun === 'function') {
|
||||||
|
const source = fun.toString();
|
||||||
|
const argString = Object.is(arg, undefined) ? 'undefined' : JSON.stringify(arg);
|
||||||
|
return `(${source})(${argString})`;
|
||||||
|
}
|
||||||
|
if (arg !== undefined) throw new Error('Cannot evaluate a string with arguments');
|
||||||
|
if ((0, _utils.isString)(fun)) return fun;
|
||||||
|
if (fun.content !== undefined) return fun.content;
|
||||||
|
if (fun.path !== undefined) {
|
||||||
|
let source = await _fs.default.promises.readFile(fun.path, 'utf8');
|
||||||
|
if (addSourceUrl) source = addSourceUrlToScript(source, fun.path);
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
throw new Error('Either path or content property must be present');
|
||||||
|
}
|
||||||
|
function addSourceUrlToScript(source, path) {
|
||||||
|
return `${source}\n//# sourceURL=${path.replace(/\n/g, '')}`;
|
||||||
|
}
|
||||||
50
node_modules/playwright-core/lib/client/clientInstrumentation.js
generated
vendored
Normal file
50
node_modules/playwright-core/lib/client/clientInstrumentation.js
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.createInstrumentation = createInstrumentation;
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function createInstrumentation() {
|
||||||
|
const listeners = [];
|
||||||
|
return new Proxy({}, {
|
||||||
|
get: (obj, prop) => {
|
||||||
|
if (typeof prop !== 'string') return obj[prop];
|
||||||
|
if (prop === 'addListener') return listener => listeners.push(listener);
|
||||||
|
if (prop === 'removeListener') return listener => listeners.splice(listeners.indexOf(listener), 1);
|
||||||
|
if (prop === 'removeAllListeners') return () => listeners.splice(0, listeners.length);
|
||||||
|
if (prop.startsWith('run')) {
|
||||||
|
return async (...params) => {
|
||||||
|
for (const listener of listeners) {
|
||||||
|
var _prop, _ref;
|
||||||
|
await ((_prop = (_ref = listener)[prop]) === null || _prop === void 0 ? void 0 : _prop.call(_ref, ...params));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (prop.startsWith('on')) {
|
||||||
|
return (...params) => {
|
||||||
|
for (const listener of listeners) {
|
||||||
|
var _prop2, _ref2;
|
||||||
|
(_prop2 = (_ref2 = listener)[prop]) === null || _prop2 === void 0 || _prop2.call(_ref2, ...params);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return obj[prop];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
68
node_modules/playwright-core/lib/client/clock.js
generated
vendored
Normal file
68
node_modules/playwright-core/lib/client/clock.js
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.Clock = void 0;
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Clock {
|
||||||
|
constructor(browserContext) {
|
||||||
|
this._browserContext = void 0;
|
||||||
|
this._browserContext = browserContext;
|
||||||
|
}
|
||||||
|
async install(options = {}) {
|
||||||
|
await this._browserContext._channel.clockInstall(options.time !== undefined ? parseTime(options.time) : {});
|
||||||
|
}
|
||||||
|
async fastForward(ticks) {
|
||||||
|
await this._browserContext._channel.clockFastForward(parseTicks(ticks));
|
||||||
|
}
|
||||||
|
async pauseAt(time) {
|
||||||
|
await this._browserContext._channel.clockPauseAt(parseTime(time));
|
||||||
|
}
|
||||||
|
async resume() {
|
||||||
|
await this._browserContext._channel.clockResume({});
|
||||||
|
}
|
||||||
|
async runFor(ticks) {
|
||||||
|
await this._browserContext._channel.clockRunFor(parseTicks(ticks));
|
||||||
|
}
|
||||||
|
async setFixedTime(time) {
|
||||||
|
await this._browserContext._channel.clockSetFixedTime(parseTime(time));
|
||||||
|
}
|
||||||
|
async setSystemTime(time) {
|
||||||
|
await this._browserContext._channel.clockSetSystemTime(parseTime(time));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Clock = Clock;
|
||||||
|
function parseTime(time) {
|
||||||
|
if (typeof time === 'number') return {
|
||||||
|
timeNumber: time
|
||||||
|
};
|
||||||
|
if (typeof time === 'string') return {
|
||||||
|
timeString: time
|
||||||
|
};
|
||||||
|
if (!isFinite(time.getTime())) throw new Error(`Invalid date: ${time}`);
|
||||||
|
return {
|
||||||
|
timeNumber: time.getTime()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function parseTicks(ticks) {
|
||||||
|
return {
|
||||||
|
ticksNumber: typeof ticks === 'number' ? ticks : undefined,
|
||||||
|
ticksString: typeof ticks === 'string' ? ticks : undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
333
node_modules/playwright-core/lib/client/connection.js
generated
vendored
Normal file
333
node_modules/playwright-core/lib/client/connection.js
generated
vendored
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.Connection = void 0;
|
||||||
|
var _browser = require("./browser");
|
||||||
|
var _browserContext = require("./browserContext");
|
||||||
|
var _browserType = require("./browserType");
|
||||||
|
var _channelOwner = require("./channelOwner");
|
||||||
|
var _elementHandle = require("./elementHandle");
|
||||||
|
var _frame = require("./frame");
|
||||||
|
var _jsHandle = require("./jsHandle");
|
||||||
|
var _network = require("./network");
|
||||||
|
var _page = require("./page");
|
||||||
|
var _worker = require("./worker");
|
||||||
|
var _dialog = require("./dialog");
|
||||||
|
var _errors = require("./errors");
|
||||||
|
var _cdpSession = require("./cdpSession");
|
||||||
|
var _playwright = require("./playwright");
|
||||||
|
var _electron = require("./electron");
|
||||||
|
var _stream = require("./stream");
|
||||||
|
var _writableStream = require("./writableStream");
|
||||||
|
var _debugLogger = require("../utils/debugLogger");
|
||||||
|
var _selectors = require("./selectors");
|
||||||
|
var _android = require("./android");
|
||||||
|
var _artifact = require("./artifact");
|
||||||
|
var _events = require("events");
|
||||||
|
var _jsonPipe = require("./jsonPipe");
|
||||||
|
var _fetch = require("./fetch");
|
||||||
|
var _localUtils = require("./localUtils");
|
||||||
|
var _tracing = require("./tracing");
|
||||||
|
var _validator = require("../protocol/validator");
|
||||||
|
var _clientInstrumentation = require("./clientInstrumentation");
|
||||||
|
var _utils = require("../utils");
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Root extends _channelOwner.ChannelOwner {
|
||||||
|
constructor(connection) {
|
||||||
|
super(connection, 'Root', '', {});
|
||||||
|
}
|
||||||
|
async initialize() {
|
||||||
|
return _playwright.Playwright.from((await this._channel.initialize({
|
||||||
|
sdkLanguage: 'javascript'
|
||||||
|
})).playwright);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class DummyChannelOwner extends _channelOwner.ChannelOwner {}
|
||||||
|
class Connection extends _events.EventEmitter {
|
||||||
|
constructor(localUtils, instrumentation) {
|
||||||
|
super();
|
||||||
|
this._objects = new Map();
|
||||||
|
this.onmessage = message => {};
|
||||||
|
this._lastId = 0;
|
||||||
|
this._callbacks = new Map();
|
||||||
|
this._rootObject = void 0;
|
||||||
|
this._closedError = void 0;
|
||||||
|
this._isRemote = false;
|
||||||
|
this._localUtils = void 0;
|
||||||
|
this._rawBuffers = false;
|
||||||
|
// Some connections allow resolving in-process dispatchers.
|
||||||
|
this.toImpl = void 0;
|
||||||
|
this._tracingCount = 0;
|
||||||
|
this._instrumentation = void 0;
|
||||||
|
this._rootObject = new Root(this);
|
||||||
|
this._localUtils = localUtils;
|
||||||
|
this._instrumentation = instrumentation || (0, _clientInstrumentation.createInstrumentation)();
|
||||||
|
}
|
||||||
|
markAsRemote() {
|
||||||
|
this._isRemote = true;
|
||||||
|
}
|
||||||
|
isRemote() {
|
||||||
|
return this._isRemote;
|
||||||
|
}
|
||||||
|
useRawBuffers() {
|
||||||
|
this._rawBuffers = true;
|
||||||
|
}
|
||||||
|
rawBuffers() {
|
||||||
|
return this._rawBuffers;
|
||||||
|
}
|
||||||
|
localUtils() {
|
||||||
|
return this._localUtils;
|
||||||
|
}
|
||||||
|
async initializePlaywright() {
|
||||||
|
return await this._rootObject.initialize();
|
||||||
|
}
|
||||||
|
getObjectWithKnownName(guid) {
|
||||||
|
return this._objects.get(guid);
|
||||||
|
}
|
||||||
|
setIsTracing(isTracing) {
|
||||||
|
if (isTracing) this._tracingCount++;else this._tracingCount--;
|
||||||
|
}
|
||||||
|
async sendMessageToServer(object, method, params, apiName, frames, stepId) {
|
||||||
|
var _this$_localUtils;
|
||||||
|
if (this._closedError) throw this._closedError;
|
||||||
|
if (object._wasCollected) throw new Error('The object has been collected to prevent unbounded heap growth.');
|
||||||
|
const guid = object._guid;
|
||||||
|
const type = object._type;
|
||||||
|
const id = ++this._lastId;
|
||||||
|
const message = {
|
||||||
|
id,
|
||||||
|
guid,
|
||||||
|
method,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
if (_debugLogger.debugLogger.isEnabled('channel')) {
|
||||||
|
// Do not include metadata in debug logs to avoid noise.
|
||||||
|
_debugLogger.debugLogger.log('channel', 'SEND> ' + JSON.stringify(message));
|
||||||
|
}
|
||||||
|
const location = frames[0] ? {
|
||||||
|
file: frames[0].file,
|
||||||
|
line: frames[0].line,
|
||||||
|
column: frames[0].column
|
||||||
|
} : undefined;
|
||||||
|
const metadata = {
|
||||||
|
apiName,
|
||||||
|
location,
|
||||||
|
internal: !apiName,
|
||||||
|
stepId
|
||||||
|
};
|
||||||
|
if (this._tracingCount && frames && type !== 'LocalUtils') (_this$_localUtils = this._localUtils) === null || _this$_localUtils === void 0 || _this$_localUtils._channel.addStackToTracingNoReply({
|
||||||
|
callData: {
|
||||||
|
stack: frames,
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}).catch(() => {});
|
||||||
|
// We need to exit zones before calling into the server, otherwise
|
||||||
|
// when we receive events from the server, we would be in an API zone.
|
||||||
|
_utils.zones.exitZones(() => this.onmessage({
|
||||||
|
...message,
|
||||||
|
metadata
|
||||||
|
}));
|
||||||
|
return await new Promise((resolve, reject) => this._callbacks.set(id, {
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
apiName,
|
||||||
|
type,
|
||||||
|
method
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
dispatch(message) {
|
||||||
|
if (this._closedError) return;
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
guid,
|
||||||
|
method,
|
||||||
|
params,
|
||||||
|
result,
|
||||||
|
error,
|
||||||
|
log
|
||||||
|
} = message;
|
||||||
|
if (id) {
|
||||||
|
if (_debugLogger.debugLogger.isEnabled('channel')) _debugLogger.debugLogger.log('channel', '<RECV ' + JSON.stringify(message));
|
||||||
|
const callback = this._callbacks.get(id);
|
||||||
|
if (!callback) throw new Error(`Cannot find command to respond: ${id}`);
|
||||||
|
this._callbacks.delete(id);
|
||||||
|
if (error && !result) {
|
||||||
|
const parsedError = (0, _errors.parseError)(error);
|
||||||
|
(0, _utils.rewriteErrorMessage)(parsedError, parsedError.message + (0, _utils.formatCallLog)(log));
|
||||||
|
callback.reject(parsedError);
|
||||||
|
} else {
|
||||||
|
const validator = (0, _validator.findValidator)(callback.type, callback.method, 'Result');
|
||||||
|
callback.resolve(validator(result, '', {
|
||||||
|
tChannelImpl: this._tChannelImplFromWire.bind(this),
|
||||||
|
binary: this._rawBuffers ? 'buffer' : 'fromBase64'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_debugLogger.debugLogger.isEnabled('channel')) _debugLogger.debugLogger.log('channel', '<EVENT ' + JSON.stringify(message));
|
||||||
|
if (method === '__create__') {
|
||||||
|
this._createRemoteObject(guid, params.type, params.guid, params.initializer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const object = this._objects.get(guid);
|
||||||
|
if (!object) throw new Error(`Cannot find object to "${method}": ${guid}`);
|
||||||
|
if (method === '__adopt__') {
|
||||||
|
const child = this._objects.get(params.guid);
|
||||||
|
if (!child) throw new Error(`Unknown new child: ${params.guid}`);
|
||||||
|
object._adopt(child);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (method === '__dispose__') {
|
||||||
|
object._dispose(params.reason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const validator = (0, _validator.findValidator)(object._type, method, 'Event');
|
||||||
|
object._channel.emit(method, validator(params, '', {
|
||||||
|
tChannelImpl: this._tChannelImplFromWire.bind(this),
|
||||||
|
binary: this._rawBuffers ? 'buffer' : 'fromBase64'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
close(cause) {
|
||||||
|
if (this._closedError) return;
|
||||||
|
this._closedError = new _errors.TargetClosedError(cause);
|
||||||
|
for (const callback of this._callbacks.values()) callback.reject(this._closedError);
|
||||||
|
this._callbacks.clear();
|
||||||
|
this.emit('close');
|
||||||
|
}
|
||||||
|
_tChannelImplFromWire(names, arg, path, context) {
|
||||||
|
if (arg && typeof arg === 'object' && typeof arg.guid === 'string') {
|
||||||
|
const object = this._objects.get(arg.guid);
|
||||||
|
if (!object) throw new Error(`Object with guid ${arg.guid} was not bound in the connection`);
|
||||||
|
if (names !== '*' && !names.includes(object._type)) throw new _validator.ValidationError(`${path}: expected channel ${names.toString()}`);
|
||||||
|
return object._channel;
|
||||||
|
}
|
||||||
|
throw new _validator.ValidationError(`${path}: expected channel ${names.toString()}`);
|
||||||
|
}
|
||||||
|
_createRemoteObject(parentGuid, type, guid, initializer) {
|
||||||
|
const parent = this._objects.get(parentGuid);
|
||||||
|
if (!parent) throw new Error(`Cannot find parent object ${parentGuid} to create ${guid}`);
|
||||||
|
let result;
|
||||||
|
const validator = (0, _validator.findValidator)(type, '', 'Initializer');
|
||||||
|
initializer = validator(initializer, '', {
|
||||||
|
tChannelImpl: this._tChannelImplFromWire.bind(this),
|
||||||
|
binary: this._rawBuffers ? 'buffer' : 'fromBase64'
|
||||||
|
});
|
||||||
|
switch (type) {
|
||||||
|
case 'Android':
|
||||||
|
result = new _android.Android(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'AndroidSocket':
|
||||||
|
result = new _android.AndroidSocket(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'AndroidDevice':
|
||||||
|
result = new _android.AndroidDevice(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'APIRequestContext':
|
||||||
|
result = new _fetch.APIRequestContext(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Artifact':
|
||||||
|
result = new _artifact.Artifact(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'BindingCall':
|
||||||
|
result = new _page.BindingCall(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Browser':
|
||||||
|
result = new _browser.Browser(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'BrowserContext':
|
||||||
|
result = new _browserContext.BrowserContext(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'BrowserType':
|
||||||
|
result = new _browserType.BrowserType(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'CDPSession':
|
||||||
|
result = new _cdpSession.CDPSession(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Dialog':
|
||||||
|
result = new _dialog.Dialog(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Electron':
|
||||||
|
result = new _electron.Electron(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'ElectronApplication':
|
||||||
|
result = new _electron.ElectronApplication(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'ElementHandle':
|
||||||
|
result = new _elementHandle.ElementHandle(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Frame':
|
||||||
|
result = new _frame.Frame(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'JSHandle':
|
||||||
|
result = new _jsHandle.JSHandle(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'JsonPipe':
|
||||||
|
result = new _jsonPipe.JsonPipe(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'LocalUtils':
|
||||||
|
result = new _localUtils.LocalUtils(parent, type, guid, initializer);
|
||||||
|
if (!this._localUtils) this._localUtils = result;
|
||||||
|
break;
|
||||||
|
case 'Page':
|
||||||
|
result = new _page.Page(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Playwright':
|
||||||
|
result = new _playwright.Playwright(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Request':
|
||||||
|
result = new _network.Request(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Response':
|
||||||
|
result = new _network.Response(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Route':
|
||||||
|
result = new _network.Route(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Stream':
|
||||||
|
result = new _stream.Stream(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Selectors':
|
||||||
|
result = new _selectors.SelectorsOwner(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'SocksSupport':
|
||||||
|
result = new DummyChannelOwner(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Tracing':
|
||||||
|
result = new _tracing.Tracing(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'WebSocket':
|
||||||
|
result = new _network.WebSocket(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'WebSocketRoute':
|
||||||
|
result = new _network.WebSocketRoute(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'Worker':
|
||||||
|
result = new _worker.Worker(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
case 'WritableStream':
|
||||||
|
result = new _writableStream.WritableStream(parent, type, guid, initializer);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('Missing type ' + type);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Connection = Connection;
|
||||||
55
node_modules/playwright-core/lib/client/consoleMessage.js
generated
vendored
Normal file
55
node_modules/playwright-core/lib/client/consoleMessage.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.ConsoleMessage = void 0;
|
||||||
|
var util = _interopRequireWildcard(require("util"));
|
||||||
|
var _jsHandle = require("./jsHandle");
|
||||||
|
var _page = require("./page");
|
||||||
|
let _util$inspect$custom;
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||||
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||||
|
_util$inspect$custom = util.inspect.custom;
|
||||||
|
class ConsoleMessage {
|
||||||
|
constructor(event) {
|
||||||
|
this._page = void 0;
|
||||||
|
this._event = void 0;
|
||||||
|
this._page = 'page' in event && event.page ? _page.Page.from(event.page) : null;
|
||||||
|
this._event = event;
|
||||||
|
}
|
||||||
|
page() {
|
||||||
|
return this._page;
|
||||||
|
}
|
||||||
|
type() {
|
||||||
|
return this._event.type;
|
||||||
|
}
|
||||||
|
text() {
|
||||||
|
return this._event.text;
|
||||||
|
}
|
||||||
|
args() {
|
||||||
|
return this._event.args.map(_jsHandle.JSHandle.from);
|
||||||
|
}
|
||||||
|
location() {
|
||||||
|
return this._event.location;
|
||||||
|
}
|
||||||
|
[_util$inspect$custom]() {
|
||||||
|
return this.text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.ConsoleMessage = ConsoleMessage;
|
||||||
41
node_modules/playwright-core/lib/client/coverage.js
generated
vendored
Normal file
41
node_modules/playwright-core/lib/client/coverage.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.Coverage = void 0;
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Coverage {
|
||||||
|
constructor(channel) {
|
||||||
|
this._channel = void 0;
|
||||||
|
this._channel = channel;
|
||||||
|
}
|
||||||
|
async startJSCoverage(options = {}) {
|
||||||
|
await this._channel.startJSCoverage(options);
|
||||||
|
}
|
||||||
|
async stopJSCoverage() {
|
||||||
|
return (await this._channel.stopJSCoverage()).entries;
|
||||||
|
}
|
||||||
|
async startCSSCoverage(options = {}) {
|
||||||
|
await this._channel.startCSSCoverage(options);
|
||||||
|
}
|
||||||
|
async stopCSSCoverage() {
|
||||||
|
return (await this._channel.stopCSSCoverage()).entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Coverage = Coverage;
|
||||||
57
node_modules/playwright-core/lib/client/dialog.js
generated
vendored
Normal file
57
node_modules/playwright-core/lib/client/dialog.js
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.Dialog = void 0;
|
||||||
|
var _channelOwner = require("./channelOwner");
|
||||||
|
var _page = require("./page");
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Dialog extends _channelOwner.ChannelOwner {
|
||||||
|
static from(dialog) {
|
||||||
|
return dialog._object;
|
||||||
|
}
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
|
// Note: dialogs that open early during page initialization block it.
|
||||||
|
// Therefore, we must report the dialog without a page to be able to handle it.
|
||||||
|
this._page = void 0;
|
||||||
|
this._page = _page.Page.fromNullable(initializer.page);
|
||||||
|
}
|
||||||
|
page() {
|
||||||
|
return this._page;
|
||||||
|
}
|
||||||
|
type() {
|
||||||
|
return this._initializer.type;
|
||||||
|
}
|
||||||
|
message() {
|
||||||
|
return this._initializer.message;
|
||||||
|
}
|
||||||
|
defaultValue() {
|
||||||
|
return this._initializer.defaultValue;
|
||||||
|
}
|
||||||
|
async accept(promptText) {
|
||||||
|
await this._channel.accept({
|
||||||
|
promptText
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async dismiss() {
|
||||||
|
await this._channel.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Dialog = Dialog;
|
||||||
62
node_modules/playwright-core/lib/client/download.js
generated
vendored
Normal file
62
node_modules/playwright-core/lib/client/download.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.Download = void 0;
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Download {
|
||||||
|
constructor(page, url, suggestedFilename, artifact) {
|
||||||
|
this._page = void 0;
|
||||||
|
this._url = void 0;
|
||||||
|
this._suggestedFilename = void 0;
|
||||||
|
this._artifact = void 0;
|
||||||
|
this._page = page;
|
||||||
|
this._url = url;
|
||||||
|
this._suggestedFilename = suggestedFilename;
|
||||||
|
this._artifact = artifact;
|
||||||
|
}
|
||||||
|
page() {
|
||||||
|
return this._page;
|
||||||
|
}
|
||||||
|
url() {
|
||||||
|
return this._url;
|
||||||
|
}
|
||||||
|
suggestedFilename() {
|
||||||
|
return this._suggestedFilename;
|
||||||
|
}
|
||||||
|
async path() {
|
||||||
|
return await this._artifact.pathAfterFinished();
|
||||||
|
}
|
||||||
|
async saveAs(path) {
|
||||||
|
return await this._artifact.saveAs(path);
|
||||||
|
}
|
||||||
|
async failure() {
|
||||||
|
return await this._artifact.failure();
|
||||||
|
}
|
||||||
|
async createReadStream() {
|
||||||
|
return await this._artifact.createReadStream();
|
||||||
|
}
|
||||||
|
async cancel() {
|
||||||
|
return await this._artifact.cancel();
|
||||||
|
}
|
||||||
|
async delete() {
|
||||||
|
return await this._artifact.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Download = Download;
|
||||||
135
node_modules/playwright-core/lib/client/electron.js
generated
vendored
Normal file
135
node_modules/playwright-core/lib/client/electron.js
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.ElectronApplication = exports.Electron = void 0;
|
||||||
|
var _timeoutSettings = require("../common/timeoutSettings");
|
||||||
|
var _browserContext = require("./browserContext");
|
||||||
|
var _channelOwner = require("./channelOwner");
|
||||||
|
var _clientHelper = require("./clientHelper");
|
||||||
|
var _events = require("./events");
|
||||||
|
var _jsHandle = require("./jsHandle");
|
||||||
|
var _consoleMessage = require("./consoleMessage");
|
||||||
|
var _waiter = require("./waiter");
|
||||||
|
var _errors = require("./errors");
|
||||||
|
let _Symbol$asyncDispose;
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
class Electron extends _channelOwner.ChannelOwner {
|
||||||
|
static from(electron) {
|
||||||
|
return electron._object;
|
||||||
|
}
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
|
}
|
||||||
|
async launch(options = {}) {
|
||||||
|
const params = {
|
||||||
|
...(await (0, _browserContext.prepareBrowserContextParams)(options)),
|
||||||
|
env: (0, _clientHelper.envObjectToArray)(options.env ? options.env : process.env),
|
||||||
|
tracesDir: options.tracesDir
|
||||||
|
};
|
||||||
|
const app = ElectronApplication.from((await this._channel.launch(params)).electronApplication);
|
||||||
|
app._context._setOptions(params, options);
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Electron = Electron;
|
||||||
|
_Symbol$asyncDispose = Symbol.asyncDispose;
|
||||||
|
class ElectronApplication extends _channelOwner.ChannelOwner {
|
||||||
|
static from(electronApplication) {
|
||||||
|
return electronApplication._object;
|
||||||
|
}
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
|
this._context = void 0;
|
||||||
|
this._windows = new Set();
|
||||||
|
this._timeoutSettings = new _timeoutSettings.TimeoutSettings();
|
||||||
|
this._context = _browserContext.BrowserContext.from(initializer.context);
|
||||||
|
for (const page of this._context._pages) this._onPage(page);
|
||||||
|
this._context.on(_events.Events.BrowserContext.Page, page => this._onPage(page));
|
||||||
|
this._channel.on('close', () => {
|
||||||
|
this.emit(_events.Events.ElectronApplication.Close);
|
||||||
|
});
|
||||||
|
this._channel.on('console', event => this.emit(_events.Events.ElectronApplication.Console, new _consoleMessage.ConsoleMessage(event)));
|
||||||
|
this._setEventToSubscriptionMapping(new Map([[_events.Events.ElectronApplication.Console, 'console']]));
|
||||||
|
}
|
||||||
|
process() {
|
||||||
|
return this._toImpl().process();
|
||||||
|
}
|
||||||
|
_onPage(page) {
|
||||||
|
this._windows.add(page);
|
||||||
|
this.emit(_events.Events.ElectronApplication.Window, page);
|
||||||
|
page.once(_events.Events.Page.Close, () => this._windows.delete(page));
|
||||||
|
}
|
||||||
|
windows() {
|
||||||
|
// TODO: add ElectronPage class inheriting from Page.
|
||||||
|
return [...this._windows];
|
||||||
|
}
|
||||||
|
async firstWindow(options) {
|
||||||
|
if (this._windows.size) return this._windows.values().next().value;
|
||||||
|
return await this.waitForEvent('window', options);
|
||||||
|
}
|
||||||
|
context() {
|
||||||
|
return this._context;
|
||||||
|
}
|
||||||
|
async [_Symbol$asyncDispose]() {
|
||||||
|
await this.close();
|
||||||
|
}
|
||||||
|
async close() {
|
||||||
|
try {
|
||||||
|
await this._context.close();
|
||||||
|
} catch (e) {
|
||||||
|
if ((0, _errors.isTargetClosedError)(e)) return;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async waitForEvent(event, optionsOrPredicate = {}) {
|
||||||
|
return await this._wrapApiCall(async () => {
|
||||||
|
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
|
||||||
|
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
|
||||||
|
const waiter = _waiter.Waiter.createForEvent(this, event);
|
||||||
|
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
|
||||||
|
if (event !== _events.Events.ElectronApplication.Close) waiter.rejectOnEvent(this, _events.Events.ElectronApplication.Close, () => new _errors.TargetClosedError());
|
||||||
|
const result = await waiter.waitForEvent(this, event, predicate);
|
||||||
|
waiter.dispose();
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async browserWindow(page) {
|
||||||
|
const result = await this._channel.browserWindow({
|
||||||
|
page: page._channel
|
||||||
|
});
|
||||||
|
return _jsHandle.JSHandle.from(result.handle);
|
||||||
|
}
|
||||||
|
async evaluate(pageFunction, arg) {
|
||||||
|
const result = await this._channel.evaluateExpression({
|
||||||
|
expression: String(pageFunction),
|
||||||
|
isFunction: typeof pageFunction === 'function',
|
||||||
|
arg: (0, _jsHandle.serializeArgument)(arg)
|
||||||
|
});
|
||||||
|
return (0, _jsHandle.parseResult)(result.value);
|
||||||
|
}
|
||||||
|
async evaluateHandle(pageFunction, arg) {
|
||||||
|
const result = await this._channel.evaluateExpressionHandle({
|
||||||
|
expression: String(pageFunction),
|
||||||
|
isFunction: typeof pageFunction === 'function',
|
||||||
|
arg: (0, _jsHandle.serializeArgument)(arg)
|
||||||
|
});
|
||||||
|
return _jsHandle.JSHandle.from(result.handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.ElectronApplication = ElectronApplication;
|
||||||
321
node_modules/playwright-core/lib/client/elementHandle.js
generated
vendored
Normal file
321
node_modules/playwright-core/lib/client/elementHandle.js
generated
vendored
Normal file
@@ -0,0 +1,321 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.ElementHandle = void 0;
|
||||||
|
exports.convertInputFiles = convertInputFiles;
|
||||||
|
exports.convertSelectOptionValues = convertSelectOptionValues;
|
||||||
|
exports.determineScreenshotType = determineScreenshotType;
|
||||||
|
var _frame = require("./frame");
|
||||||
|
var _jsHandle = require("./jsHandle");
|
||||||
|
var _fs = _interopRequireDefault(require("fs"));
|
||||||
|
var _utilsBundle = require("../utilsBundle");
|
||||||
|
var _path = _interopRequireDefault(require("path"));
|
||||||
|
var _utils = require("../utils");
|
||||||
|
var _fileUtils = require("../utils/fileUtils");
|
||||||
|
var _writableStream = require("./writableStream");
|
||||||
|
var _stream = require("stream");
|
||||||
|
var _util = require("util");
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const pipelineAsync = (0, _util.promisify)(_stream.pipeline);
|
||||||
|
class ElementHandle extends _jsHandle.JSHandle {
|
||||||
|
static from(handle) {
|
||||||
|
return handle._object;
|
||||||
|
}
|
||||||
|
static fromNullable(handle) {
|
||||||
|
return handle ? ElementHandle.from(handle) : null;
|
||||||
|
}
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
|
this._elementChannel = void 0;
|
||||||
|
this._elementChannel = this._channel;
|
||||||
|
}
|
||||||
|
asElement() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
async ownerFrame() {
|
||||||
|
return _frame.Frame.fromNullable((await this._elementChannel.ownerFrame()).frame);
|
||||||
|
}
|
||||||
|
async contentFrame() {
|
||||||
|
return _frame.Frame.fromNullable((await this._elementChannel.contentFrame()).frame);
|
||||||
|
}
|
||||||
|
async getAttribute(name) {
|
||||||
|
const value = (await this._elementChannel.getAttribute({
|
||||||
|
name
|
||||||
|
})).value;
|
||||||
|
return value === undefined ? null : value;
|
||||||
|
}
|
||||||
|
async inputValue() {
|
||||||
|
return (await this._elementChannel.inputValue()).value;
|
||||||
|
}
|
||||||
|
async textContent() {
|
||||||
|
const value = (await this._elementChannel.textContent()).value;
|
||||||
|
return value === undefined ? null : value;
|
||||||
|
}
|
||||||
|
async innerText() {
|
||||||
|
return (await this._elementChannel.innerText()).value;
|
||||||
|
}
|
||||||
|
async innerHTML() {
|
||||||
|
return (await this._elementChannel.innerHTML()).value;
|
||||||
|
}
|
||||||
|
async isChecked() {
|
||||||
|
return (await this._elementChannel.isChecked()).value;
|
||||||
|
}
|
||||||
|
async isDisabled() {
|
||||||
|
return (await this._elementChannel.isDisabled()).value;
|
||||||
|
}
|
||||||
|
async isEditable() {
|
||||||
|
return (await this._elementChannel.isEditable()).value;
|
||||||
|
}
|
||||||
|
async isEnabled() {
|
||||||
|
return (await this._elementChannel.isEnabled()).value;
|
||||||
|
}
|
||||||
|
async isHidden() {
|
||||||
|
return (await this._elementChannel.isHidden()).value;
|
||||||
|
}
|
||||||
|
async isVisible() {
|
||||||
|
return (await this._elementChannel.isVisible()).value;
|
||||||
|
}
|
||||||
|
async dispatchEvent(type, eventInit = {}) {
|
||||||
|
await this._elementChannel.dispatchEvent({
|
||||||
|
type,
|
||||||
|
eventInit: (0, _jsHandle.serializeArgument)(eventInit)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async scrollIntoViewIfNeeded(options = {}) {
|
||||||
|
await this._elementChannel.scrollIntoViewIfNeeded(options);
|
||||||
|
}
|
||||||
|
async hover(options = {}) {
|
||||||
|
await this._elementChannel.hover(options);
|
||||||
|
}
|
||||||
|
async click(options = {}) {
|
||||||
|
return await this._elementChannel.click(options);
|
||||||
|
}
|
||||||
|
async dblclick(options = {}) {
|
||||||
|
return await this._elementChannel.dblclick(options);
|
||||||
|
}
|
||||||
|
async tap(options = {}) {
|
||||||
|
return await this._elementChannel.tap(options);
|
||||||
|
}
|
||||||
|
async selectOption(values, options = {}) {
|
||||||
|
const result = await this._elementChannel.selectOption({
|
||||||
|
...convertSelectOptionValues(values),
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
return result.values;
|
||||||
|
}
|
||||||
|
async fill(value, options = {}) {
|
||||||
|
return await this._elementChannel.fill({
|
||||||
|
value,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async selectText(options = {}) {
|
||||||
|
await this._elementChannel.selectText(options);
|
||||||
|
}
|
||||||
|
async setInputFiles(files, options = {}) {
|
||||||
|
const frame = await this.ownerFrame();
|
||||||
|
if (!frame) throw new Error('Cannot set input files to detached element');
|
||||||
|
const converted = await convertInputFiles(files, frame.page().context());
|
||||||
|
await this._elementChannel.setInputFiles({
|
||||||
|
...converted,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async focus() {
|
||||||
|
await this._elementChannel.focus();
|
||||||
|
}
|
||||||
|
async type(text, options = {}) {
|
||||||
|
await this._elementChannel.type({
|
||||||
|
text,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async press(key, options = {}) {
|
||||||
|
await this._elementChannel.press({
|
||||||
|
key,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async check(options = {}) {
|
||||||
|
return await this._elementChannel.check(options);
|
||||||
|
}
|
||||||
|
async uncheck(options = {}) {
|
||||||
|
return await this._elementChannel.uncheck(options);
|
||||||
|
}
|
||||||
|
async setChecked(checked, options) {
|
||||||
|
if (checked) await this.check(options);else await this.uncheck(options);
|
||||||
|
}
|
||||||
|
async boundingBox() {
|
||||||
|
const value = (await this._elementChannel.boundingBox()).value;
|
||||||
|
return value === undefined ? null : value;
|
||||||
|
}
|
||||||
|
async screenshot(options = {}) {
|
||||||
|
const copy = {
|
||||||
|
...options,
|
||||||
|
mask: undefined
|
||||||
|
};
|
||||||
|
if (!copy.type) copy.type = determineScreenshotType(options);
|
||||||
|
if (options.mask) {
|
||||||
|
copy.mask = options.mask.map(locator => ({
|
||||||
|
frame: locator._frame._channel,
|
||||||
|
selector: locator._selector
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
const result = await this._elementChannel.screenshot(copy);
|
||||||
|
if (options.path) {
|
||||||
|
await (0, _fileUtils.mkdirIfNeeded)(options.path);
|
||||||
|
await _fs.default.promises.writeFile(options.path, result.binary);
|
||||||
|
}
|
||||||
|
return result.binary;
|
||||||
|
}
|
||||||
|
async $(selector) {
|
||||||
|
return ElementHandle.fromNullable((await this._elementChannel.querySelector({
|
||||||
|
selector
|
||||||
|
})).element);
|
||||||
|
}
|
||||||
|
async $$(selector) {
|
||||||
|
const result = await this._elementChannel.querySelectorAll({
|
||||||
|
selector
|
||||||
|
});
|
||||||
|
return result.elements.map(h => ElementHandle.from(h));
|
||||||
|
}
|
||||||
|
async $eval(selector, pageFunction, arg) {
|
||||||
|
const result = await this._elementChannel.evalOnSelector({
|
||||||
|
selector,
|
||||||
|
expression: String(pageFunction),
|
||||||
|
isFunction: typeof pageFunction === 'function',
|
||||||
|
arg: (0, _jsHandle.serializeArgument)(arg)
|
||||||
|
});
|
||||||
|
return (0, _jsHandle.parseResult)(result.value);
|
||||||
|
}
|
||||||
|
async $$eval(selector, pageFunction, arg) {
|
||||||
|
const result = await this._elementChannel.evalOnSelectorAll({
|
||||||
|
selector,
|
||||||
|
expression: String(pageFunction),
|
||||||
|
isFunction: typeof pageFunction === 'function',
|
||||||
|
arg: (0, _jsHandle.serializeArgument)(arg)
|
||||||
|
});
|
||||||
|
return (0, _jsHandle.parseResult)(result.value);
|
||||||
|
}
|
||||||
|
async waitForElementState(state, options = {}) {
|
||||||
|
return await this._elementChannel.waitForElementState({
|
||||||
|
state,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async waitForSelector(selector, options = {}) {
|
||||||
|
const result = await this._elementChannel.waitForSelector({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
return ElementHandle.fromNullable(result.element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.ElementHandle = ElementHandle;
|
||||||
|
function convertSelectOptionValues(values) {
|
||||||
|
if (values === null) return {};
|
||||||
|
if (!Array.isArray(values)) values = [values];
|
||||||
|
if (!values.length) return {};
|
||||||
|
for (let i = 0; i < values.length; i++) (0, _utils.assert)(values[i] !== null, `options[${i}]: expected object, got null`);
|
||||||
|
if (values[0] instanceof ElementHandle) return {
|
||||||
|
elements: values.map(v => v._elementChannel)
|
||||||
|
};
|
||||||
|
if ((0, _utils.isString)(values[0])) return {
|
||||||
|
options: values.map(valueOrLabel => ({
|
||||||
|
valueOrLabel
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
options: values
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function filePayloadExceedsSizeLimit(payloads) {
|
||||||
|
return payloads.reduce((size, item) => size + (item.buffer ? item.buffer.byteLength : 0), 0) >= _fileUtils.fileUploadSizeLimit;
|
||||||
|
}
|
||||||
|
async function resolvePathsAndDirectoryForInputFiles(items) {
|
||||||
|
var _localPaths2;
|
||||||
|
let localPaths;
|
||||||
|
let localDirectory;
|
||||||
|
for (const item of items) {
|
||||||
|
const stat = await _fs.default.promises.stat(item);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
if (localDirectory) throw new Error('Multiple directories are not supported');
|
||||||
|
localDirectory = _path.default.resolve(item);
|
||||||
|
} else {
|
||||||
|
var _localPaths;
|
||||||
|
(_localPaths = localPaths) !== null && _localPaths !== void 0 ? _localPaths : localPaths = [];
|
||||||
|
localPaths.push(_path.default.resolve(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((_localPaths2 = localPaths) !== null && _localPaths2 !== void 0 && _localPaths2.length && localDirectory) throw new Error('File paths must be all files or a single directory');
|
||||||
|
return [localPaths, localDirectory];
|
||||||
|
}
|
||||||
|
async function convertInputFiles(files, context) {
|
||||||
|
const items = Array.isArray(files) ? files.slice() : [files];
|
||||||
|
if (items.some(item => typeof item === 'string')) {
|
||||||
|
if (!items.every(item => typeof item === 'string')) throw new Error('File paths cannot be mixed with buffers');
|
||||||
|
const [localPaths, localDirectory] = await resolvePathsAndDirectoryForInputFiles(items);
|
||||||
|
if (context._connection.isRemote()) {
|
||||||
|
const files = localDirectory ? (await _fs.default.promises.readdir(localDirectory, {
|
||||||
|
withFileTypes: true,
|
||||||
|
recursive: true
|
||||||
|
})).filter(f => f.isFile()).map(f => _path.default.join(f.path, f.name)) : localPaths;
|
||||||
|
const {
|
||||||
|
writableStreams,
|
||||||
|
rootDir
|
||||||
|
} = await context._wrapApiCall(async () => context._channel.createTempFiles({
|
||||||
|
rootDirName: localDirectory ? _path.default.basename(localDirectory) : undefined,
|
||||||
|
items: await Promise.all(files.map(async file => {
|
||||||
|
const lastModifiedMs = (await _fs.default.promises.stat(file)).mtimeMs;
|
||||||
|
return {
|
||||||
|
name: localDirectory ? _path.default.relative(localDirectory, file) : _path.default.basename(file),
|
||||||
|
lastModifiedMs
|
||||||
|
};
|
||||||
|
}))
|
||||||
|
}), true);
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
const writable = _writableStream.WritableStream.from(writableStreams[i]);
|
||||||
|
await pipelineAsync(_fs.default.createReadStream(files[i]), writable.stream());
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
directoryStream: rootDir,
|
||||||
|
streams: localDirectory ? undefined : writableStreams
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
localPaths,
|
||||||
|
localDirectory
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const payloads = items;
|
||||||
|
if (filePayloadExceedsSizeLimit(payloads)) throw new Error('Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead.');
|
||||||
|
return {
|
||||||
|
payloads
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function determineScreenshotType(options) {
|
||||||
|
if (options.path) {
|
||||||
|
const mimeType = _utilsBundle.mime.getType(options.path);
|
||||||
|
if (mimeType === 'image/png') return 'png';else if (mimeType === 'image/jpeg') return 'jpeg';
|
||||||
|
throw new Error(`path: unsupported mime type "${mimeType}"`);
|
||||||
|
}
|
||||||
|
return options.type;
|
||||||
|
}
|
||||||
77
node_modules/playwright-core/lib/client/errors.js
generated
vendored
Normal file
77
node_modules/playwright-core/lib/client/errors.js
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.TimeoutError = exports.TargetClosedError = void 0;
|
||||||
|
exports.isTargetClosedError = isTargetClosedError;
|
||||||
|
exports.parseError = parseError;
|
||||||
|
exports.serializeError = serializeError;
|
||||||
|
var _utils = require("../utils");
|
||||||
|
var _serializers = require("../protocol/serializers");
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class TimeoutError extends Error {
|
||||||
|
constructor(message) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'TimeoutError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.TimeoutError = TimeoutError;
|
||||||
|
class TargetClosedError extends Error {
|
||||||
|
constructor(cause) {
|
||||||
|
super(cause || 'Target page, context or browser has been closed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.TargetClosedError = TargetClosedError;
|
||||||
|
function isTargetClosedError(error) {
|
||||||
|
return error instanceof TargetClosedError;
|
||||||
|
}
|
||||||
|
function serializeError(e) {
|
||||||
|
if ((0, _utils.isError)(e)) return {
|
||||||
|
error: {
|
||||||
|
message: e.message,
|
||||||
|
stack: e.stack,
|
||||||
|
name: e.name
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
value: (0, _serializers.serializeValue)(e, value => ({
|
||||||
|
fallThrough: value
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function parseError(error) {
|
||||||
|
if (!error.error) {
|
||||||
|
if (error.value === undefined) throw new Error('Serialized error must have either an error or a value');
|
||||||
|
return (0, _serializers.parseSerializedValue)(error.value, undefined);
|
||||||
|
}
|
||||||
|
if (error.error.name === 'TimeoutError') {
|
||||||
|
const e = new TimeoutError(error.error.message);
|
||||||
|
e.stack = error.error.stack || '';
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
if (error.error.name === 'TargetClosedError') {
|
||||||
|
const e = new TargetClosedError(error.error.message);
|
||||||
|
e.stack = error.error.stack || '';
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
const e = new Error(error.error.message);
|
||||||
|
e.stack = error.error.stack || '';
|
||||||
|
e.name = error.error.name;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
314
node_modules/playwright-core/lib/client/eventEmitter.js
generated
vendored
Normal file
314
node_modules/playwright-core/lib/client/eventEmitter.js
generated
vendored
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.EventEmitter = void 0;
|
||||||
|
var _events = require("events");
|
||||||
|
var _utils = require("../utils");
|
||||||
|
/**
|
||||||
|
* Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
* Modifications copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||||
|
* persons to whom the Software is furnished to do so, subject to the
|
||||||
|
* following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||||
|
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
|
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class EventEmitter {
|
||||||
|
constructor() {
|
||||||
|
this._events = undefined;
|
||||||
|
this._eventsCount = 0;
|
||||||
|
this._maxListeners = undefined;
|
||||||
|
this._pendingHandlers = new Map();
|
||||||
|
this._rejectionHandler = void 0;
|
||||||
|
if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) {
|
||||||
|
this._events = Object.create(null);
|
||||||
|
this._eventsCount = 0;
|
||||||
|
}
|
||||||
|
this._maxListeners = this._maxListeners || undefined;
|
||||||
|
this.on = this.addListener;
|
||||||
|
this.off = this.removeListener;
|
||||||
|
}
|
||||||
|
setMaxListeners(n) {
|
||||||
|
if (typeof n !== 'number' || n < 0 || Number.isNaN(n)) throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
|
||||||
|
this._maxListeners = n;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
getMaxListeners() {
|
||||||
|
return this._maxListeners === undefined ? _events.EventEmitter.defaultMaxListeners : this._maxListeners;
|
||||||
|
}
|
||||||
|
emit(type, ...args) {
|
||||||
|
const events = this._events;
|
||||||
|
if (events === undefined) return false;
|
||||||
|
const handler = events === null || events === void 0 ? void 0 : events[type];
|
||||||
|
if (handler === undefined) return false;
|
||||||
|
if (typeof handler === 'function') {
|
||||||
|
this._callHandler(type, handler, args);
|
||||||
|
} else {
|
||||||
|
const len = handler.length;
|
||||||
|
const listeners = handler.slice();
|
||||||
|
for (let i = 0; i < len; ++i) this._callHandler(type, listeners[i], args);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
_callHandler(type, handler, args) {
|
||||||
|
const promise = Reflect.apply(handler, this, args);
|
||||||
|
if (!(promise instanceof Promise)) return;
|
||||||
|
let set = this._pendingHandlers.get(type);
|
||||||
|
if (!set) {
|
||||||
|
set = new Set();
|
||||||
|
this._pendingHandlers.set(type, set);
|
||||||
|
}
|
||||||
|
set.add(promise);
|
||||||
|
promise.catch(e => {
|
||||||
|
if (this._rejectionHandler) this._rejectionHandler(e);else throw e;
|
||||||
|
}).finally(() => set.delete(promise));
|
||||||
|
}
|
||||||
|
addListener(type, listener) {
|
||||||
|
return this._addListener(type, listener, false);
|
||||||
|
}
|
||||||
|
on(type, listener) {
|
||||||
|
return this._addListener(type, listener, false);
|
||||||
|
}
|
||||||
|
_addListener(type, listener, prepend) {
|
||||||
|
checkListener(listener);
|
||||||
|
let events = this._events;
|
||||||
|
let existing;
|
||||||
|
if (events === undefined) {
|
||||||
|
events = this._events = Object.create(null);
|
||||||
|
this._eventsCount = 0;
|
||||||
|
} else {
|
||||||
|
// To avoid recursion in the case that type === "newListener"! Before
|
||||||
|
// adding it to the listeners, first emit "newListener".
|
||||||
|
if (events.newListener !== undefined) {
|
||||||
|
this.emit('newListener', type, unwrapListener(listener));
|
||||||
|
|
||||||
|
// Re-assign `events` because a newListener handler could have caused the
|
||||||
|
// this._events to be assigned to a new object
|
||||||
|
events = this._events;
|
||||||
|
}
|
||||||
|
existing = events[type];
|
||||||
|
}
|
||||||
|
if (existing === undefined) {
|
||||||
|
// Optimize the case of one listener. Don't need the extra array object.
|
||||||
|
existing = events[type] = listener;
|
||||||
|
++this._eventsCount;
|
||||||
|
} else {
|
||||||
|
if (typeof existing === 'function') {
|
||||||
|
// Adding the second element, need to change to array.
|
||||||
|
existing = events[type] = prepend ? [listener, existing] : [existing, listener];
|
||||||
|
// If we've already got an array, just append.
|
||||||
|
} else if (prepend) {
|
||||||
|
existing.unshift(listener);
|
||||||
|
} else {
|
||||||
|
existing.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for listener leak
|
||||||
|
const m = this.getMaxListeners();
|
||||||
|
if (m > 0 && existing.length > m && !existing.warned) {
|
||||||
|
existing.warned = true;
|
||||||
|
// No error code for this since it is a Warning
|
||||||
|
const w = new Error('Possible EventEmitter memory leak detected. ' + existing.length + ' ' + String(type) + ' listeners ' + 'added. Use emitter.setMaxListeners() to ' + 'increase limit');
|
||||||
|
w.name = 'MaxListenersExceededWarning';
|
||||||
|
w.emitter = this;
|
||||||
|
w.type = type;
|
||||||
|
w.count = existing.length;
|
||||||
|
if (!(0, _utils.isUnderTest)()) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
prependListener(type, listener) {
|
||||||
|
return this._addListener(type, listener, true);
|
||||||
|
}
|
||||||
|
once(type, listener) {
|
||||||
|
checkListener(listener);
|
||||||
|
this.on(type, new OnceWrapper(this, type, listener).wrapperFunction);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
prependOnceListener(type, listener) {
|
||||||
|
checkListener(listener);
|
||||||
|
this.prependListener(type, new OnceWrapper(this, type, listener).wrapperFunction);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
removeListener(type, listener) {
|
||||||
|
checkListener(listener);
|
||||||
|
const events = this._events;
|
||||||
|
if (events === undefined) return this;
|
||||||
|
const list = events[type];
|
||||||
|
if (list === undefined) return this;
|
||||||
|
if (list === listener || list.listener === listener) {
|
||||||
|
if (--this._eventsCount === 0) {
|
||||||
|
this._events = Object.create(null);
|
||||||
|
} else {
|
||||||
|
var _listener;
|
||||||
|
delete events[type];
|
||||||
|
if (events.removeListener) this.emit('removeListener', type, (_listener = list.listener) !== null && _listener !== void 0 ? _listener : listener);
|
||||||
|
}
|
||||||
|
} else if (typeof list !== 'function') {
|
||||||
|
let position = -1;
|
||||||
|
let originalListener;
|
||||||
|
for (let i = list.length - 1; i >= 0; i--) {
|
||||||
|
if (list[i] === listener || wrappedListener(list[i]) === listener) {
|
||||||
|
originalListener = wrappedListener(list[i]);
|
||||||
|
position = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (position < 0) return this;
|
||||||
|
if (position === 0) list.shift();else list.splice(position, 1);
|
||||||
|
if (list.length === 1) events[type] = list[0];
|
||||||
|
if (events.removeListener !== undefined) this.emit('removeListener', type, originalListener || listener);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
off(type, listener) {
|
||||||
|
return this.removeListener(type, listener);
|
||||||
|
}
|
||||||
|
removeAllListeners(type, options) {
|
||||||
|
this._removeAllListeners(type);
|
||||||
|
if (!options) return this;
|
||||||
|
if (options.behavior === 'wait') {
|
||||||
|
const errors = [];
|
||||||
|
this._rejectionHandler = error => errors.push(error);
|
||||||
|
// eslint-disable-next-line internal-playwright/await-promise-in-class-returns
|
||||||
|
return this._waitFor(type).then(() => {
|
||||||
|
if (errors.length) throw errors[0];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (options.behavior === 'ignoreErrors') this._rejectionHandler = () => {};
|
||||||
|
|
||||||
|
// eslint-disable-next-line internal-playwright/await-promise-in-class-returns
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
_removeAllListeners(type) {
|
||||||
|
const events = this._events;
|
||||||
|
if (!events) return;
|
||||||
|
|
||||||
|
// not listening for removeListener, no need to emit
|
||||||
|
if (!events.removeListener) {
|
||||||
|
if (type === undefined) {
|
||||||
|
this._events = Object.create(null);
|
||||||
|
this._eventsCount = 0;
|
||||||
|
} else if (events[type] !== undefined) {
|
||||||
|
if (--this._eventsCount === 0) this._events = Object.create(null);else delete events[type];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// emit removeListener for all listeners on all events
|
||||||
|
if (type === undefined) {
|
||||||
|
const keys = Object.keys(events);
|
||||||
|
let key;
|
||||||
|
for (let i = 0; i < keys.length; ++i) {
|
||||||
|
key = keys[i];
|
||||||
|
if (key === 'removeListener') continue;
|
||||||
|
this._removeAllListeners(key);
|
||||||
|
}
|
||||||
|
this._removeAllListeners('removeListener');
|
||||||
|
this._events = Object.create(null);
|
||||||
|
this._eventsCount = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const listeners = events[type];
|
||||||
|
if (typeof listeners === 'function') {
|
||||||
|
this.removeListener(type, listeners);
|
||||||
|
} else if (listeners !== undefined) {
|
||||||
|
// LIFO order
|
||||||
|
for (let i = listeners.length - 1; i >= 0; i--) this.removeListener(type, listeners[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listeners(type) {
|
||||||
|
return this._listeners(this, type, true);
|
||||||
|
}
|
||||||
|
rawListeners(type) {
|
||||||
|
return this._listeners(this, type, false);
|
||||||
|
}
|
||||||
|
listenerCount(type) {
|
||||||
|
const events = this._events;
|
||||||
|
if (events !== undefined) {
|
||||||
|
const listener = events[type];
|
||||||
|
if (typeof listener === 'function') return 1;
|
||||||
|
if (listener !== undefined) return listener.length;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
eventNames() {
|
||||||
|
return this._eventsCount > 0 && this._events ? Reflect.ownKeys(this._events) : [];
|
||||||
|
}
|
||||||
|
async _waitFor(type) {
|
||||||
|
let promises = [];
|
||||||
|
if (type) {
|
||||||
|
promises = [...(this._pendingHandlers.get(type) || [])];
|
||||||
|
} else {
|
||||||
|
promises = [];
|
||||||
|
for (const [, pending] of this._pendingHandlers) promises.push(...pending);
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
_listeners(target, type, unwrap) {
|
||||||
|
const events = target._events;
|
||||||
|
if (events === undefined) return [];
|
||||||
|
const listener = events[type];
|
||||||
|
if (listener === undefined) return [];
|
||||||
|
if (typeof listener === 'function') return unwrap ? [unwrapListener(listener)] : [listener];
|
||||||
|
return unwrap ? unwrapListeners(listener) : listener.slice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.EventEmitter = EventEmitter;
|
||||||
|
function checkListener(listener) {
|
||||||
|
if (typeof listener !== 'function') throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
|
||||||
|
}
|
||||||
|
class OnceWrapper {
|
||||||
|
constructor(eventEmitter, eventType, listener) {
|
||||||
|
this._fired = false;
|
||||||
|
this.wrapperFunction = void 0;
|
||||||
|
this._listener = void 0;
|
||||||
|
this._eventEmitter = void 0;
|
||||||
|
this._eventType = void 0;
|
||||||
|
this._eventEmitter = eventEmitter;
|
||||||
|
this._eventType = eventType;
|
||||||
|
this._listener = listener;
|
||||||
|
this.wrapperFunction = this._handle.bind(this);
|
||||||
|
this.wrapperFunction.listener = listener;
|
||||||
|
}
|
||||||
|
_handle(...args) {
|
||||||
|
if (this._fired) return;
|
||||||
|
this._fired = true;
|
||||||
|
this._eventEmitter.removeListener(this._eventType, this.wrapperFunction);
|
||||||
|
return this._listener.apply(this._eventEmitter, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function unwrapListener(l) {
|
||||||
|
var _wrappedListener;
|
||||||
|
return (_wrappedListener = wrappedListener(l)) !== null && _wrappedListener !== void 0 ? _wrappedListener : l;
|
||||||
|
}
|
||||||
|
function unwrapListeners(arr) {
|
||||||
|
return arr.map(l => {
|
||||||
|
var _wrappedListener2;
|
||||||
|
return (_wrappedListener2 = wrappedListener(l)) !== null && _wrappedListener2 !== void 0 ? _wrappedListener2 : l;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function wrappedListener(l) {
|
||||||
|
return l.listener;
|
||||||
|
}
|
||||||
94
node_modules/playwright-core/lib/client/events.js
generated
vendored
Normal file
94
node_modules/playwright-core/lib/client/events.js
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.Events = void 0;
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Google Inc. All rights reserved.
|
||||||
|
* Modifications copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Events = exports.Events = {
|
||||||
|
AndroidDevice: {
|
||||||
|
WebView: 'webview',
|
||||||
|
Close: 'close'
|
||||||
|
},
|
||||||
|
AndroidSocket: {
|
||||||
|
Data: 'data',
|
||||||
|
Close: 'close'
|
||||||
|
},
|
||||||
|
AndroidWebView: {
|
||||||
|
Close: 'close'
|
||||||
|
},
|
||||||
|
Browser: {
|
||||||
|
Disconnected: 'disconnected'
|
||||||
|
},
|
||||||
|
BrowserContext: {
|
||||||
|
Console: 'console',
|
||||||
|
Close: 'close',
|
||||||
|
Dialog: 'dialog',
|
||||||
|
Page: 'page',
|
||||||
|
// Can't use just 'error' due to node.js special treatment of error events.
|
||||||
|
// @see https://nodejs.org/api/events.html#events_error_events
|
||||||
|
WebError: 'weberror',
|
||||||
|
BackgroundPage: 'backgroundpage',
|
||||||
|
ServiceWorker: 'serviceworker',
|
||||||
|
Request: 'request',
|
||||||
|
Response: 'response',
|
||||||
|
RequestFailed: 'requestfailed',
|
||||||
|
RequestFinished: 'requestfinished'
|
||||||
|
},
|
||||||
|
BrowserServer: {
|
||||||
|
Close: 'close'
|
||||||
|
},
|
||||||
|
Page: {
|
||||||
|
Close: 'close',
|
||||||
|
Crash: 'crash',
|
||||||
|
Console: 'console',
|
||||||
|
Dialog: 'dialog',
|
||||||
|
Download: 'download',
|
||||||
|
FileChooser: 'filechooser',
|
||||||
|
DOMContentLoaded: 'domcontentloaded',
|
||||||
|
// Can't use just 'error' due to node.js special treatment of error events.
|
||||||
|
// @see https://nodejs.org/api/events.html#events_error_events
|
||||||
|
PageError: 'pageerror',
|
||||||
|
Request: 'request',
|
||||||
|
Response: 'response',
|
||||||
|
RequestFailed: 'requestfailed',
|
||||||
|
RequestFinished: 'requestfinished',
|
||||||
|
FrameAttached: 'frameattached',
|
||||||
|
FrameDetached: 'framedetached',
|
||||||
|
FrameNavigated: 'framenavigated',
|
||||||
|
Load: 'load',
|
||||||
|
Popup: 'popup',
|
||||||
|
WebSocket: 'websocket',
|
||||||
|
Worker: 'worker'
|
||||||
|
},
|
||||||
|
WebSocket: {
|
||||||
|
Close: 'close',
|
||||||
|
Error: 'socketerror',
|
||||||
|
FrameReceived: 'framereceived',
|
||||||
|
FrameSent: 'framesent'
|
||||||
|
},
|
||||||
|
Worker: {
|
||||||
|
Close: 'close'
|
||||||
|
},
|
||||||
|
ElectronApplication: {
|
||||||
|
Close: 'close',
|
||||||
|
Console: 'console',
|
||||||
|
Window: 'window'
|
||||||
|
}
|
||||||
|
};
|
||||||
391
node_modules/playwright-core/lib/client/fetch.js
generated
vendored
Normal file
391
node_modules/playwright-core/lib/client/fetch.js
generated
vendored
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.APIResponse = exports.APIRequestContext = exports.APIRequest = void 0;
|
||||||
|
var _fs = _interopRequireDefault(require("fs"));
|
||||||
|
var _path = _interopRequireDefault(require("path"));
|
||||||
|
var util = _interopRequireWildcard(require("util"));
|
||||||
|
var _utils = require("../utils");
|
||||||
|
var _fileUtils = require("../utils/fileUtils");
|
||||||
|
var _channelOwner = require("./channelOwner");
|
||||||
|
var _network = require("./network");
|
||||||
|
var _tracing = require("./tracing");
|
||||||
|
var _errors = require("./errors");
|
||||||
|
var _browserContext = require("./browserContext");
|
||||||
|
let _Symbol$asyncDispose, _Symbol$asyncDispose2, _util$inspect$custom;
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||||
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
class APIRequest {
|
||||||
|
constructor(playwright) {
|
||||||
|
this._playwright = void 0;
|
||||||
|
this._contexts = new Set();
|
||||||
|
// Instrumentation.
|
||||||
|
this._defaultContextOptions = void 0;
|
||||||
|
this._playwright = playwright;
|
||||||
|
}
|
||||||
|
async newContext(options = {}) {
|
||||||
|
var _this$_defaultContext;
|
||||||
|
options = {
|
||||||
|
...this._defaultContextOptions,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
const storageState = typeof options.storageState === 'string' ? JSON.parse(await _fs.default.promises.readFile(options.storageState, 'utf8')) : options.storageState;
|
||||||
|
// We do not expose tracesDir in the API, so do not allow options to accidentally override it.
|
||||||
|
const tracesDir = (_this$_defaultContext = this._defaultContextOptions) === null || _this$_defaultContext === void 0 ? void 0 : _this$_defaultContext.tracesDir;
|
||||||
|
const context = APIRequestContext.from((await this._playwright._channel.newRequest({
|
||||||
|
...options,
|
||||||
|
extraHTTPHeaders: options.extraHTTPHeaders ? (0, _utils.headersObjectToArray)(options.extraHTTPHeaders) : undefined,
|
||||||
|
storageState,
|
||||||
|
tracesDir,
|
||||||
|
clientCertificates: await (0, _browserContext.toClientCertificatesProtocol)(options.clientCertificates)
|
||||||
|
})).request);
|
||||||
|
this._contexts.add(context);
|
||||||
|
context._request = this;
|
||||||
|
context._tracing._tracesDir = tracesDir;
|
||||||
|
await context._instrumentation.runAfterCreateRequestContext(context);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.APIRequest = APIRequest;
|
||||||
|
_Symbol$asyncDispose = Symbol.asyncDispose;
|
||||||
|
class APIRequestContext extends _channelOwner.ChannelOwner {
|
||||||
|
static from(channel) {
|
||||||
|
return channel._object;
|
||||||
|
}
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
|
this._request = void 0;
|
||||||
|
this._tracing = void 0;
|
||||||
|
this._closeReason = void 0;
|
||||||
|
this._tracing = _tracing.Tracing.from(initializer.tracing);
|
||||||
|
}
|
||||||
|
async [_Symbol$asyncDispose]() {
|
||||||
|
await this.dispose();
|
||||||
|
}
|
||||||
|
async dispose(options = {}) {
|
||||||
|
var _this$_request;
|
||||||
|
this._closeReason = options.reason;
|
||||||
|
await this._instrumentation.runBeforeCloseRequestContext(this);
|
||||||
|
try {
|
||||||
|
await this._channel.dispose(options);
|
||||||
|
} catch (e) {
|
||||||
|
if ((0, _errors.isTargetClosedError)(e)) return;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
this._tracing._resetStackCounter();
|
||||||
|
(_this$_request = this._request) === null || _this$_request === void 0 || _this$_request._contexts.delete(this);
|
||||||
|
}
|
||||||
|
async delete(url, options) {
|
||||||
|
return await this.fetch(url, {
|
||||||
|
...options,
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async head(url, options) {
|
||||||
|
return await this.fetch(url, {
|
||||||
|
...options,
|
||||||
|
method: 'HEAD'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async get(url, options) {
|
||||||
|
return await this.fetch(url, {
|
||||||
|
...options,
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async patch(url, options) {
|
||||||
|
return await this.fetch(url, {
|
||||||
|
...options,
|
||||||
|
method: 'PATCH'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async post(url, options) {
|
||||||
|
return await this.fetch(url, {
|
||||||
|
...options,
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async put(url, options) {
|
||||||
|
return await this.fetch(url, {
|
||||||
|
...options,
|
||||||
|
method: 'PUT'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async fetch(urlOrRequest, options = {}) {
|
||||||
|
const url = (0, _utils.isString)(urlOrRequest) ? urlOrRequest : undefined;
|
||||||
|
const request = (0, _utils.isString)(urlOrRequest) ? undefined : urlOrRequest;
|
||||||
|
return await this._innerFetch({
|
||||||
|
url,
|
||||||
|
request,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async _innerFetch(options = {}) {
|
||||||
|
return await this._wrapApiCall(async () => {
|
||||||
|
var _options$request, _options$request2, _options$request3;
|
||||||
|
if (this._closeReason) throw new _errors.TargetClosedError(this._closeReason);
|
||||||
|
(0, _utils.assert)(options.request || typeof options.url === 'string', 'First argument must be either URL string or Request');
|
||||||
|
(0, _utils.assert)((options.data === undefined ? 0 : 1) + (options.form === undefined ? 0 : 1) + (options.multipart === undefined ? 0 : 1) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`);
|
||||||
|
(0, _utils.assert)(options.maxRedirects === undefined || options.maxRedirects >= 0, `'maxRedirects' must be greater than or equal to '0'`);
|
||||||
|
(0, _utils.assert)(options.maxRetries === undefined || options.maxRetries >= 0, `'maxRetries' must be greater than or equal to '0'`);
|
||||||
|
const url = options.url !== undefined ? options.url : options.request.url();
|
||||||
|
const method = options.method || ((_options$request = options.request) === null || _options$request === void 0 ? void 0 : _options$request.method());
|
||||||
|
let encodedParams = undefined;
|
||||||
|
if (typeof options.params === 'string') encodedParams = options.params;else if (options.params instanceof URLSearchParams) encodedParams = options.params.toString();
|
||||||
|
// Cannot call allHeaders() here as the request may be paused inside route handler.
|
||||||
|
const headersObj = options.headers || ((_options$request2 = options.request) === null || _options$request2 === void 0 ? void 0 : _options$request2.headers());
|
||||||
|
const headers = headersObj ? (0, _utils.headersObjectToArray)(headersObj) : undefined;
|
||||||
|
let jsonData;
|
||||||
|
let formData;
|
||||||
|
let multipartData;
|
||||||
|
let postDataBuffer;
|
||||||
|
if (options.data !== undefined) {
|
||||||
|
if ((0, _utils.isString)(options.data)) {
|
||||||
|
if (isJsonContentType(headers)) jsonData = isJsonParsable(options.data) ? options.data : JSON.stringify(options.data);else postDataBuffer = Buffer.from(options.data, 'utf8');
|
||||||
|
} else if (Buffer.isBuffer(options.data)) {
|
||||||
|
postDataBuffer = options.data;
|
||||||
|
} else if (typeof options.data === 'object' || typeof options.data === 'number' || typeof options.data === 'boolean') {
|
||||||
|
jsonData = JSON.stringify(options.data);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unexpected 'data' type`);
|
||||||
|
}
|
||||||
|
} else if (options.form) {
|
||||||
|
if (globalThis.FormData && options.form instanceof FormData) {
|
||||||
|
formData = [];
|
||||||
|
for (const [name, value] of options.form.entries()) {
|
||||||
|
if (typeof value !== 'string') throw new Error(`Expected string for options.form["${name}"], found File. Please use options.multipart instead.`);
|
||||||
|
formData.push({
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
formData = objectToArray(options.form);
|
||||||
|
}
|
||||||
|
} else if (options.multipart) {
|
||||||
|
multipartData = [];
|
||||||
|
if (globalThis.FormData && options.multipart instanceof FormData) {
|
||||||
|
const form = options.multipart;
|
||||||
|
for (const [name, value] of form.entries()) {
|
||||||
|
if ((0, _utils.isString)(value)) {
|
||||||
|
multipartData.push({
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const file = {
|
||||||
|
name: value.name,
|
||||||
|
mimeType: value.type,
|
||||||
|
buffer: Buffer.from(await value.arrayBuffer())
|
||||||
|
};
|
||||||
|
multipartData.push({
|
||||||
|
name,
|
||||||
|
file
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Convert file-like values to ServerFilePayload structs.
|
||||||
|
for (const [name, value] of Object.entries(options.multipart)) multipartData.push(await toFormField(name, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (postDataBuffer === undefined && jsonData === undefined && formData === undefined && multipartData === undefined) postDataBuffer = ((_options$request3 = options.request) === null || _options$request3 === void 0 ? void 0 : _options$request3.postDataBuffer()) || undefined;
|
||||||
|
const fixtures = {
|
||||||
|
__testHookLookup: options.__testHookLookup
|
||||||
|
};
|
||||||
|
const result = await this._channel.fetch({
|
||||||
|
url,
|
||||||
|
params: typeof options.params === 'object' ? objectToArray(options.params) : undefined,
|
||||||
|
encodedParams,
|
||||||
|
method,
|
||||||
|
headers,
|
||||||
|
postData: postDataBuffer,
|
||||||
|
jsonData,
|
||||||
|
formData,
|
||||||
|
multipartData,
|
||||||
|
timeout: options.timeout,
|
||||||
|
failOnStatusCode: options.failOnStatusCode,
|
||||||
|
ignoreHTTPSErrors: options.ignoreHTTPSErrors,
|
||||||
|
maxRedirects: options.maxRedirects,
|
||||||
|
maxRetries: options.maxRetries,
|
||||||
|
...fixtures
|
||||||
|
});
|
||||||
|
return new APIResponse(this, result.response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async storageState(options = {}) {
|
||||||
|
const state = await this._channel.storageState();
|
||||||
|
if (options.path) {
|
||||||
|
await (0, _fileUtils.mkdirIfNeeded)(options.path);
|
||||||
|
await _fs.default.promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8');
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.APIRequestContext = APIRequestContext;
|
||||||
|
async function toFormField(name, value) {
|
||||||
|
if (isFilePayload(value)) {
|
||||||
|
const payload = value;
|
||||||
|
if (!Buffer.isBuffer(payload.buffer)) throw new Error(`Unexpected buffer type of 'data.${name}'`);
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
file: filePayloadToJson(payload)
|
||||||
|
};
|
||||||
|
} else if (value instanceof _fs.default.ReadStream) {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
file: await readStreamToJson(value)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
value: String(value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function isJsonParsable(value) {
|
||||||
|
if (typeof value !== 'string') return false;
|
||||||
|
try {
|
||||||
|
JSON.parse(value);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof SyntaxError) return false;else throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_Symbol$asyncDispose2 = Symbol.asyncDispose;
|
||||||
|
_util$inspect$custom = util.inspect.custom;
|
||||||
|
class APIResponse {
|
||||||
|
constructor(context, initializer) {
|
||||||
|
this._initializer = void 0;
|
||||||
|
this._headers = void 0;
|
||||||
|
this._request = void 0;
|
||||||
|
this._request = context;
|
||||||
|
this._initializer = initializer;
|
||||||
|
this._headers = new _network.RawHeaders(this._initializer.headers);
|
||||||
|
}
|
||||||
|
ok() {
|
||||||
|
return this._initializer.status >= 200 && this._initializer.status <= 299;
|
||||||
|
}
|
||||||
|
url() {
|
||||||
|
return this._initializer.url;
|
||||||
|
}
|
||||||
|
status() {
|
||||||
|
return this._initializer.status;
|
||||||
|
}
|
||||||
|
statusText() {
|
||||||
|
return this._initializer.statusText;
|
||||||
|
}
|
||||||
|
headers() {
|
||||||
|
return this._headers.headers();
|
||||||
|
}
|
||||||
|
headersArray() {
|
||||||
|
return this._headers.headersArray();
|
||||||
|
}
|
||||||
|
async body() {
|
||||||
|
try {
|
||||||
|
const result = await this._request._channel.fetchResponseBody({
|
||||||
|
fetchUid: this._fetchUid()
|
||||||
|
});
|
||||||
|
if (result.binary === undefined) throw new Error('Response has been disposed');
|
||||||
|
return result.binary;
|
||||||
|
} catch (e) {
|
||||||
|
if ((0, _errors.isTargetClosedError)(e)) throw new Error('Response has been disposed');
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async text() {
|
||||||
|
const content = await this.body();
|
||||||
|
return content.toString('utf8');
|
||||||
|
}
|
||||||
|
async json() {
|
||||||
|
const content = await this.text();
|
||||||
|
return JSON.parse(content);
|
||||||
|
}
|
||||||
|
async [_Symbol$asyncDispose2]() {
|
||||||
|
await this.dispose();
|
||||||
|
}
|
||||||
|
async dispose() {
|
||||||
|
await this._request._channel.disposeAPIResponse({
|
||||||
|
fetchUid: this._fetchUid()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
[_util$inspect$custom]() {
|
||||||
|
const headers = this.headersArray().map(({
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
}) => ` ${name}: ${value}`);
|
||||||
|
return `APIResponse: ${this.status()} ${this.statusText()}\n${headers.join('\n')}`;
|
||||||
|
}
|
||||||
|
_fetchUid() {
|
||||||
|
return this._initializer.fetchUid;
|
||||||
|
}
|
||||||
|
async _fetchLog() {
|
||||||
|
const {
|
||||||
|
log
|
||||||
|
} = await this._request._channel.fetchLog({
|
||||||
|
fetchUid: this._fetchUid()
|
||||||
|
});
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.APIResponse = APIResponse;
|
||||||
|
function filePayloadToJson(payload) {
|
||||||
|
return {
|
||||||
|
name: payload.name,
|
||||||
|
mimeType: payload.mimeType,
|
||||||
|
buffer: payload.buffer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async function readStreamToJson(stream) {
|
||||||
|
const buffer = await new Promise((resolve, reject) => {
|
||||||
|
const chunks = [];
|
||||||
|
stream.on('data', chunk => chunks.push(chunk));
|
||||||
|
stream.on('end', () => resolve(Buffer.concat(chunks)));
|
||||||
|
stream.on('error', err => reject(err));
|
||||||
|
});
|
||||||
|
const streamPath = Buffer.isBuffer(stream.path) ? stream.path.toString('utf8') : stream.path;
|
||||||
|
return {
|
||||||
|
name: _path.default.basename(streamPath),
|
||||||
|
buffer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function isJsonContentType(headers) {
|
||||||
|
if (!headers) return false;
|
||||||
|
for (const {
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
} of headers) {
|
||||||
|
if (name.toLocaleLowerCase() === 'content-type') return value === 'application/json';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function objectToArray(map) {
|
||||||
|
if (!map) return undefined;
|
||||||
|
const result = [];
|
||||||
|
for (const [name, value] of Object.entries(map)) result.push({
|
||||||
|
name,
|
||||||
|
value: String(value)
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
function isFilePayload(value) {
|
||||||
|
return typeof value === 'object' && value['name'] && value['mimeType'] && value['buffer'];
|
||||||
|
}
|
||||||
45
node_modules/playwright-core/lib/client/fileChooser.js
generated
vendored
Normal file
45
node_modules/playwright-core/lib/client/fileChooser.js
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.FileChooser = void 0;
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class FileChooser {
|
||||||
|
constructor(page, elementHandle, isMultiple) {
|
||||||
|
this._page = void 0;
|
||||||
|
this._elementHandle = void 0;
|
||||||
|
this._isMultiple = void 0;
|
||||||
|
this._page = page;
|
||||||
|
this._elementHandle = elementHandle;
|
||||||
|
this._isMultiple = isMultiple;
|
||||||
|
}
|
||||||
|
element() {
|
||||||
|
return this._elementHandle;
|
||||||
|
}
|
||||||
|
isMultiple() {
|
||||||
|
return this._isMultiple;
|
||||||
|
}
|
||||||
|
page() {
|
||||||
|
return this._page;
|
||||||
|
}
|
||||||
|
async setFiles(files, options) {
|
||||||
|
return await this._elementHandle.setInputFiles(files, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.FileChooser = FileChooser;
|
||||||
504
node_modules/playwright-core/lib/client/frame.js
generated
vendored
Normal file
504
node_modules/playwright-core/lib/client/frame.js
generated
vendored
Normal file
@@ -0,0 +1,504 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.Frame = void 0;
|
||||||
|
exports.verifyLoadState = verifyLoadState;
|
||||||
|
var _utils = require("../utils");
|
||||||
|
var _channelOwner = require("./channelOwner");
|
||||||
|
var _locator = require("./locator");
|
||||||
|
var _locatorUtils = require("../utils/isomorphic/locatorUtils");
|
||||||
|
var _elementHandle = require("./elementHandle");
|
||||||
|
var _jsHandle = require("./jsHandle");
|
||||||
|
var _fs = _interopRequireDefault(require("fs"));
|
||||||
|
var network = _interopRequireWildcard(require("./network"));
|
||||||
|
var _events = require("events");
|
||||||
|
var _waiter = require("./waiter");
|
||||||
|
var _events2 = require("./events");
|
||||||
|
var _types = require("./types");
|
||||||
|
var _clientHelper = require("./clientHelper");
|
||||||
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||||
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
/**
|
||||||
|
* Copyright 2017 Google Inc. All rights reserved.
|
||||||
|
* Modifications copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Frame extends _channelOwner.ChannelOwner {
|
||||||
|
static from(frame) {
|
||||||
|
return frame._object;
|
||||||
|
}
|
||||||
|
static fromNullable(frame) {
|
||||||
|
return frame ? Frame.from(frame) : null;
|
||||||
|
}
|
||||||
|
constructor(parent, type, guid, initializer) {
|
||||||
|
super(parent, type, guid, initializer);
|
||||||
|
this._eventEmitter = void 0;
|
||||||
|
this._loadStates = void 0;
|
||||||
|
this._parentFrame = null;
|
||||||
|
this._url = '';
|
||||||
|
this._name = '';
|
||||||
|
this._detached = false;
|
||||||
|
this._childFrames = new Set();
|
||||||
|
this._page = void 0;
|
||||||
|
this._eventEmitter = new _events.EventEmitter();
|
||||||
|
this._eventEmitter.setMaxListeners(0);
|
||||||
|
this._parentFrame = Frame.fromNullable(initializer.parentFrame);
|
||||||
|
if (this._parentFrame) this._parentFrame._childFrames.add(this);
|
||||||
|
this._name = initializer.name;
|
||||||
|
this._url = initializer.url;
|
||||||
|
this._loadStates = new Set(initializer.loadStates);
|
||||||
|
this._channel.on('loadstate', event => {
|
||||||
|
if (event.add) {
|
||||||
|
this._loadStates.add(event.add);
|
||||||
|
this._eventEmitter.emit('loadstate', event.add);
|
||||||
|
}
|
||||||
|
if (event.remove) this._loadStates.delete(event.remove);
|
||||||
|
if (!this._parentFrame && event.add === 'load' && this._page) this._page.emit(_events2.Events.Page.Load, this._page);
|
||||||
|
if (!this._parentFrame && event.add === 'domcontentloaded' && this._page) this._page.emit(_events2.Events.Page.DOMContentLoaded, this._page);
|
||||||
|
});
|
||||||
|
this._channel.on('navigated', event => {
|
||||||
|
this._url = event.url;
|
||||||
|
this._name = event.name;
|
||||||
|
this._eventEmitter.emit('navigated', event);
|
||||||
|
if (!event.error && this._page) this._page.emit(_events2.Events.Page.FrameNavigated, this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
page() {
|
||||||
|
return this._page;
|
||||||
|
}
|
||||||
|
async goto(url, options = {}) {
|
||||||
|
const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
||||||
|
return network.Response.fromNullable((await this._channel.goto({
|
||||||
|
url,
|
||||||
|
...options,
|
||||||
|
waitUntil
|
||||||
|
})).response);
|
||||||
|
}
|
||||||
|
_setupNavigationWaiter(options) {
|
||||||
|
const waiter = new _waiter.Waiter(this._page, '');
|
||||||
|
if (this._page.isClosed()) waiter.rejectImmediately(this._page._closeErrorWithReason());
|
||||||
|
waiter.rejectOnEvent(this._page, _events2.Events.Page.Close, () => this._page._closeErrorWithReason());
|
||||||
|
waiter.rejectOnEvent(this._page, _events2.Events.Page.Crash, new Error('Navigation failed because page crashed!'));
|
||||||
|
waiter.rejectOnEvent(this._page, _events2.Events.Page.FrameDetached, new Error('Navigating frame was detached!'), frame => frame === this);
|
||||||
|
const timeout = this._page._timeoutSettings.navigationTimeout(options);
|
||||||
|
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded.`);
|
||||||
|
return waiter;
|
||||||
|
}
|
||||||
|
async waitForNavigation(options = {}) {
|
||||||
|
return await this._page._wrapApiCall(async () => {
|
||||||
|
const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
||||||
|
const waiter = this._setupNavigationWaiter(options);
|
||||||
|
const toUrl = typeof options.url === 'string' ? ` to "${options.url}"` : '';
|
||||||
|
waiter.log(`waiting for navigation${toUrl} until "${waitUntil}"`);
|
||||||
|
const navigatedEvent = await waiter.waitForEvent(this._eventEmitter, 'navigated', event => {
|
||||||
|
var _this$_page;
|
||||||
|
// Any failed navigation results in a rejection.
|
||||||
|
if (event.error) return true;
|
||||||
|
waiter.log(` navigated to "${event.url}"`);
|
||||||
|
return (0, _utils.urlMatches)((_this$_page = this._page) === null || _this$_page === void 0 ? void 0 : _this$_page.context()._options.baseURL, event.url, options.url);
|
||||||
|
});
|
||||||
|
if (navigatedEvent.error) {
|
||||||
|
const e = new Error(navigatedEvent.error);
|
||||||
|
e.stack = '';
|
||||||
|
await waiter.waitForPromise(Promise.reject(e));
|
||||||
|
}
|
||||||
|
if (!this._loadStates.has(waitUntil)) {
|
||||||
|
await waiter.waitForEvent(this._eventEmitter, 'loadstate', s => {
|
||||||
|
waiter.log(` "${s}" event fired`);
|
||||||
|
return s === waitUntil;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const request = navigatedEvent.newDocument ? network.Request.fromNullable(navigatedEvent.newDocument.request) : null;
|
||||||
|
const response = request ? await waiter.waitForPromise(request._finalRequest()._internalResponse()) : null;
|
||||||
|
waiter.dispose();
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async waitForLoadState(state = 'load', options = {}) {
|
||||||
|
state = verifyLoadState('state', state);
|
||||||
|
return await this._page._wrapApiCall(async () => {
|
||||||
|
const waiter = this._setupNavigationWaiter(options);
|
||||||
|
if (this._loadStates.has(state)) {
|
||||||
|
waiter.log(` not waiting, "${state}" event already fired`);
|
||||||
|
} else {
|
||||||
|
await waiter.waitForEvent(this._eventEmitter, 'loadstate', s => {
|
||||||
|
waiter.log(` "${s}" event fired`);
|
||||||
|
return s === state;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
waiter.dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async waitForURL(url, options = {}) {
|
||||||
|
var _this$_page2;
|
||||||
|
if ((0, _utils.urlMatches)((_this$_page2 = this._page) === null || _this$_page2 === void 0 ? void 0 : _this$_page2.context()._options.baseURL, this.url(), url)) return await this.waitForLoadState(options.waitUntil, options);
|
||||||
|
await this.waitForNavigation({
|
||||||
|
url,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async frameElement() {
|
||||||
|
return _elementHandle.ElementHandle.from((await this._channel.frameElement()).element);
|
||||||
|
}
|
||||||
|
async evaluateHandle(pageFunction, arg) {
|
||||||
|
(0, _jsHandle.assertMaxArguments)(arguments.length, 2);
|
||||||
|
const result = await this._channel.evaluateExpressionHandle({
|
||||||
|
expression: String(pageFunction),
|
||||||
|
isFunction: typeof pageFunction === 'function',
|
||||||
|
arg: (0, _jsHandle.serializeArgument)(arg)
|
||||||
|
});
|
||||||
|
return _jsHandle.JSHandle.from(result.handle);
|
||||||
|
}
|
||||||
|
async evaluate(pageFunction, arg) {
|
||||||
|
(0, _jsHandle.assertMaxArguments)(arguments.length, 2);
|
||||||
|
const result = await this._channel.evaluateExpression({
|
||||||
|
expression: String(pageFunction),
|
||||||
|
isFunction: typeof pageFunction === 'function',
|
||||||
|
arg: (0, _jsHandle.serializeArgument)(arg)
|
||||||
|
});
|
||||||
|
return (0, _jsHandle.parseResult)(result.value);
|
||||||
|
}
|
||||||
|
async _evaluateExposeUtilityScript(pageFunction, arg) {
|
||||||
|
(0, _jsHandle.assertMaxArguments)(arguments.length, 2);
|
||||||
|
const result = await this._channel.evaluateExpression({
|
||||||
|
expression: String(pageFunction),
|
||||||
|
isFunction: typeof pageFunction === 'function',
|
||||||
|
arg: (0, _jsHandle.serializeArgument)(arg)
|
||||||
|
});
|
||||||
|
return (0, _jsHandle.parseResult)(result.value);
|
||||||
|
}
|
||||||
|
async $(selector, options) {
|
||||||
|
const result = await this._channel.querySelector({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
return _elementHandle.ElementHandle.fromNullable(result.element);
|
||||||
|
}
|
||||||
|
async waitForSelector(selector, options = {}) {
|
||||||
|
if (options.visibility) throw new Error('options.visibility is not supported, did you mean options.state?');
|
||||||
|
if (options.waitFor && options.waitFor !== 'visible') throw new Error('options.waitFor is not supported, did you mean options.state?');
|
||||||
|
const result = await this._channel.waitForSelector({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
return _elementHandle.ElementHandle.fromNullable(result.element);
|
||||||
|
}
|
||||||
|
async dispatchEvent(selector, type, eventInit, options = {}) {
|
||||||
|
await this._channel.dispatchEvent({
|
||||||
|
selector,
|
||||||
|
type,
|
||||||
|
eventInit: (0, _jsHandle.serializeArgument)(eventInit),
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async $eval(selector, pageFunction, arg) {
|
||||||
|
(0, _jsHandle.assertMaxArguments)(arguments.length, 3);
|
||||||
|
const result = await this._channel.evalOnSelector({
|
||||||
|
selector,
|
||||||
|
expression: String(pageFunction),
|
||||||
|
isFunction: typeof pageFunction === 'function',
|
||||||
|
arg: (0, _jsHandle.serializeArgument)(arg)
|
||||||
|
});
|
||||||
|
return (0, _jsHandle.parseResult)(result.value);
|
||||||
|
}
|
||||||
|
async $$eval(selector, pageFunction, arg) {
|
||||||
|
(0, _jsHandle.assertMaxArguments)(arguments.length, 3);
|
||||||
|
const result = await this._channel.evalOnSelectorAll({
|
||||||
|
selector,
|
||||||
|
expression: String(pageFunction),
|
||||||
|
isFunction: typeof pageFunction === 'function',
|
||||||
|
arg: (0, _jsHandle.serializeArgument)(arg)
|
||||||
|
});
|
||||||
|
return (0, _jsHandle.parseResult)(result.value);
|
||||||
|
}
|
||||||
|
async $$(selector) {
|
||||||
|
const result = await this._channel.querySelectorAll({
|
||||||
|
selector
|
||||||
|
});
|
||||||
|
return result.elements.map(e => _elementHandle.ElementHandle.from(e));
|
||||||
|
}
|
||||||
|
async _queryCount(selector) {
|
||||||
|
return (await this._channel.queryCount({
|
||||||
|
selector
|
||||||
|
})).value;
|
||||||
|
}
|
||||||
|
async content() {
|
||||||
|
return (await this._channel.content()).value;
|
||||||
|
}
|
||||||
|
async setContent(html, options = {}) {
|
||||||
|
const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
||||||
|
await this._channel.setContent({
|
||||||
|
html,
|
||||||
|
...options,
|
||||||
|
waitUntil
|
||||||
|
});
|
||||||
|
}
|
||||||
|
name() {
|
||||||
|
return this._name || '';
|
||||||
|
}
|
||||||
|
url() {
|
||||||
|
return this._url;
|
||||||
|
}
|
||||||
|
parentFrame() {
|
||||||
|
return this._parentFrame;
|
||||||
|
}
|
||||||
|
childFrames() {
|
||||||
|
return Array.from(this._childFrames);
|
||||||
|
}
|
||||||
|
isDetached() {
|
||||||
|
return this._detached;
|
||||||
|
}
|
||||||
|
async addScriptTag(options = {}) {
|
||||||
|
const copy = {
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
if (copy.path) {
|
||||||
|
copy.content = (await _fs.default.promises.readFile(copy.path)).toString();
|
||||||
|
copy.content = (0, _clientHelper.addSourceUrlToScript)(copy.content, copy.path);
|
||||||
|
}
|
||||||
|
return _elementHandle.ElementHandle.from((await this._channel.addScriptTag({
|
||||||
|
...copy
|
||||||
|
})).element);
|
||||||
|
}
|
||||||
|
async addStyleTag(options = {}) {
|
||||||
|
const copy = {
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
if (copy.path) {
|
||||||
|
copy.content = (await _fs.default.promises.readFile(copy.path)).toString();
|
||||||
|
copy.content += '/*# sourceURL=' + copy.path.replace(/\n/g, '') + '*/';
|
||||||
|
}
|
||||||
|
return _elementHandle.ElementHandle.from((await this._channel.addStyleTag({
|
||||||
|
...copy
|
||||||
|
})).element);
|
||||||
|
}
|
||||||
|
async click(selector, options = {}) {
|
||||||
|
return await this._channel.click({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async dblclick(selector, options = {}) {
|
||||||
|
return await this._channel.dblclick({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async dragAndDrop(source, target, options = {}) {
|
||||||
|
return await this._channel.dragAndDrop({
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async tap(selector, options = {}) {
|
||||||
|
return await this._channel.tap({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async fill(selector, value, options = {}) {
|
||||||
|
return await this._channel.fill({
|
||||||
|
selector,
|
||||||
|
value,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async _highlight(selector) {
|
||||||
|
return await this._channel.highlight({
|
||||||
|
selector
|
||||||
|
});
|
||||||
|
}
|
||||||
|
locator(selector, options) {
|
||||||
|
return new _locator.Locator(this, selector, options);
|
||||||
|
}
|
||||||
|
getByTestId(testId) {
|
||||||
|
return this.locator((0, _locatorUtils.getByTestIdSelector)((0, _locator.testIdAttributeName)(), testId));
|
||||||
|
}
|
||||||
|
getByAltText(text, options) {
|
||||||
|
return this.locator((0, _locatorUtils.getByAltTextSelector)(text, options));
|
||||||
|
}
|
||||||
|
getByLabel(text, options) {
|
||||||
|
return this.locator((0, _locatorUtils.getByLabelSelector)(text, options));
|
||||||
|
}
|
||||||
|
getByPlaceholder(text, options) {
|
||||||
|
return this.locator((0, _locatorUtils.getByPlaceholderSelector)(text, options));
|
||||||
|
}
|
||||||
|
getByText(text, options) {
|
||||||
|
return this.locator((0, _locatorUtils.getByTextSelector)(text, options));
|
||||||
|
}
|
||||||
|
getByTitle(text, options) {
|
||||||
|
return this.locator((0, _locatorUtils.getByTitleSelector)(text, options));
|
||||||
|
}
|
||||||
|
getByRole(role, options = {}) {
|
||||||
|
return this.locator((0, _locatorUtils.getByRoleSelector)(role, options));
|
||||||
|
}
|
||||||
|
frameLocator(selector) {
|
||||||
|
return new _locator.FrameLocator(this, selector);
|
||||||
|
}
|
||||||
|
async focus(selector, options = {}) {
|
||||||
|
await this._channel.focus({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async textContent(selector, options = {}) {
|
||||||
|
const value = (await this._channel.textContent({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
})).value;
|
||||||
|
return value === undefined ? null : value;
|
||||||
|
}
|
||||||
|
async innerText(selector, options = {}) {
|
||||||
|
return (await this._channel.innerText({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
})).value;
|
||||||
|
}
|
||||||
|
async innerHTML(selector, options = {}) {
|
||||||
|
return (await this._channel.innerHTML({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
})).value;
|
||||||
|
}
|
||||||
|
async getAttribute(selector, name, options = {}) {
|
||||||
|
const value = (await this._channel.getAttribute({
|
||||||
|
selector,
|
||||||
|
name,
|
||||||
|
...options
|
||||||
|
})).value;
|
||||||
|
return value === undefined ? null : value;
|
||||||
|
}
|
||||||
|
async inputValue(selector, options = {}) {
|
||||||
|
return (await this._channel.inputValue({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
})).value;
|
||||||
|
}
|
||||||
|
async isChecked(selector, options = {}) {
|
||||||
|
return (await this._channel.isChecked({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
})).value;
|
||||||
|
}
|
||||||
|
async isDisabled(selector, options = {}) {
|
||||||
|
return (await this._channel.isDisabled({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
})).value;
|
||||||
|
}
|
||||||
|
async isEditable(selector, options = {}) {
|
||||||
|
return (await this._channel.isEditable({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
})).value;
|
||||||
|
}
|
||||||
|
async isEnabled(selector, options = {}) {
|
||||||
|
return (await this._channel.isEnabled({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
})).value;
|
||||||
|
}
|
||||||
|
async isHidden(selector, options = {}) {
|
||||||
|
return (await this._channel.isHidden({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
})).value;
|
||||||
|
}
|
||||||
|
async isVisible(selector, options = {}) {
|
||||||
|
return (await this._channel.isVisible({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
})).value;
|
||||||
|
}
|
||||||
|
async hover(selector, options = {}) {
|
||||||
|
await this._channel.hover({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async selectOption(selector, values, options = {}) {
|
||||||
|
return (await this._channel.selectOption({
|
||||||
|
selector,
|
||||||
|
...(0, _elementHandle.convertSelectOptionValues)(values),
|
||||||
|
...options
|
||||||
|
})).values;
|
||||||
|
}
|
||||||
|
async setInputFiles(selector, files, options = {}) {
|
||||||
|
const converted = await (0, _elementHandle.convertInputFiles)(files, this.page().context());
|
||||||
|
await this._channel.setInputFiles({
|
||||||
|
selector,
|
||||||
|
...converted,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async type(selector, text, options = {}) {
|
||||||
|
await this._channel.type({
|
||||||
|
selector,
|
||||||
|
text,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async press(selector, key, options = {}) {
|
||||||
|
await this._channel.press({
|
||||||
|
selector,
|
||||||
|
key,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async check(selector, options = {}) {
|
||||||
|
await this._channel.check({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async uncheck(selector, options = {}) {
|
||||||
|
await this._channel.uncheck({
|
||||||
|
selector,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async setChecked(selector, checked, options) {
|
||||||
|
if (checked) await this.check(selector, options);else await this.uncheck(selector, options);
|
||||||
|
}
|
||||||
|
async waitForTimeout(timeout) {
|
||||||
|
await this._channel.waitForTimeout({
|
||||||
|
timeout
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async waitForFunction(pageFunction, arg, options = {}) {
|
||||||
|
if (typeof options.polling === 'string') (0, _utils.assert)(options.polling === 'raf', 'Unknown polling option: ' + options.polling);
|
||||||
|
const result = await this._channel.waitForFunction({
|
||||||
|
...options,
|
||||||
|
pollingInterval: options.polling === 'raf' ? undefined : options.polling,
|
||||||
|
expression: String(pageFunction),
|
||||||
|
isFunction: typeof pageFunction === 'function',
|
||||||
|
arg: (0, _jsHandle.serializeArgument)(arg)
|
||||||
|
});
|
||||||
|
return _jsHandle.JSHandle.from(result.handle);
|
||||||
|
}
|
||||||
|
async title() {
|
||||||
|
return (await this._channel.title()).value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Frame = Frame;
|
||||||
|
function verifyLoadState(name, waitUntil) {
|
||||||
|
if (waitUntil === 'networkidle0') waitUntil = 'networkidle';
|
||||||
|
if (!_types.kLifecycleEvents.has(waitUntil)) throw new Error(`${name}: expected one of (load|domcontentloaded|networkidle|commit)`);
|
||||||
|
return waitUntil;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user