init
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
name-template: $NEXT_PATCH_VERSION
|
||||||
|
tag-template: $NEXT_PATCH_VERSION
|
||||||
|
template: |
|
||||||
|
## What’s Changed
|
||||||
|
|
||||||
|
$CHANGES
|
||||||
|
|
||||||
|
categories:
|
||||||
|
- title: New features
|
||||||
|
label: new-feature
|
||||||
|
- title: Fixes
|
||||||
|
label: bug
|
||||||
|
- title: Improvements
|
||||||
|
label: improvement
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
golang:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Golang
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.17.x
|
||||||
|
|
||||||
|
- uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: ci/test.sh
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: ci/build.sh
|
||||||
|
|
||||||
|
- name: Latest docs
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: ci/docs.sh latest ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- uses: release-drafter/release-drafter@v5
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
name: release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
golang:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Golang
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.17.x
|
||||||
|
|
||||||
|
- uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: ci/test.sh
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: ci/build.sh
|
||||||
|
|
||||||
|
- name: Prepare release version
|
||||||
|
run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- uses: AButler/upload-release-assets@v2.0
|
||||||
|
with:
|
||||||
|
files: 'dist/*'
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Release docs
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: ci/docs.sh $RELEASE_VERSION ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
name: test
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
golang:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Golang
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.17.x
|
||||||
|
|
||||||
|
- uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: ci/test.sh
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: ci/build.sh
|
||||||
|
|
||||||
|
- uses: codecov/codecov-action@v1
|
||||||
|
with:
|
||||||
|
flags: go
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
*.deb
|
||||||
|
*.bak
|
||||||
|
images
|
||||||
|
pkged.go
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
selenium/barrier/barrier
|
||||||
@@ -0,0 +1,389 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Copyright 2017+ Aerokube Software OÜ
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Browser Images
|
||||||
|
[](https://github.com/aerokube/images/actions?query=workflow%3Abuild)
|
||||||
|
[](https://github.com/aerokube/images/releases/latest)
|
||||||
|
|
||||||
|
This repository contains [Docker](http://docker.com/) build files to be used for [Selenoid](http://github.com/aerokube/selenoid) and [Moon](http://github.com/aerokube/moon) projects. You can find prebuilt images [here](https://hub.docker.com/u/selenoid/).
|
||||||
|
|
||||||
|
## Download Statistics
|
||||||
|
|
||||||
|
### Firefox: [](https://hub.docker.com/r/selenoid/firefox)
|
||||||
|
|
||||||
|
### Chrome: [](https://hub.docker.com/r/selenoid/chrome)
|
||||||
|
|
||||||
|
### Opera: [](https://hub.docker.com/r/selenoid/opera)
|
||||||
|
|
||||||
|
### Android: [](https://hub.docker.com/r/selenoid/android)
|
||||||
|
|
||||||
|
## Building Images
|
||||||
|
|
||||||
|
Moved to: http://aerokube.com/images/latest/#_building_images
|
||||||
|
|
||||||
|
## Image information
|
||||||
|
Moved to: http://aerokube.com/images/latest/#_browser_image_information
|
||||||
+393
@@ -0,0 +1,393 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/markbates/pkger"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LatestVersion = "latest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Requirements struct {
|
||||||
|
BrowserSource BrowserSource
|
||||||
|
BrowserChannel string // "beta", "esr", "dev" and so on
|
||||||
|
DriverVersion string
|
||||||
|
Tags []string
|
||||||
|
NoCache bool
|
||||||
|
TestsDir string
|
||||||
|
RunTests bool
|
||||||
|
IgnoreTests bool
|
||||||
|
PushImage bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type BrowserSource string
|
||||||
|
|
||||||
|
// Return regular file corresponding to this source and optionally download this file
|
||||||
|
func (bs *BrowserSource) Prepare() (string, string, error) {
|
||||||
|
src := string(*bs)
|
||||||
|
if src == "" {
|
||||||
|
return "", "", errors.New("empty browser source")
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(src); err == nil {
|
||||||
|
pkgName := filepath.Base(src)
|
||||||
|
return src, extractVersion(pkgName), nil
|
||||||
|
} else if u, err := url.Parse(src); strings.HasPrefix(src, "http") && err == nil {
|
||||||
|
pkgName := path.Base(src)
|
||||||
|
data, err := downloadFile(u.String())
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("download file: %v", err)
|
||||||
|
}
|
||||||
|
f, err := ioutil.TempFile("", "images")
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("temporary file: %v", err)
|
||||||
|
}
|
||||||
|
outputFileName := f.Name()
|
||||||
|
err = ioutil.WriteFile(outputFileName, data, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("save downloaded file: %v", err)
|
||||||
|
}
|
||||||
|
return outputFileName, extractVersion(pkgName), nil
|
||||||
|
}
|
||||||
|
return "", src, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractVersion(name string) string {
|
||||||
|
pieces := strings.Split(name, "_")
|
||||||
|
version := name
|
||||||
|
if len(pieces) >= 2 {
|
||||||
|
version = pieces[1]
|
||||||
|
}
|
||||||
|
pieces = strings.Split(version, "+")
|
||||||
|
pieces = strings.Split(pieces[0], "-")
|
||||||
|
pieces = strings.Split(pieces[0], "~")
|
||||||
|
return pieces[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func versionN(pkgVersion string, n int) string {
|
||||||
|
buildVersion := pkgVersion
|
||||||
|
pieces := strings.Split(pkgVersion, ".")
|
||||||
|
if len(pieces) >= n {
|
||||||
|
buildVersion = strings.Join(pieces[0:n], ".")
|
||||||
|
}
|
||||||
|
return buildVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func majorVersion(pkgVersion string) string {
|
||||||
|
return versionN(pkgVersion, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func majorMinorVersion(pkgVersion string) string {
|
||||||
|
return versionN(pkgVersion, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildVersion(pkgVersion string) string {
|
||||||
|
return versionN(pkgVersion, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Image struct {
|
||||||
|
Dir string
|
||||||
|
BuildArgs []string
|
||||||
|
Labels []string
|
||||||
|
FileServer bool
|
||||||
|
Requirements
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewImage(srcDir string, destDir string, req Requirements) (*Image, error) {
|
||||||
|
if !requireCommand("docker") {
|
||||||
|
return nil, fmt.Errorf("docker is not installed")
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := copySourceFiles(srcDir, destDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("copy source files: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.Tags) == 0 {
|
||||||
|
return nil, errors.New("image tag is required")
|
||||||
|
}
|
||||||
|
return &Image{Dir: dir, Requirements: req}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func requireCommand(cmd string) bool {
|
||||||
|
_, err := exec.LookPath(cmd)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func tmpDir() (string, error) {
|
||||||
|
dir, err := ioutil.TempDir("", "images")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("create temporary dir: %v", err)
|
||||||
|
}
|
||||||
|
return dir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copySourceFiles(srcDir string, destDir string) (string, error) {
|
||||||
|
|
||||||
|
const prefix = "/static"
|
||||||
|
walkDir := filepath.Join(prefix, srcDir)
|
||||||
|
err := pkger.Walk(walkDir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
regex := regexp.MustCompile(`^.+:/static(.+)$`)
|
||||||
|
relativePath := regex.FindStringSubmatch(path)[1]
|
||||||
|
outputPath := filepath.Join(destDir, relativePath)
|
||||||
|
if info.IsDir() {
|
||||||
|
return os.MkdirAll(outputPath, info.Mode())
|
||||||
|
}
|
||||||
|
|
||||||
|
fileDir := filepath.Join(destDir, filepath.Dir(relativePath))
|
||||||
|
if !fileExists(fileDir) {
|
||||||
|
log.Printf("mkdir dir %s", fileDir)
|
||||||
|
return os.MkdirAll(fileDir, info.Mode())
|
||||||
|
}
|
||||||
|
|
||||||
|
src, err := pkger.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
|
||||||
|
dest, err := os.Create(outputPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dest.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(dest, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dest.Sync()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Chmod(outputPath, info.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(destDir, srcDir), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) Build() error {
|
||||||
|
|
||||||
|
args := []string{"build"}
|
||||||
|
for _, tag := range i.Tags {
|
||||||
|
args = append(args, "-t", tag)
|
||||||
|
}
|
||||||
|
if len(i.BuildArgs) > 0 {
|
||||||
|
for _, arg := range i.BuildArgs {
|
||||||
|
args = append(args, "--build-arg", arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if httpProxy := os.Getenv("HTTP_PROXY"); httpProxy != "" {
|
||||||
|
args = append(args, "--build-arg", fmt.Sprintf("http_proxy=%s", httpProxy))
|
||||||
|
}
|
||||||
|
if httpsProxy := os.Getenv("HTTPS_PROXY"); httpsProxy != "" {
|
||||||
|
args = append(args, "--build-arg", fmt.Sprintf("https_proxy=%s", httpsProxy))
|
||||||
|
}
|
||||||
|
if len(i.Labels) > 0 {
|
||||||
|
for _, label := range i.Labels {
|
||||||
|
args = append(args, "--label", label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.NoCache {
|
||||||
|
args = append(args, "--no-cache")
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.FileServer {
|
||||||
|
server := &http.Server{
|
||||||
|
Handler: http.FileServer(http.Dir(i.Dir)),
|
||||||
|
}
|
||||||
|
ln, err := net.Listen("tcp", ":8080")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to allocate free port: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e := make(chan error)
|
||||||
|
go func() {
|
||||||
|
e <- server.Serve(ln)
|
||||||
|
}()
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
defer server.Shutdown(ctx)
|
||||||
|
if runtime.GOOS == "linux" {
|
||||||
|
ip, err := dockerHostIP()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect host machine IP: %v", err)
|
||||||
|
}
|
||||||
|
args = append(args, "--add-host", fmt.Sprintf("host.docker.internal:%s", ip))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, i.Dir)
|
||||||
|
log.Printf("running command: docker %s", strings.Join(args, " "))
|
||||||
|
cmd := exec.Command("docker", args...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to start command: %v", err)
|
||||||
|
}
|
||||||
|
err = cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("command execution error: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dockerHostIP() (string, error) {
|
||||||
|
ifaces, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
for _, iface := range ifaces {
|
||||||
|
if iface.Name != "docker0" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if iface.Flags&net.FlagUp == 0 {
|
||||||
|
continue // interface down
|
||||||
|
}
|
||||||
|
if iface.Flags&net.FlagLoopback != 0 {
|
||||||
|
continue // loopback interface
|
||||||
|
}
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
for _, addr := range addrs {
|
||||||
|
var ip net.IP
|
||||||
|
switch v := addr.(type) {
|
||||||
|
case *net.IPNet:
|
||||||
|
ip = v.IP
|
||||||
|
case *net.IPAddr:
|
||||||
|
ip = v.IP
|
||||||
|
}
|
||||||
|
if ip == nil || ip.IsLoopback() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ip = ip.To4()
|
||||||
|
if ip == nil {
|
||||||
|
continue // not an ipv4 address
|
||||||
|
}
|
||||||
|
return ip.String(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("no live network interfaces detected")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) Test(testsDir string, browserName string, browserVersion string) error {
|
||||||
|
if !i.RunTests {
|
||||||
|
log.Println("not running tests")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ref := i.Tags[0]
|
||||||
|
err := doTest(ref, testsDir, browserName, browserVersion)
|
||||||
|
if err != nil {
|
||||||
|
if i.IgnoreTests {
|
||||||
|
log.Printf("ignoring tests: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("tests error: %v", err)
|
||||||
|
}
|
||||||
|
log.Println("tests passed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doTest(ref string, testsDir string, browserName string, browserVersion string) error {
|
||||||
|
if !fileExists(testsDir) {
|
||||||
|
return fmt.Errorf("tests directory %s does not exist", testsDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
seleniumUrl := "http://localhost:4445/"
|
||||||
|
if browserName == "firefox" || (browserName == "opera" && browserVersion == "12.16") {
|
||||||
|
seleniumUrl = "http://localhost:4445/wd/hub"
|
||||||
|
}
|
||||||
|
|
||||||
|
exec.Command("docker", "rm", "-f", "selenium").Output()
|
||||||
|
defer func() {
|
||||||
|
exec.Command("docker", "rm", "-f", "selenium").Output()
|
||||||
|
}()
|
||||||
|
|
||||||
|
output, err := exec.Command("docker", "run", "-d", "--name", "selenium", "--privileged", "-p", "4445:4444", ref).Output()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to start docker image: %s %v", string(output), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !requireCommand("mvn") {
|
||||||
|
return fmt.Errorf("maven is not installed")
|
||||||
|
}
|
||||||
|
|
||||||
|
mvnCmd := exec.Command("mvn", "clean", "test",
|
||||||
|
fmt.Sprintf("-Dgrid.connection.url=%s", seleniumUrl),
|
||||||
|
fmt.Sprintf("-Dgrid.browser.name=%s", browserName),
|
||||||
|
fmt.Sprintf("-Dgrid.browser.version=%s", browserVersion),
|
||||||
|
)
|
||||||
|
mvnCmd.Dir = testsDir
|
||||||
|
mvnCmd.Stdout = os.Stdout
|
||||||
|
mvnCmd.Stderr = os.Stderr
|
||||||
|
err = mvnCmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to start tests: %v", err)
|
||||||
|
}
|
||||||
|
err = mvnCmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("tests finished with error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileExists(p string) bool {
|
||||||
|
_, err := os.Stat(p)
|
||||||
|
return !os.IsNotExist(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) Push() error {
|
||||||
|
if i.PushImage {
|
||||||
|
for _, tag := range i.Tags {
|
||||||
|
cmd := exec.Command("docker", "push", tag)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid docker push %s: %v", tag, err)
|
||||||
|
}
|
||||||
|
err = cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("pushing failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/aandryashin/matchers"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVersionFromPackageName(t *testing.T) {
|
||||||
|
AssertThat(t, extractVersion("firefox_45.0.2+build1-0ubuntu0.14.04.1+aerokube0_amd64.deb"), EqualTo{"45.0.2"})
|
||||||
|
AssertThat(t, extractVersion("firefox_71.0_b11+build1-0ubuntu0.18.04.1_amd64.deb"), EqualTo{"71.0"})
|
||||||
|
AssertThat(t, extractVersion("firefox-trunk_72.0_a1_hg20191114r501767-0ubuntu0.18.04.1_umd1_amd64.deb"), EqualTo{"72.0"})
|
||||||
|
AssertThat(t, extractVersion("google-chrome-stable_48.0.2564.109-1+aerokube0_amd64.deb"), EqualTo{"48.0.2564.109"})
|
||||||
|
AssertThat(t, extractVersion("google-chrome-beta_79.0.3945.36-1_amd64.deb"), EqualTo{"79.0.3945.36"})
|
||||||
|
AssertThat(t, extractVersion("google-chrome-unstable_80.0.3964.0-1_amd64.deb"), EqualTo{"80.0.3964.0"})
|
||||||
|
AssertThat(t, extractVersion("opera-stable_38.0.2220.31_amd64.deb"), EqualTo{"38.0.2220.31"})
|
||||||
|
AssertThat(t, extractVersion("opera-beta_65.0.3467.32_amd64.deb"), EqualTo{"65.0.3467.32"})
|
||||||
|
AssertThat(t, extractVersion("opera-developer_66.0.3502.0_amd64.deb"), EqualTo{"66.0.3502.0"})
|
||||||
|
AssertThat(t, extractVersion("yandex-browser-beta_19.9.3.358-1_amd64.deb"), EqualTo{"19.9.3.358"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionToTag(t *testing.T) {
|
||||||
|
AssertThat(t, extractVersion("45.0.2+build1-0ubuntu0.14.04.1+aerokube0"), EqualTo{"45.0.2"})
|
||||||
|
AssertThat(t, extractVersion("48.0.2564.109-1+aerokube0"), EqualTo{"48.0.2564.109"})
|
||||||
|
AssertThat(t, extractVersion("78.0.3904.108-1"), EqualTo{"78.0.3904.108"})
|
||||||
|
AssertThat(t, extractVersion("38.0.2220.31"), EqualTo{"38.0.2220.31"})
|
||||||
|
AssertThat(t, extractVersion("71.0~b11+build1-0ubuntu0.18.04.1"), EqualTo{"71.0"})
|
||||||
|
AssertThat(t, extractVersion("74.0~a1~hg20200205r512485-0ubuntu0.18.04.1~umd1"), EqualTo{"74.0"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseVersion(t *testing.T) {
|
||||||
|
AssertThat(t, majorVersion("78.0.3904.108"), EqualTo{"78"})
|
||||||
|
AssertThat(t, majorVersion("78"), EqualTo{"78"})
|
||||||
|
AssertThat(t, majorMinorVersion("78.0.3904.108"), EqualTo{"78.0"})
|
||||||
|
AssertThat(t, majorMinorVersion("78.0"), EqualTo{"78.0"})
|
||||||
|
AssertThat(t, buildVersion("78.0.3904.108"), EqualTo{"78.0.3904"})
|
||||||
|
AssertThat(t, buildVersion("78.0.3904"), EqualTo{"78.0.3904"})
|
||||||
|
}
|
||||||
+178
@@ -0,0 +1,178 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
chromeDriverBinary = "chromedriver"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Chrome struct {
|
||||||
|
Requirements
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chrome) Build() error {
|
||||||
|
|
||||||
|
pkgSrcPath, pkgVersion, err := c.BrowserSource.Prepare()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid browser source: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgTagVersion := extractVersion(pkgVersion)
|
||||||
|
|
||||||
|
driverVersion, err := c.parseChromeDriverVersion(pkgTagVersion)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parse chromedriver version: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build dev image
|
||||||
|
devDestDir, err := tmpDir()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create dev temporary dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcDir := "chrome/apt"
|
||||||
|
|
||||||
|
if pkgSrcPath != "" {
|
||||||
|
srcDir = "chrome/local"
|
||||||
|
pkgDestDir := filepath.Join(devDestDir, srcDir)
|
||||||
|
err := os.MkdirAll(pkgDestDir, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create %v temporary dir: %v", pkgDestDir, err)
|
||||||
|
}
|
||||||
|
pkgDestPath := filepath.Join(pkgDestDir, "google-chrome.deb")
|
||||||
|
err = os.Rename(pkgSrcPath, pkgDestPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("move package: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
devImageTag := fmt.Sprintf("selenoid/dev_chrome:%s", pkgTagVersion)
|
||||||
|
devImageRequirements := Requirements{NoCache: c.NoCache, Tags: []string{devImageTag}}
|
||||||
|
devImage, err := NewImage(srcDir, devDestDir, devImageRequirements)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("init dev image: %v", err)
|
||||||
|
}
|
||||||
|
devBuildArgs := []string{fmt.Sprintf("VERSION=%s", pkgVersion)}
|
||||||
|
devBuildArgs = append(devBuildArgs, c.channelToBuildArgs()...)
|
||||||
|
devImage.BuildArgs = devBuildArgs
|
||||||
|
if pkgSrcPath != "" {
|
||||||
|
devImage.FileServer = true
|
||||||
|
}
|
||||||
|
|
||||||
|
err = devImage.Build()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build dev image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build main image
|
||||||
|
destDir, err := tmpDir()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create temporary dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
image, err := NewImage("chrome", destDir, c.Requirements)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("init image: %v", err)
|
||||||
|
}
|
||||||
|
image.BuildArgs = append(image.BuildArgs, fmt.Sprintf("VERSION=%s", pkgTagVersion))
|
||||||
|
|
||||||
|
err = c.downloadChromeDriver(image.Dir, driverVersion)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to download chromedriver: %v", err)
|
||||||
|
}
|
||||||
|
image.Labels = []string{fmt.Sprintf("driver=chromedriver:%s", driverVersion)}
|
||||||
|
|
||||||
|
err = image.Build()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = image.Test(c.TestsDir, "chrome", pkgTagVersion)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("test image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = image.Push()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("push image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chrome) channelToBuildArgs() []string {
|
||||||
|
switch c.BrowserChannel {
|
||||||
|
case "beta":
|
||||||
|
return []string{"PACKAGE=google-chrome-beta", "INSTALL_DIR=chrome-beta"}
|
||||||
|
case "dev":
|
||||||
|
return []string{"PACKAGE=google-chrome-unstable", "INSTALL_DIR=chrome-unstable"}
|
||||||
|
default:
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chrome) parseChromeDriverVersion(pkgVersion string) (string, error) {
|
||||||
|
version := c.DriverVersion
|
||||||
|
if version == LatestVersion {
|
||||||
|
const baseUrl = "https://chromedriver.storage.googleapis.com/"
|
||||||
|
v, err := c.getLatestChromeDriver(baseUrl, pkgVersion)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
version = v
|
||||||
|
}
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chrome) downloadChromeDriver(dir string, version string) error {
|
||||||
|
u := fmt.Sprintf("http://chromedriver.storage.googleapis.com/%s/chromedriver_linux64.zip", version)
|
||||||
|
_, err := downloadDriver(u, chromeDriverBinary, dir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("download chromedriver: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chrome) getLatestChromeDriver(baseUrl string, pkgVersion string) (string, error) {
|
||||||
|
fetchVersion := func(url string) (string, error) {
|
||||||
|
data, err := sendGet(url)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("read chromedriver version: %v", err)
|
||||||
|
}
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.BrowserChannel != "dev" {
|
||||||
|
chromeBuildVersion := buildVersion(pkgVersion)
|
||||||
|
u := baseUrl + fmt.Sprintf("LATEST_RELEASE_%s", chromeBuildVersion)
|
||||||
|
v, err := fetchVersion(u)
|
||||||
|
if err == nil {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chromeMajorVersion, err := strconv.Atoi(majorVersion(pkgVersion))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("chrome major version: %v", err)
|
||||||
|
}
|
||||||
|
u := baseUrl + fmt.Sprintf("LATEST_RELEASE_%d", chromeMajorVersion)
|
||||||
|
v, err := fetchVersion(u)
|
||||||
|
if err == nil {
|
||||||
|
return v, nil
|
||||||
|
} else {
|
||||||
|
previousChromeMajorVersion := chromeMajorVersion - 1
|
||||||
|
u = baseUrl + fmt.Sprintf("LATEST_RELEASE_%d", previousChromeMajorVersion)
|
||||||
|
v, err := fetchVersion(u)
|
||||||
|
if err == nil {
|
||||||
|
return v, nil
|
||||||
|
} else {
|
||||||
|
return "", errors.New("could not find compatible chromedriver")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+122
@@ -0,0 +1,122 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
msedgeDriverBinary = "msedgedriver"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Edge struct {
|
||||||
|
Requirements
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Edge) Build() error {
|
||||||
|
|
||||||
|
pkgSrcPath, pkgVersion, err := c.BrowserSource.Prepare()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid browser source: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgTagVersion := extractVersion(pkgVersion)
|
||||||
|
|
||||||
|
// Build dev image
|
||||||
|
devDestDir, err := tmpDir()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create dev temporary dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcDir := "edge/apt"
|
||||||
|
|
||||||
|
if pkgSrcPath != "" {
|
||||||
|
srcDir = "edge/local"
|
||||||
|
pkgDestDir := filepath.Join(devDestDir, srcDir)
|
||||||
|
err := os.MkdirAll(pkgDestDir, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create %v temporary dir: %v", pkgDestDir, err)
|
||||||
|
}
|
||||||
|
pkgDestPath := filepath.Join(pkgDestDir, "microsoft-edge.deb")
|
||||||
|
err = os.Rename(pkgSrcPath, pkgDestPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("move package: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
devImageTag := fmt.Sprintf("selenoid/dev_edge:%s", pkgTagVersion)
|
||||||
|
devImageRequirements := Requirements{NoCache: c.NoCache, Tags: []string{devImageTag}}
|
||||||
|
devImage, err := NewImage(srcDir, devDestDir, devImageRequirements)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("init dev image: %v", err)
|
||||||
|
}
|
||||||
|
devBuildArgs := []string{fmt.Sprintf("VERSION=%s", pkgVersion)}
|
||||||
|
devBuildArgs = append(devBuildArgs, c.channelToBuildArgs()...)
|
||||||
|
devImage.BuildArgs = devBuildArgs
|
||||||
|
if pkgSrcPath != "" {
|
||||||
|
devImage.FileServer = true
|
||||||
|
}
|
||||||
|
|
||||||
|
err = devImage.Build()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build dev image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build main image
|
||||||
|
destDir, err := tmpDir()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create temporary dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
image, err := NewImage("edge", destDir, c.Requirements)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("init image: %v", err)
|
||||||
|
}
|
||||||
|
image.BuildArgs = append(image.BuildArgs, fmt.Sprintf("VERSION=%s", pkgTagVersion))
|
||||||
|
|
||||||
|
driverVersion, err := c.downloadMSEdgeDriver(image.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to download msedgedriver: %v", err)
|
||||||
|
}
|
||||||
|
image.Labels = []string{fmt.Sprintf("driver=msedgedriver:%s", driverVersion)}
|
||||||
|
|
||||||
|
err = image.Build()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = image.Test(c.TestsDir, "MicrosoftEdge", pkgTagVersion)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("test image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = image.Push()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("push image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Edge) channelToBuildArgs() []string {
|
||||||
|
switch c.BrowserChannel {
|
||||||
|
case "beta":
|
||||||
|
return []string{"PACKAGE=microsoft-edge-beta", "INSTALL_DIR=msedge-beta"}
|
||||||
|
case "dev":
|
||||||
|
return []string{"PACKAGE=microsoft-edge-dev", "INSTALL_DIR=msedge-dev"}
|
||||||
|
default:
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Edge) downloadMSEdgeDriver(dir string) (string, error) {
|
||||||
|
version := c.DriverVersion
|
||||||
|
// Full driver versions list can be fetched as XML from https://msedgedriver.azureedge.net/
|
||||||
|
u := fmt.Sprintf("https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver/%s/edgedriver_linux64.zip", version)
|
||||||
|
_, err := downloadDriver(u, msedgeDriverBinary, dir)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("download msedgedriver: %v", err)
|
||||||
|
}
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
+326
@@ -0,0 +1,326 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"archive/zip"
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/cheggaaa/pb.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
zipMagicHeader = "504b"
|
||||||
|
gzipMagicHeader = "1f8b"
|
||||||
|
)
|
||||||
|
|
||||||
|
func downloadFile(url string) ([]byte, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
w := bufio.NewWriter(&b)
|
||||||
|
err := downloadFileWithProgressBar(url, w)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadFileWithProgressBar(url string, w io.Writer) error {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("file download error: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("unexpected response code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
contentLength := int(resp.ContentLength)
|
||||||
|
writer := w
|
||||||
|
|
||||||
|
if contentLength > 0 {
|
||||||
|
bar := pb.New(contentLength).SetUnits(pb.U_BYTES)
|
||||||
|
bar.Output = os.Stderr
|
||||||
|
bar.Start()
|
||||||
|
defer bar.Finish()
|
||||||
|
writer = io.MultiWriter(w, bar)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(writer, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to save file: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadDriver(url string, filename string, outputDir string) (string, error) {
|
||||||
|
log.Printf("downloading driver from %s", url)
|
||||||
|
data, err := downloadFile(url)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to download driver archive: %v", err)
|
||||||
|
}
|
||||||
|
return extractFile(data, filename, outputDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMagicHeader(data []byte) string {
|
||||||
|
if len(data) >= 2 {
|
||||||
|
return hex.EncodeToString(data[:2])
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func isZipFile(data []byte) bool {
|
||||||
|
return getMagicHeader(data) == zipMagicHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
func isTarGzFile(data []byte) bool {
|
||||||
|
return getMagicHeader(data) == gzipMagicHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractFile(data []byte, filename string, outputDir string) (string, error) {
|
||||||
|
if isZipFile(data) {
|
||||||
|
return unzip(data, filename, outputDir)
|
||||||
|
} else if isTarGzFile(data) {
|
||||||
|
return untar(data, filename, outputDir)
|
||||||
|
} else {
|
||||||
|
outputPath := filepath.Join(outputDir, filename)
|
||||||
|
err := ioutil.WriteFile(outputPath, data, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to save file %s: %v", outputPath, err)
|
||||||
|
}
|
||||||
|
fmt.Println("Patched chromedriver")
|
||||||
|
patchDriver(outputPath)
|
||||||
|
return outputPath, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func patchDriver(filename string) {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
var lettersLower = []byte("abcdefghijklmnopqrstuvwxyz")
|
||||||
|
var lettersUpper = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
|
rand_str_repl := make([]byte, 26)
|
||||||
|
for i := range rand_str_repl {
|
||||||
|
if i == 2 {
|
||||||
|
rand_str_repl[i] = rand_str_repl[0]
|
||||||
|
} else if i == 3 {
|
||||||
|
rand_str_repl[i] = byte('_')
|
||||||
|
} else if i == 20 || i == 21 {
|
||||||
|
rand_str_repl[i] = lettersUpper[rand.Intn(len(lettersUpper))]
|
||||||
|
} else {
|
||||||
|
rand_str_repl[i] = lettersLower[rand.Intn(len(lettersLower))]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
log.Println(string(rand_str_repl))
|
||||||
|
re := regexp.MustCompile(`cdc_.{22}`)
|
||||||
|
input_file, err := ioutil.ReadFile(filename)
|
||||||
|
log.Println(filename)
|
||||||
|
if err == nil {
|
||||||
|
match := re.Find(input_file)
|
||||||
|
if match == nil {
|
||||||
|
log.Fatalln("cant find driver cdc_")
|
||||||
|
}
|
||||||
|
log.Println(string(match))
|
||||||
|
output_data := re.ReplaceAll(input_file, rand_str_repl)
|
||||||
|
match = re.Find(output_data)
|
||||||
|
if match != nil {
|
||||||
|
log.Fatalln("cant patch driver cdc_")
|
||||||
|
}
|
||||||
|
err := ioutil.WriteFile(filename, output_data, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("cant write to file")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on http://stackoverflow.com/questions/20357223/easy-way-to-unzip-file-with-golang
|
||||||
|
func unzip(data []byte, fileName string, outputDir string) (string, error) {
|
||||||
|
zr, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
|
||||||
|
|
||||||
|
// Closure to address file descriptors issue with all the deferred .Close() methods
|
||||||
|
extractAndWriteFile := func(f *zip.File) (string, error) {
|
||||||
|
rc, err := f.Open()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
outputPath := filepath.Join(outputDir, f.Name)
|
||||||
|
|
||||||
|
if f.FileInfo().IsDir() {
|
||||||
|
return "", fmt.Errorf("can only unzip files but %s is a directory", f.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = outputFile(outputPath, f.Mode(), rc)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
fmt.Println("Patched chromedriver")
|
||||||
|
patchDriver(outputPath)
|
||||||
|
return outputPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
for _, f := range zr.File {
|
||||||
|
if f.Name == fileName {
|
||||||
|
return extractAndWriteFile(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("file %s does not exist in archive", fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on https://medium.com/@skdomino/taring-untaring-files-in-go-6b07cf56bc07
|
||||||
|
func untar(data []byte, fileName string, outputDir string) (string, error) {
|
||||||
|
|
||||||
|
gzr, err := gzip.NewReader(bytes.NewReader(data))
|
||||||
|
defer gzr.Close()
|
||||||
|
|
||||||
|
extractAndWriteFile := func(tr *tar.Reader, header *tar.Header) (string, error) {
|
||||||
|
|
||||||
|
outputPath := filepath.Join(outputDir, header.Name)
|
||||||
|
|
||||||
|
if header.Typeflag == tar.TypeDir {
|
||||||
|
return "", fmt.Errorf("can only untar files but %s is a directory", header.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = outputFile(outputPath, os.FileMode(header.Mode), tr)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return outputPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
tr := tar.NewReader(gzr)
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
header, err := tr.Next()
|
||||||
|
switch {
|
||||||
|
case err == io.EOF:
|
||||||
|
break loop
|
||||||
|
case err != nil:
|
||||||
|
return "", err
|
||||||
|
case header == nil:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return extractAndWriteFile(tr, header)
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("file %s does not exist in archive", fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
func outputFile(outputPath string, mode os.FileMode, r io.Reader) error {
|
||||||
|
os.MkdirAll(filepath.Dir(outputPath), 0755)
|
||||||
|
f, err := os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(f, r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doSendGet(url string, token string) ([]byte, error) {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid request: %v", err)
|
||||||
|
}
|
||||||
|
if token != "" {
|
||||||
|
req.Header.Add("Authorization", fmt.Sprintf("token %s", token))
|
||||||
|
}
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("request error: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("unsuccessful response: %d %s", resp.StatusCode, resp.Status)
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read response body: %v", err)
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendGet(url string) ([]byte, error) {
|
||||||
|
return doSendGet(url, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendGetWithAuth(url string, token string) ([]byte, error) {
|
||||||
|
return doSendGet(url, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func latestGithubRelease(repo string) (string, error) {
|
||||||
|
token := os.Getenv("GITHUB_TOKEN")
|
||||||
|
url := fmt.Sprintf("https://api.github.com/repos/%s/releases/latest", repo)
|
||||||
|
data, err := sendGetWithAuth(url, token)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("get latest github release data: %v", err)
|
||||||
|
}
|
||||||
|
type info struct {
|
||||||
|
TagName string `json:"tag_name"`
|
||||||
|
}
|
||||||
|
var i info
|
||||||
|
err = json.Unmarshal(data, &i)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("json unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
return i.TagName, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func latestGithubLinuxAsset(repo string) (string, error) {
|
||||||
|
token := os.Getenv("GITHUB_TOKEN")
|
||||||
|
url := fmt.Sprintf("https://api.github.com/repos/%s/releases", repo)
|
||||||
|
data, err := sendGetWithAuth(url, token)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("get github releases data: %v", err)
|
||||||
|
}
|
||||||
|
type AssetInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
type Release struct {
|
||||||
|
Assets []AssetInfo `json:"assets"`
|
||||||
|
}
|
||||||
|
type Releases []Release
|
||||||
|
var releases Releases
|
||||||
|
err = json.Unmarshal(data, &releases)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("json unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
for _, release := range releases {
|
||||||
|
for _, asset := range release.Assets {
|
||||||
|
if strings.Contains(asset.Name, "linux") {
|
||||||
|
return asset.Name, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("could not find github linux asset")
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/aandryashin/matchers"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnzip(t *testing.T) {
|
||||||
|
data := readFile(t, "testfile.zip")
|
||||||
|
AssertThat(t, isZipFile(data), Is{true})
|
||||||
|
AssertThat(t, isTarGzFile(data), Is{false})
|
||||||
|
testUnpack(t, data, "zip-testfile", func(data []byte, filePath string, outputDir string) (string, error) {
|
||||||
|
return unzip(data, filePath, outputDir)
|
||||||
|
}, "zip\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUntar(t *testing.T) {
|
||||||
|
data := readFile(t, "testfile.tar.gz")
|
||||||
|
AssertThat(t, isTarGzFile(data), Is{true})
|
||||||
|
AssertThat(t, isZipFile(data), Is{false})
|
||||||
|
testUnpack(t, data, "gzip-testfile", func(data []byte, filePath string, outputDir string) (string, error) {
|
||||||
|
return untar(data, filePath, outputDir)
|
||||||
|
}, "gzip\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func withTmpDir(t *testing.T, prefix string, fn func(*testing.T, string)) {
|
||||||
|
dir, err := ioutil.TempDir("", prefix)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
fn(t, dir)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUnpack(t *testing.T, data []byte, fileName string, fn func([]byte, string, string) (string, error), correctContents string) {
|
||||||
|
|
||||||
|
withTmpDir(t, "test-unpack", func(t *testing.T, dir string) {
|
||||||
|
unpackedFile, err := fn(data, fileName, dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fileExists(unpackedFile) {
|
||||||
|
t.Fatalf("file %s does not exist\n", unpackedFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
unpackedFileContents := string(readFile(t, unpackedFile))
|
||||||
|
if unpackedFileContents != correctContents {
|
||||||
|
t.Fatalf("incorrect unpacked file contents; expected: '%s', actual: '%s'\n", correctContents, unpackedFileContents)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFile(t *testing.T, fileName string) []byte {
|
||||||
|
data, err := ioutil.ReadFile(fileName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
geckoDriverBinary = "geckodriver"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Firefox struct {
|
||||||
|
SelenoidVersion string
|
||||||
|
SeleniumVersion string
|
||||||
|
Requirements
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Firefox) Build() error {
|
||||||
|
|
||||||
|
if c.SelenoidVersion == "" && c.SeleniumVersion == "" {
|
||||||
|
return errors.New("missing Selenoid or Selenium JAR version")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build dev image
|
||||||
|
devDestDir, err := tmpDir()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create dev temporary dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devSrcDir := "firefox/apt"
|
||||||
|
pkgSrcPath, pkgVersion, err := c.BrowserSource.Prepare()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid browser source: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgSrcPath != "" {
|
||||||
|
devSrcDir = "firefox/local"
|
||||||
|
pkgDestDir := filepath.Join(devDestDir, devSrcDir)
|
||||||
|
err := os.MkdirAll(pkgDestDir, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create %v temporary dir: %v", pkgDestDir, err)
|
||||||
|
}
|
||||||
|
pkgDestPath := filepath.Join(pkgDestDir, "firefox.deb")
|
||||||
|
err = os.Rename(pkgSrcPath, pkgDestPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("move package: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgTagVersion := extractVersion(pkgVersion)
|
||||||
|
devImageTag := fmt.Sprintf("selenoid/dev_firefox:%s", pkgTagVersion)
|
||||||
|
devImageRequirements := Requirements{NoCache: c.NoCache, Tags: []string{devImageTag}}
|
||||||
|
devImage, err := NewImage(devSrcDir, devDestDir, devImageRequirements)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("init dev image: %v", err)
|
||||||
|
}
|
||||||
|
devBuildArgs := []string{fmt.Sprintf("VERSION=%s", pkgVersion)}
|
||||||
|
devBuildArgs = append(devBuildArgs, c.channelToBuildArgs()...)
|
||||||
|
devImage.BuildArgs = devBuildArgs
|
||||||
|
if pkgSrcPath != "" {
|
||||||
|
devImage.FileServer = true
|
||||||
|
}
|
||||||
|
|
||||||
|
err = devImage.Build()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build dev image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build main image
|
||||||
|
destDir, err := tmpDir()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create temporary dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
firefoxMajorVersion, err := strconv.Atoi(majorVersion(pkgTagVersion))
|
||||||
|
geckoDriverCompatible := firefoxMajorVersion > 48
|
||||||
|
srcDir := "firefox/selenoid"
|
||||||
|
if !geckoDriverCompatible {
|
||||||
|
srcDir = "firefox/selenium"
|
||||||
|
}
|
||||||
|
|
||||||
|
image, err := NewImage(srcDir, destDir, c.Requirements)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("init dev image: %v", err)
|
||||||
|
}
|
||||||
|
image.BuildArgs = append(image.BuildArgs, fmt.Sprintf("VERSION=%s", pkgTagVersion))
|
||||||
|
|
||||||
|
firefoxMajorMinorVersion := majorMinorVersion(pkgTagVersion)
|
||||||
|
if geckoDriverCompatible {
|
||||||
|
driverVersion, err := c.downloadGeckoDriver(image.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to download geckodriver: %v", err)
|
||||||
|
}
|
||||||
|
labels := []string{fmt.Sprintf("driver=geckodriver:%s", driverVersion)}
|
||||||
|
|
||||||
|
selenoidVersion, err := c.downloadSelenoid(image.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to download Selenoid: %v", err)
|
||||||
|
}
|
||||||
|
labels = append(labels, fmt.Sprintf("selenoid=%s", selenoidVersion))
|
||||||
|
image.Labels = labels
|
||||||
|
|
||||||
|
browsersJsonFile := filepath.Join(image.Dir, "browsers.json")
|
||||||
|
data, err := ioutil.ReadFile(browsersJsonFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read browsers.json: %v", err)
|
||||||
|
}
|
||||||
|
newContents := strings.Replace(string(data), "@@VERSION@@", firefoxMajorMinorVersion, -1)
|
||||||
|
err = ioutil.WriteFile(browsersJsonFile, []byte(newContents), 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update browsers.json: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
driverVersion, err := c.downloadSeleniumJAR(image.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to download Selenium JAR: %v", err)
|
||||||
|
}
|
||||||
|
image.Labels = []string{fmt.Sprintf("driver=selenium:%s", driverVersion)}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = image.Build()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = image.Test(c.TestsDir, "firefox", firefoxMajorMinorVersion)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("test image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = image.Push()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("push image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Firefox) channelToBuildArgs() []string {
|
||||||
|
switch c.BrowserChannel {
|
||||||
|
case "beta":
|
||||||
|
return []string{"PPA=ppa:mozillateam/firefox-next"}
|
||||||
|
case "dev":
|
||||||
|
return []string{"PACKAGE=firefox-trunk", "PPA=ppa:ubuntu-mozilla-daily/ppa"}
|
||||||
|
case "esr":
|
||||||
|
return []string{"PACKAGE=firefox-esr", "PPA=ppa:mozillateam/ppa"}
|
||||||
|
default:
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Firefox) downloadGeckoDriver(dir string) (string, error) {
|
||||||
|
version := c.DriverVersion
|
||||||
|
if version == LatestVersion {
|
||||||
|
v, err := latestGithubRelease("mozilla/geckodriver")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("latest geckodriver version: %v", err)
|
||||||
|
}
|
||||||
|
version = strings.TrimPrefix(v, "v")
|
||||||
|
}
|
||||||
|
|
||||||
|
u := fmt.Sprintf("https://github.com/mozilla/geckodriver/releases/download/v%s/geckodriver-v%s-linux64.tar.gz", version, version)
|
||||||
|
_, err := downloadDriver(u, geckoDriverBinary, dir)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("download geckodriver: %v", err)
|
||||||
|
}
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Firefox) downloadSelenoid(dir string) (string, error) {
|
||||||
|
version := c.SelenoidVersion
|
||||||
|
if version == LatestVersion {
|
||||||
|
v, err := latestGithubRelease("aerokube/selenoid")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("latest Selenoid version: %v", err)
|
||||||
|
}
|
||||||
|
version = v
|
||||||
|
}
|
||||||
|
|
||||||
|
u := fmt.Sprintf("https://github.com/aerokube/selenoid/releases/download/%s/selenoid_linux_amd64", version)
|
||||||
|
data, err := downloadFile(u)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("download Selenoid: %v", err)
|
||||||
|
}
|
||||||
|
outputPath := filepath.Join(dir, "selenoid")
|
||||||
|
err = ioutil.WriteFile(outputPath, data, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("save Selenoid: %v", err)
|
||||||
|
}
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Firefox) downloadSeleniumJAR(dir string) (string, error) {
|
||||||
|
version := c.SeleniumVersion
|
||||||
|
var u string
|
||||||
|
switch version {
|
||||||
|
case "2.15.0", "2.19.0", "2.20.0", "2.21.0", "2.25.0", "2.32.0", "2.35.0", "2.37.0", "2.39.0", "2.40.0", "2.41.0", "2.43.1", "2.44.0", "2.45.0", "2.48.2":
|
||||||
|
u = fmt.Sprintf("https://repo.jenkins-ci.org/releases/org/seleniumhq/selenium/selenium-server-standalone/%s/selenium-server-standalone-$selenium_version.jar", version)
|
||||||
|
case "2.47.1":
|
||||||
|
u = "http://selenium-release.storage.googleapis.com/2.47/selenium-server-standalone-2.47.1.jar"
|
||||||
|
case "2.53.1":
|
||||||
|
u = "http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.1.jar"
|
||||||
|
case "3.2.0":
|
||||||
|
u = "http://selenium-release.storage.googleapis.com/3.2/selenium-server-standalone-3.2.0.jar"
|
||||||
|
case "3.3.1":
|
||||||
|
u = "http://selenium-release.storage.googleapis.com/3.3/selenium-server-standalone-3.3.1.jar"
|
||||||
|
case "3.4.0":
|
||||||
|
u = "https://selenium-release.storage.googleapis.com/3.4/selenium-server-standalone-3.4.0.jar"
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unsupported Selenium JAR version: %s", version)
|
||||||
|
}
|
||||||
|
data, err := downloadFile(u)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("download Selenium JAR: %v", err)
|
||||||
|
}
|
||||||
|
outputPath := filepath.Join(dir, "selenium-server-standalone.jar")
|
||||||
|
err = ioutil.WriteFile(outputPath, data, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("save Selenium JAR: %v", err)
|
||||||
|
}
|
||||||
|
return version, nil
|
||||||
|
|
||||||
|
}
|
||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
operaDriverBinary = "operadriver_linux64/operadriver"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Opera struct {
|
||||||
|
Requirements
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Opera) Build() error {
|
||||||
|
|
||||||
|
// Build dev image
|
||||||
|
devDestDir, err := tmpDir()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create dev temporary dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcDir := "opera/apt"
|
||||||
|
pkgSrcPath, pkgVersion, err := o.BrowserSource.Prepare()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid browser source: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgSrcPath != "" {
|
||||||
|
srcDir = "opera/local"
|
||||||
|
pkgDestDir := filepath.Join(devDestDir, srcDir)
|
||||||
|
err := os.MkdirAll(pkgDestDir, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create %v temporary dir: %v", pkgDestDir, err)
|
||||||
|
}
|
||||||
|
pkgDestPath := filepath.Join(pkgDestDir, "opera.deb")
|
||||||
|
err = os.Rename(pkgSrcPath, pkgDestPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("move package: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgTagVersion := extractVersion(pkgVersion)
|
||||||
|
devImageTag := fmt.Sprintf("selenoid/dev_opera:%s", pkgTagVersion)
|
||||||
|
devImageRequirements := Requirements{NoCache: o.NoCache, Tags: []string{devImageTag}}
|
||||||
|
devImage, err := NewImage(srcDir, devDestDir, devImageRequirements)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("init dev image: %v", err)
|
||||||
|
}
|
||||||
|
devBuildArgs := []string{fmt.Sprintf("VERSION=%s", pkgVersion)}
|
||||||
|
devBuildArgs = append(devBuildArgs, o.channelToBuildArgs()...)
|
||||||
|
devImage.BuildArgs = devBuildArgs
|
||||||
|
if pkgSrcPath != "" {
|
||||||
|
devImage.FileServer = true
|
||||||
|
}
|
||||||
|
|
||||||
|
err = devImage.Build()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build dev image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build main image
|
||||||
|
destDir, err := tmpDir()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create temporary dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
image, err := NewImage("opera", destDir, o.Requirements)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("init image: %v", err)
|
||||||
|
}
|
||||||
|
image.BuildArgs = append(image.BuildArgs, fmt.Sprintf("VERSION=%s", pkgTagVersion))
|
||||||
|
|
||||||
|
driverVersion, err := o.downloadOperaDriver(image.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to download operadriver: %v", err)
|
||||||
|
}
|
||||||
|
image.Labels = []string{fmt.Sprintf("driver=operadriver:%s", driverVersion)}
|
||||||
|
|
||||||
|
err = image.Build()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = image.Test(o.TestsDir, "opera", pkgTagVersion)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("test image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = image.Push()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("push image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Opera) channelToBuildArgs() []string {
|
||||||
|
switch o.BrowserChannel {
|
||||||
|
case "beta":
|
||||||
|
return []string{"PACKAGE=opera-beta"}
|
||||||
|
case "dev":
|
||||||
|
return []string{"PACKAGE=opera-developer"}
|
||||||
|
default:
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Opera) downloadOperaDriver(dir string) (string, error) {
|
||||||
|
version := o.DriverVersion
|
||||||
|
if version == LatestVersion {
|
||||||
|
v, err := latestGithubRelease("operasoftware/operachromiumdriver")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("latest Operadriver version: %v", err)
|
||||||
|
}
|
||||||
|
version = strings.TrimPrefix(v, "v.")
|
||||||
|
}
|
||||||
|
|
||||||
|
u := fmt.Sprintf("https://github.com/operasoftware/operachromiumdriver/releases/download/v.%s/operadriver_linux64.zip", version)
|
||||||
|
_, err := downloadDriver(u, operaDriverBinary, dir)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("download Operadriver: %v", err)
|
||||||
|
}
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
+117
@@ -0,0 +1,117 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
yandexDriverBinary = "yandexdriver"
|
||||||
|
)
|
||||||
|
|
||||||
|
type YandexBrowser struct {
|
||||||
|
Requirements
|
||||||
|
}
|
||||||
|
|
||||||
|
func (yb *YandexBrowser) Build() error {
|
||||||
|
|
||||||
|
// Build dev image
|
||||||
|
devDestDir, err := tmpDir()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create dev temporary dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcDir := "yandex/apt"
|
||||||
|
pkgSrcPath, pkgVersion, err := yb.BrowserSource.Prepare()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid browser source: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgSrcPath != "" {
|
||||||
|
srcDir = "yandex/local"
|
||||||
|
pkgDestDir := filepath.Join(devDestDir, srcDir)
|
||||||
|
err := os.MkdirAll(pkgDestDir, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create %v temporary dir: %v", pkgDestDir, err)
|
||||||
|
}
|
||||||
|
pkgDestPath := filepath.Join(pkgDestDir, "yandex-browser.deb")
|
||||||
|
err = os.Rename(pkgSrcPath, pkgDestPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("move package: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgTagVersion := extractVersion(pkgVersion)
|
||||||
|
devImageTag := fmt.Sprintf("selenoid/dev_yandex:%s", pkgTagVersion)
|
||||||
|
devImageRequirements := Requirements{NoCache: yb.NoCache, Tags: []string{devImageTag}}
|
||||||
|
devImage, err := NewImage(srcDir, devDestDir, devImageRequirements)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("init dev image: %v", err)
|
||||||
|
}
|
||||||
|
devBuildArgs := []string{fmt.Sprintf("VERSION=%s", pkgVersion)}
|
||||||
|
devImage.BuildArgs = devBuildArgs
|
||||||
|
if pkgSrcPath != "" {
|
||||||
|
devImage.FileServer = true
|
||||||
|
}
|
||||||
|
|
||||||
|
err = devImage.Build()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build dev image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build main image
|
||||||
|
destDir, err := tmpDir()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create temporary dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
image, err := NewImage("yandex", destDir, yb.Requirements)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("init image: %v", err)
|
||||||
|
}
|
||||||
|
image.BuildArgs = append(image.BuildArgs, fmt.Sprintf("VERSION=%s", pkgTagVersion))
|
||||||
|
|
||||||
|
driverVersion, err := yb.downloadYandexDriver(image.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to download yandexdriver: %v", err)
|
||||||
|
}
|
||||||
|
image.Labels = []string{fmt.Sprintf("driver=yandexdriver:%s", driverVersion)}
|
||||||
|
|
||||||
|
err = image.Build()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = image.Test(yb.TestsDir, "yandex", pkgTagVersion)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("test image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = image.Push()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("push image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (yb *YandexBrowser) downloadYandexDriver(dir string) (string, error) {
|
||||||
|
version := yb.DriverVersion
|
||||||
|
if version == LatestVersion {
|
||||||
|
a, err := latestGithubLinuxAsset("yandex/YandexDriver")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("latest yandexdriver linux asset: %v", err)
|
||||||
|
}
|
||||||
|
version = strings.Split(a, "-")[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
bv := buildVersion(version)
|
||||||
|
u := fmt.Sprintf("https://github.com/yandex/YandexDriver/releases/download/v%s-stable/yandexdriver-%s-linux.zip", bv, version)
|
||||||
|
_, err := downloadDriver(u, yandexDriverBinary, dir)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("download Yandexdriver: %v", err)
|
||||||
|
}
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
Executable
+8
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export GO111MODULE="on"
|
||||||
|
go get github.com/markbates/pkger/cmd/pkger
|
||||||
|
go get -u github.com/mitchellh/gox # cross compile
|
||||||
|
go generate github.com/aerokube/images
|
||||||
|
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-X github.com/aerokube/images/cmd.buildStamp=$(date -u '+%Y-%m-%d_%I:%M:%S%p') -X github.com/aerokube/images/cmd.gitRevision=$(git describe --tags || git rev-parse HEAD) -s -w"
|
||||||
|
gox -os "linux darwin windows" -arch "amd64" -osarch="windows/386" -output "dist/{{.Dir}}_{{.OS}}_{{.Arch}}" -ldflags "-X github.com/aerokube/images/cmd.buildStamp=$(date -u '+%Y-%m-%d_%I:%M:%S%p') -X github.com/aerokube/images/cmd.gitRevision=$(git describe --tags || git rev-parse HEAD) -s -w"
|
||||||
Executable
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
TAGNAME=$1
|
||||||
|
GH_REF=github.com/aerokube/images.git
|
||||||
|
git config user.name "${GITHUB_REPOSITORY}"
|
||||||
|
git config user.email "aerokube@aerokube.github.com"
|
||||||
|
git remote add upstream "https://${GITHUB_TOKEN}@${GH_REF}"
|
||||||
|
git fetch upstream
|
||||||
|
|
||||||
|
git branch -r
|
||||||
|
|
||||||
|
echo "Deleting old output"
|
||||||
|
rm -rf ${GITHUB_WORKSPACE}/docs/output
|
||||||
|
mkdir ${GITHUB_WORKSPACE}/docs/output
|
||||||
|
git worktree prune
|
||||||
|
rm -rf ${GITHUB_WORKSPACE}/.git/worktrees/docs/output/
|
||||||
|
|
||||||
|
echo "Checking out gh-pages branch into docs/output"
|
||||||
|
git worktree add -B gh-pages ${GITHUB_WORKSPACE}/docs/output upstream/gh-pages
|
||||||
|
|
||||||
|
echo "Removing existing files"
|
||||||
|
mkdir -p ${GITHUB_WORKSPACE}/docs/output/${TAGNAME}
|
||||||
|
rm -rf ${GITHUB_WORKSPACE}/docs/output/${TAGNAME}/*
|
||||||
|
|
||||||
|
echo "Copying images"
|
||||||
|
cp -R ${GITHUB_WORKSPACE}/docs/img ${GITHUB_WORKSPACE}/docs/output/${TAGNAME}/img
|
||||||
|
echo "Generating docs"
|
||||||
|
docker run -v ${GITHUB_WORKSPACE}/docs/:/documents/ --name asciidoc-to-html asciidoctor/docker-asciidoctor asciidoctor -a revnumber=${TAGNAME} -D /documents/output/${TAGNAME} index.adoc
|
||||||
|
|
||||||
|
|
||||||
|
echo "Updating gh-pages branch"
|
||||||
|
cd ${GITHUB_WORKSPACE}/docs/output && git add --all && git commit -m "Publishing to gh-pages"
|
||||||
|
|
||||||
|
git push upstream HEAD:gh-pages
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export GO111MODULE="on"
|
||||||
|
go test -race -v -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aerokube/images/build"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
chromeCmd = &cobra.Command{
|
||||||
|
Use: "chrome",
|
||||||
|
Short: "build Chrome image",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
req := build.Requirements{
|
||||||
|
BrowserSource: build.BrowserSource(browserSource),
|
||||||
|
BrowserChannel: browserChannel,
|
||||||
|
DriverVersion: driverVersion,
|
||||||
|
NoCache: noCache,
|
||||||
|
TestsDir: testsDir,
|
||||||
|
RunTests: test,
|
||||||
|
IgnoreTests: ignoreTests,
|
||||||
|
Tags: tags,
|
||||||
|
PushImage: push,
|
||||||
|
}
|
||||||
|
chrome := &build.Chrome{req}
|
||||||
|
return chrome.Build()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aerokube/images/build"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
edgeCmd = &cobra.Command{
|
||||||
|
Use: "edge",
|
||||||
|
Short: "build Microsoft Edge image",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
req := build.Requirements{
|
||||||
|
BrowserSource: build.BrowserSource(browserSource),
|
||||||
|
BrowserChannel: browserChannel,
|
||||||
|
DriverVersion: driverVersion,
|
||||||
|
NoCache: noCache,
|
||||||
|
TestsDir: testsDir,
|
||||||
|
RunTests: test,
|
||||||
|
IgnoreTests: ignoreTests,
|
||||||
|
Tags: tags,
|
||||||
|
PushImage: push,
|
||||||
|
}
|
||||||
|
edge := &build.Edge{req}
|
||||||
|
return edge.Build()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aerokube/images/build"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
selenoidVersion string
|
||||||
|
seleniumVersion string
|
||||||
|
|
||||||
|
firefoxCmd = &cobra.Command{
|
||||||
|
Use: "firefox",
|
||||||
|
Short: "build Firefox image",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
req := build.Requirements{
|
||||||
|
BrowserSource: build.BrowserSource(browserSource),
|
||||||
|
BrowserChannel: browserChannel,
|
||||||
|
DriverVersion: driverVersion,
|
||||||
|
NoCache: noCache,
|
||||||
|
TestsDir: testsDir,
|
||||||
|
RunTests: test,
|
||||||
|
IgnoreTests: ignoreTests,
|
||||||
|
Tags: tags,
|
||||||
|
PushImage: push,
|
||||||
|
}
|
||||||
|
firefox := &build.Firefox{SelenoidVersion: selenoidVersion, SeleniumVersion: seleniumVersion, Requirements: req}
|
||||||
|
return firefox.Build()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
firefoxCmd.Flags().StringVar(&selenoidVersion, "selenoid-version", build.LatestVersion, "Selenoid binary version")
|
||||||
|
firefoxCmd.Flags().StringVar(&seleniumVersion, "selenium-version", "", "Selenium JAR version")
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aerokube/images/build"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
operaCmd = &cobra.Command{
|
||||||
|
Use: "opera",
|
||||||
|
Short: "build Opera image",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
req := build.Requirements{
|
||||||
|
BrowserSource: build.BrowserSource(browserSource),
|
||||||
|
BrowserChannel: browserChannel,
|
||||||
|
DriverVersion: driverVersion,
|
||||||
|
NoCache: noCache,
|
||||||
|
TestsDir: testsDir,
|
||||||
|
RunTests: test,
|
||||||
|
IgnoreTests: ignoreTests,
|
||||||
|
Tags: tags,
|
||||||
|
PushImage: push,
|
||||||
|
}
|
||||||
|
opera := &build.Opera{req}
|
||||||
|
return opera.Build()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aerokube/images/build"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
browserSource string
|
||||||
|
browserChannel string
|
||||||
|
driverVersion string
|
||||||
|
noCache bool
|
||||||
|
testsDir string
|
||||||
|
test bool
|
||||||
|
ignoreTests bool
|
||||||
|
push bool
|
||||||
|
tags []string
|
||||||
|
|
||||||
|
rootCmd = &cobra.Command{
|
||||||
|
Use: "images",
|
||||||
|
Short: "images is a tool for building Docker images with browsers",
|
||||||
|
SilenceUsage: true,
|
||||||
|
SilenceErrors: true,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return cmd.Usage()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func initFlags() {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("get current dir: %v", err)
|
||||||
|
}
|
||||||
|
defaultTestsDir := filepath.Join(cwd, "../selenoid-container-tests")
|
||||||
|
|
||||||
|
rootCmd.PersistentFlags().StringSliceVarP(&tags, "tag", "t", []string{}, "image tag")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&browserSource, "browser", "b", "", "browser APT package version, package file path, package file URL")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&driverVersion, "driver-version", "d", build.LatestVersion, "webdriver version")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&browserChannel, "channel", "c", "default", "browser channel")
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&noCache, "no-cache", "n", false, "do not use Docker cache")
|
||||||
|
rootCmd.PersistentFlags().StringVar(&testsDir, "tests-dir", defaultTestsDir, "directory with tests")
|
||||||
|
rootCmd.PersistentFlags().BoolVar(&test, "test", false, "run tests")
|
||||||
|
rootCmd.PersistentFlags().BoolVar(&ignoreTests, "ignore-tests", false, "continue to run even if tests failed")
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&push, "push", "p", false, "push image to Docker registry")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initFlags()
|
||||||
|
rootCmd.AddCommand(chromeCmd)
|
||||||
|
rootCmd.AddCommand(edgeCmd)
|
||||||
|
rootCmd.AddCommand(firefoxCmd)
|
||||||
|
rootCmd.AddCommand(operaCmd)
|
||||||
|
rootCmd.AddCommand(yandexCmd)
|
||||||
|
rootCmd.AddCommand(versionCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Execute() {
|
||||||
|
if _, err := rootCmd.ExecuteC(); err != nil {
|
||||||
|
log.Fatalf("command error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
gitRevision = "HEAD"
|
||||||
|
buildStamp = "unknown"
|
||||||
|
)
|
||||||
|
|
||||||
|
var versionCmd = &cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Show version",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("Git Revision: %s\n", gitRevision)
|
||||||
|
fmt.Printf("UTC Build Time: %s\n", buildStamp)
|
||||||
|
os.Exit(0)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aerokube/images/build"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
yandexCmd = &cobra.Command{
|
||||||
|
Use: "yandex",
|
||||||
|
Short: "build Yandex.Browser image",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
req := build.Requirements{
|
||||||
|
BrowserSource: build.BrowserSource(browserSource),
|
||||||
|
BrowserChannel: browserChannel,
|
||||||
|
DriverVersion: driverVersion,
|
||||||
|
NoCache: noCache,
|
||||||
|
TestsDir: testsDir,
|
||||||
|
RunTests: test,
|
||||||
|
IgnoreTests: ignoreTests,
|
||||||
|
Tags: tags,
|
||||||
|
PushImage: push,
|
||||||
|
}
|
||||||
|
yandexBrowser := &build.YandexBrowser{req}
|
||||||
|
return yandexBrowser.Build()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
== Android
|
||||||
|
|
||||||
|
WARNING: Hardware server or virtual machine with nested virtualization support is required to run Android images.
|
||||||
|
|
||||||
|
.Android Images
|
||||||
|
|===
|
||||||
|
| Image | Android version | Appium version
|
||||||
|
|
||||||
|
| browsers/android:4.4 | 4.4 | 1.18.1
|
||||||
|
| browsers/android:5.0 | 5.0 | 1.18.1
|
||||||
|
| browsers/android:5.1 | 5.1 | 1.18.1
|
||||||
|
| browsers/android:6.0 | 6.0 | 1.18.1
|
||||||
|
| browsers/android:7.0 | 7.0 | 1.18.1
|
||||||
|
| browsers/android:7.1 | 7.1 | 1.18.1
|
||||||
|
| browsers/android:8.0 | 8.0 | 1.18.1
|
||||||
|
| browsers/android:8.1 | 8.1 | 1.18.1
|
||||||
|
| browsers/android:9.0 | 9.0 | 1.18.1
|
||||||
|
| browsers/android:10.0 | 10.0 | 1.18.1
|
||||||
|
|===
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
====
|
||||||
|
. These images include VNC server and Android Quick Boot snapshot.
|
||||||
|
. Neither Chromedriver nor Chrome Mobile are installed. To test hybrid apps build your own image using provided automation script.
|
||||||
|
====
|
||||||
|
|
||||||
|
An example `browsers.json` for Android images looks like the following:
|
||||||
|
[source,json]
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"android": {
|
||||||
|
"default": "6.0",
|
||||||
|
"versions": {
|
||||||
|
"6.0": {
|
||||||
|
"image": "browsers/android:6.0",
|
||||||
|
"port": "4444",
|
||||||
|
"path": "/wd/hub"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
An example Java test can be found https://github.com/aerokube/demo-tests/blob/master/src/test/java/com/aerokube/selenoid/AndroidRemoteApkTest.java[here].
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
== Building Images
|
||||||
|
|
||||||
|
=== What's Inside Images
|
||||||
|
|
||||||
|
Each image consists of 3 layers:
|
||||||
|
|
||||||
|
. **Base layer** - contains stuff needed in every image: Xvfb, fonts, locales, cursor blinking fix, timezone definition and so on. You have to build this layer manually.
|
||||||
|
. **Browser layer** - contains browser binary.
|
||||||
|
. **Driver layer** - contains either respective web driver binary or corresponding Selenium \ Selenoid server version.
|
||||||
|
|
||||||
|
=== Building Procedure
|
||||||
|
|
||||||
|
Building procedure is automated with a Go binary. All Docker build files are embedded to this binary. To build the binary from source:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ go get github.com/markbates/pkger/cmd/pkger
|
||||||
|
$ go generate github.com/aerokube/images
|
||||||
|
$ go build
|
||||||
|
----
|
||||||
|
|
||||||
|
To show help:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ ./images --help
|
||||||
|
$ ./images firefox --help
|
||||||
|
----
|
||||||
|
|
||||||
|
* Before building images you can optionally clone tests repository:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ git clone https://github.com/aerokube/selenoid-container-tests.git
|
||||||
|
----
|
||||||
|
+
|
||||||
|
These tests require Java and Maven 3 to be installed. Tests directory should be cloned to this repository parent directory:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
images/ # <== this repo
|
||||||
|
selenoid-container-tests/ # <== optional tests repo
|
||||||
|
----
|
||||||
|
* To build a Firefox image use the following command:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ ./images firefox -b 79.0+build1-0ubuntu0.18.04.1 -d 0.26.0 --selenoid-version 1.10.0 -t selenoid/firefox:79.0
|
||||||
|
----
|
||||||
|
+
|
||||||
|
Here `79.0+build1-0ubuntu0.18.04.1` is `firefox` package version for Ubuntu 18.04, `1.10.10` is https://github.com/aerokube/selenoid/releases[Selenoid] version to use inside image (just use latest release version here), `selenoid/firefox:79.0` is Docker tag to be applied, `0.26.0` is http://github.com/mozilla/geckodriver/releases[Geckodriver] version to use.
|
||||||
|
+
|
||||||
|
If you wish to automatically use the latest Selenoid and Geckodriver versions - just omit respective flags or use **latest** as value:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ ./images firefox -b 79.0+build1-0ubuntu0.18.04.1 -t selenoid/firefox:79.0
|
||||||
|
----
|
||||||
|
+
|
||||||
|
If you wish to pack a local Debian package instead of APT - just replace package version with full path to **deb** file:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ ./images firefox -b /path/to/firefox_79.0+build1-0ubuntu0.18.04.1_amd64.deb -t selenoid/firefox:79.0
|
||||||
|
----
|
||||||
|
+
|
||||||
|
It is important to use package files with full version specified name because automation scripts determine browser version by parsing package file name!
|
||||||
|
+
|
||||||
|
To run the tests after building the image add `--test` flag:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ ./images firefox -b 79.0+build1-0ubuntu0.18.04.1 -t selenoid/firefox:79.0 --test
|
||||||
|
----
|
||||||
|
+
|
||||||
|
To push image after building add `--push` flag:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ ./images firefox -b 79.0+build1-0ubuntu0.18.04.1 -t selenoid/firefox:79.0 --push
|
||||||
|
----
|
||||||
|
|
||||||
|
* To build a Chrome image use the following command:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ ./images chrome -b 78.0.3904.97-1 -d 78.0.3904.70 -t selenoid/chrome:78.0
|
||||||
|
----
|
||||||
|
+
|
||||||
|
Here `78.0.3904.97-1` is `google-chrome-stable` package version for Ubuntu 18.04, `78.0.3904.70` is https://chromedriver.storage.googleapis.com/index.html[Chromedriver] version, `selenoid/chrome:78.0` is Docker tag to be applied.
|
||||||
|
+
|
||||||
|
If you wish to automatically use the latest https://chromedriver.chromium.org/downloads/version-selection[compatible] Chromedriver version - just omit respective flag or use **latest** as value:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ ./images chrome -b 78.0.3904.97-1 -d latest -t selenoid/chrome:78.0
|
||||||
|
----
|
||||||
|
|
||||||
|
* To build a Microsoft Edge image use the following command:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ ./images edge -b 88.0.673.0-1 -d 88.0.673.0 -t selenoid/edge:88.0
|
||||||
|
----
|
||||||
|
|
||||||
|
* To build an Opera image use the following command:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ ./images opera -b 64.0.3417.92 -d 77.0.3865.120 -t selenoid/opera:64.0
|
||||||
|
----
|
||||||
|
+
|
||||||
|
Here `64.0.3417.92` is `opera-stable` package version for Ubuntu 18.04, `77.0.3865.120` is https://github.com/operasoftware/operachromiumdriver/releases[Operadriver] version, `64.0` is Docker tag to be applied.
|
||||||
|
|
||||||
|
* To build a Yandex image use the following command:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ ./images yandex -b 20.4.3.268-1 -d 20.4.3.321 -t selenoid/yandex-browser:20.4
|
||||||
|
----
|
||||||
|
+
|
||||||
|
Here `20.4.3.268-1` is `yandex-browser-beta` package version for Ubuntu 18.04, `20.4.3.321` is https://github.com/yandex/YandexDriver/releases[Yandexdriver] Linux asset version, `20.4` is Docker tag to be applied.
|
||||||
|
|
||||||
|
* To build an Android image use the following command:
|
||||||
|
+
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ cd selenium
|
||||||
|
$ ./automate_android.sh
|
||||||
|
----
|
||||||
|
This command is interactive - just answer the questions and it will build an image for you. In order to bundle custom APK to image - put it to `selenium/android` directory before running the script.
|
||||||
|
|
||||||
|
=== Selecting Browser Channel
|
||||||
|
|
||||||
|
Apart from the default stable release channel, the following ones are also supported:
|
||||||
|
|
||||||
|
.Available Browser Channels
|
||||||
|
|===
|
||||||
|
| Browser | Channel | Package |
|
||||||
|
|
||||||
|
| firefox | beta | firefox (http://launchpad.net/~mozillateam/+archive/firefox-next/+packages[PPA]) |
|
||||||
|
| firefox | dev | firefox-trunk (http://launchpad.net/~ubuntu-mozilla-daily/+archive/ppa/+packages[PPA]) |
|
||||||
|
| firefox | esr | firefox-esr (http://launchpad.net/~mozillateam/+archive/ppa/+packages[PPA]) |
|
||||||
|
| chrome | beta | google-chrome-beta |
|
||||||
|
| chrome | dev | google-chrome-unstable |
|
||||||
|
| opera | beta | opera-beta |
|
||||||
|
| opera | dev | opera-developer |
|
||||||
|
|===
|
||||||
|
|
||||||
|
To build an image for one of the channels above use the `--channel` flag as follows:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ ./images firefox -b 72.0~a1~hg20191114r501767-0ubuntu0.18.04.1~umd1 --channel dev -t selenoid/firefox:72.0a1
|
||||||
|
----
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
== Chrome Developer Tools Protocol
|
||||||
|
|
||||||
|
These images should be used with tools and frameworks relying on https://chromedevtools.github.io/devtools-protocol/[Chrome Developer Tools Protocol] such as https://ppts.dev[Puppeteer].
|
||||||
|
|
||||||
|
.Chrome Developer Tools Protocol Images
|
||||||
|
|===
|
||||||
|
| Image | Chrome version
|
||||||
|
|
||||||
|
| cdtp/chrome:85.0 | 85.0.4183.121
|
||||||
|
| cdtp/chrome:86.0 | 86.0.4240.111
|
||||||
|
| cdtp/chrome:87.0 | 87.0.4280.66
|
||||||
|
| cdtp/chrome:88.0 | 88.0.4324.182
|
||||||
|
| cdtp/chrome:89.0 | 89.0.4389.72
|
||||||
|
| cdtp/chrome:90.0 | 90.0.4430.85
|
||||||
|
| cdtp/chrome:93.0 | 93.0.4577.63
|
||||||
|
|===
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
== Chrome Mobile
|
||||||
|
|
||||||
|
WARNING: Hardware server or virtual machine with nested virtualization support is required to run Chrome Mobile images.
|
||||||
|
|
||||||
|
.Chrome Mobile Images
|
||||||
|
|===
|
||||||
|
| Image | Android version | Appium version | Chromedriver version | Chrome version
|
||||||
|
|
||||||
|
| selenoid/chrome-mobile:73.0 | 9.0 | 1.18.1 | 73.0.3683.68 | 73.0.3683.90
|
||||||
|
| selenoid/chrome-mobile:74.0 | 9.0 | 1.18.1 | 74.0.3729.6 | 74.0.3729.157
|
||||||
|
| selenoid/chrome-mobile:75.0 | 9.0 | 1.18.1 | 75.0.3770.8 | 75.0.3770.143
|
||||||
|
| selenoid/chrome-mobile:76.0 | 9.0 | 1.18.1 | 76.0.3809.126 | 76.0.3809.132
|
||||||
|
| selenoid/chrome-mobile:77.0 | 9.0 | 1.18.1 | 77.0.3865.40 | 77.0.3865.116
|
||||||
|
| selenoid/chrome-mobile:78.0 | 9.0 | 1.18.1 | 78.0.3904.105 | 78.0.3904.96
|
||||||
|
| selenoid/chrome-mobile:79.0 | 9.0 | 1.18.1 | 79.0.3945.36 | 79.0.3945.136
|
||||||
|
| selenoid/chrome-mobile:80.0 | 9.0 | 1.18.1 | 80.0.3987.106 | 80.0.3987.162
|
||||||
|
| selenoid/chrome-mobile:81.0 | 9.0 | 1.18.1 | 81.0.4044.69 | 81.0.4044.138
|
||||||
|
| - | - | - | - | 82.0.x.x (release skipped by development team)
|
||||||
|
| selenoid/chrome-mobile:83.0 | 9.0 | 1.18.1 | 83.0.4103.39 | 83.0.4103.106
|
||||||
|
| selenoid/chrome-mobile:84.0 | 9.0 | 1.18.1 | 84.0.4147.30 | 84.0.4147.125
|
||||||
|
| selenoid/chrome-mobile:85.0 | 9.0 | 1.18.1 | 85.0.4183.87 | 85.0.4183.127
|
||||||
|
| selenoid/chrome-mobile:86.0 | 9.0 | 1.18.1 | 86.0.4240.22 | 86.0.4240.110
|
||||||
|
|===
|
||||||
|
|
||||||
|
An example `browsers.json` for Chrome Mobile images looks like the following:
|
||||||
|
[source,json]
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"chrome": {
|
||||||
|
"default": "mobile-75.0",
|
||||||
|
"versions": {
|
||||||
|
"mobile-75.0": {
|
||||||
|
"image": "selenoid/chrome-mobile:75.0",
|
||||||
|
"port": "4444",
|
||||||
|
"path": "/wd/hub"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
== Chrome
|
||||||
|
|
||||||
|
.Chrome Images
|
||||||
|
|===
|
||||||
|
| Image | VNC Image | Chromedriver version | Chrome version
|
||||||
|
|
||||||
|
| selenoid/chrome:48.0 | selenoid/vnc_chrome:48.0 | 2.21 | 48.0.2564.116
|
||||||
|
| selenoid/chrome:49.0 | selenoid/vnc_chrome:49.0 | 2.22 | 49.0.2623.112
|
||||||
|
| selenoid/chrome:50.0 | selenoid/vnc_chrome:50.0 | 2.22 | 50.0.2661.102
|
||||||
|
| selenoid/chrome:51.0 | selenoid/vnc_chrome:51.0 | 2.23 | 51.0.2704.106
|
||||||
|
| selenoid/chrome:52.0 | selenoid/vnc_chrome:52.0 | 2.24 | 52.0.2743.116
|
||||||
|
| selenoid/chrome:53.0 | selenoid/vnc_chrome:53.0 | 2.26 | 53.0.2785.143
|
||||||
|
| selenoid/chrome:54.0 | selenoid/vnc_chrome:54.0 | 2.27 | 54.0.2840.100
|
||||||
|
| selenoid/chrome:55.0 | selenoid/vnc_chrome:55.0 | 2.28 | 55.0.2883.87
|
||||||
|
| selenoid/chrome:56.0 | selenoid/vnc_chrome:56.0 | 2.29 | 56.0.2924.87
|
||||||
|
| selenoid/chrome:57.0 | selenoid/vnc_chrome:57.0 | 2.29 | 57.0.2987.110
|
||||||
|
| selenoid/chrome:58.0 | selenoid/vnc_chrome:58.0 | 2.29 | 58.0.3029.81
|
||||||
|
| selenoid/chrome:59.0 | selenoid/vnc_chrome:59.0 | 2.30 | 59.0.3071.86
|
||||||
|
| selenoid/chrome:60.0 | selenoid/vnc_chrome:60.0 | 2.31 | 60.0.3112.90
|
||||||
|
| selenoid/chrome:61.0 | selenoid/vnc_chrome:61.0 | 2.32 | 61.0.3163.79
|
||||||
|
| selenoid/chrome:62.0 | selenoid/vnc_chrome:62.0 | 2.33 | 62.0.3202.62
|
||||||
|
| selenoid/chrome:63.0 | selenoid/vnc_chrome:63.0 | 2.33 | 63.0.3239.84
|
||||||
|
| selenoid/chrome:64.0 | selenoid/vnc_chrome:64.0 | 2.35 | 64.0.3282.119
|
||||||
|
| selenoid/chrome:65.0 | selenoid/vnc_chrome:65.0 | 2.38 | 65.0.3325.181
|
||||||
|
| selenoid/chrome:66.0 | selenoid/vnc_chrome:66.0 | 2.38 | 66.0.3359.117
|
||||||
|
| selenoid/chrome:67.0 | selenoid/vnc_chrome:67.0 | 2.39 | 67.0.3396.62
|
||||||
|
| selenoid/chrome:68.0 | selenoid/vnc_chrome:68.0 | 2.41 | 68.0.3440.106
|
||||||
|
| selenoid/chrome:69.0 | selenoid/vnc_chrome:69.0 | 2.42 | 69.0.3497.100
|
||||||
|
| selenoid/chrome:70.0 | selenoid/vnc_chrome:70.0 | 2.44 | 70.0.3538.110
|
||||||
|
| selenoid/chrome:71.0 | selenoid/vnc_chrome:71.0 | 2.44 | 71.0.3578.80
|
||||||
|
| selenoid/chrome:72.0 | selenoid/vnc_chrome:72.0 | 2.46 | 72.0.3626.121
|
||||||
|
| selenoid/chrome:73.0 | selenoid/vnc_chrome:73.0 | 73.0.3683.68 | 73.0.3683.75
|
||||||
|
| selenoid/chrome:74.0 | selenoid/vnc_chrome:74.0 | 74.0.3729.6 | 74.0.3729.157
|
||||||
|
| selenoid/chrome:75.0 | selenoid/vnc_chrome:75.0 | 75.0.3770.90 | 75.0.3770.90
|
||||||
|
| selenoid/chrome:76.0 | selenoid/vnc_chrome:76.0 | 76.0.3809.87 | 76.0.3809.68
|
||||||
|
| selenoid/chrome:77.0 | selenoid/vnc_chrome:77.0 | 77.0.3865.40 | 77.0.3865.75
|
||||||
|
| selenoid/chrome:78.0 | selenoid/vnc_chrome:78.0 | 78.0.3904.87 | 78.0.3904.70
|
||||||
|
| selenoid/chrome:79.0 | selenoid/vnc_chrome:79.0 | 79.0.3945.36 | 79.0.3945.79
|
||||||
|
| selenoid/chrome:80.0 | selenoid/vnc_chrome:80.0 | 80.0.3987.106 | 80.0.3987.132
|
||||||
|
| selenoid/chrome:81.0 | selenoid/vnc_chrome:81.0 | 81.0.4044.138 | 81.0.4044.138
|
||||||
|
| - | - | - | 82.0.x.x (release skipped by development team)
|
||||||
|
| selenoid/chrome:83.0 | selenoid/vnc_chrome:83.0 | 83.0.4103.39 | 83.0.4103.61
|
||||||
|
| selenoid/chrome:84.0 | selenoid/vnc_chrome:84.0 | 84.0.4147.30 | 84.0.4147.89
|
||||||
|
| selenoid/chrome:85.0 | selenoid/vnc_chrome:85.0 | 85.0.4183.87 | 85.0.4183.121
|
||||||
|
| selenoid/chrome:86.0 | selenoid/vnc_chrome:86.0 | 86.0.4240.22 | 86.0.4240.75
|
||||||
|
| selenoid/chrome:87.0 | selenoid/vnc_chrome:87.0 | 87.0.4280.20 | 87.0.4280.66
|
||||||
|
| selenoid/chrome:88.0 | selenoid/vnc_chrome:88.0 | 88.0.4324.27 | 88.0.4324.96
|
||||||
|
| selenoid/chrome:89.0 | selenoid/vnc_chrome:89.0 | 89.0.4389.23 | 89.0.4389.72
|
||||||
|
| selenoid/chrome:90.0 | selenoid/vnc_chrome:90.0 | 90.0.4430.24 | 90.0.4430.72
|
||||||
|
| selenoid/chrome:91.0 | selenoid/vnc_chrome:91.0 | 91.0.4472.101 | 91.0.4472.106
|
||||||
|
| selenoid/chrome:92.0 | selenoid/vnc_chrome:92.0 | 92.0.4515.107 | 92.0.4515.131
|
||||||
|
| selenoid/chrome:93.0 | selenoid/vnc_chrome:93.0 | 93.0.4577.63 | 93.0.4577.63
|
||||||
|
| selenoid/chrome:94.0 | selenoid/vnc_chrome:94.0 | 94.0.4606.61 | 94.0.4606.61
|
||||||
|
| selenoid/chrome:95.0 | selenoid/vnc_chrome:95.0 | 95.0.4638.17 | 95.0.4638.54
|
||||||
|
| selenoid/chrome:96.0 | selenoid/vnc_chrome:96.0 | 96.0.4664.45 | 96.0.4664.45
|
||||||
|
| selenoid/chrome:97.0 | selenoid/vnc_chrome:97.0 | 97.0.4692.71 | 97.0.4692.71
|
||||||
|
| selenoid/chrome:98.0 | selenoid/vnc_chrome:97.0 | 98.0.4758.48 | 98.0.4758.80
|
||||||
|
| selenoid/chrome:99.0 | selenoid/vnc_chrome:99.0 | 99.0.4844.51 | 99.0.4844.51
|
||||||
|
| selenoid/chrome:100.0 | selenoid/vnc_chrome:100.0 | 100.0.4896.20 | 100.0.4896.60
|
||||||
|
| selenoid/chrome:101.0 | selenoid/vnc_chrome:101.0 | 101.0.4951.41 | 101.0.4951.54
|
||||||
|
|===
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
====
|
||||||
|
. These images work with any modern Selenium client version.
|
||||||
|
. Images for older Chrome versions were not built because we have no Debian packages. If you have such packages - we could create more images.
|
||||||
|
====
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
== Cypress
|
||||||
|
|
||||||
|
These images are used to run https://cypress.io/[Cypress] tests in parallel.
|
||||||
|
|
||||||
|
.Chromium Images
|
||||||
|
|===
|
||||||
|
| Image | Aliases | Cypress version | Chromium version
|
||||||
|
|
||||||
|
| browsers/cypress-chromium:cypress-6.8.0 | - | 6.8.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-6.9.1 | - | 6.9.1 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-7.0.0 | - | 7.0.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-7.0.1 | - | 7.0.1 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-7.1.0 | - | 7.1.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-7.2.0 | - | 7.2.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-7.3.0 | - | 7.3.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-7.4.0 | - | 7.4.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-7.5.0 | - | 7.5.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-7.6.0 | - | 7.6.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-7.7.0 | - | 7.7.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-8.0.0 | - | 8.0.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-8.1.0 | - | 8.1.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-8.2.0 | - | 8.2.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-8.3.0 | browsers/cypress-chromium:91.0 | 8.3.0 | 91.0.4472.101
|
||||||
|
| browsers/cypress-chromium:cypress-8.3.1 | - | 8.3.1 | 92.0.4515.159
|
||||||
|
| browsers/cypress-chromium:cypress-8.4.0 | - | 8.4.0 | 92.0.4515.159
|
||||||
|
| browsers/cypress-chromium:cypress-8.4.1 | browsers/cypress-chromium:92.0 | 8.4.1 | 92.0.4515.159
|
||||||
|
| browsers/cypress-chromium:cypress-8.5.0 | browsers/cypress-chromium:93.0 | 8.5.0 | 93.0.4577.63
|
||||||
|
| browsers/cypress-chromium:cypress-8.6.0 | - | 8.6.0 | 94.0.4606.71
|
||||||
|
| browsers/cypress-chromium:cypress-8.7.0 | browsers/cypress-chromium:94.0 | 8.7.0 | 94.0.4606.81
|
||||||
|
| browsers/cypress-chromium:cypress-9.0.0 | - | 9.0.0 | 95.0.4638.69
|
||||||
|
| browsers/cypress-chromium:cypress-9.1.0 | - | 9.1.0 | 95.0.4638.69
|
||||||
|
| browsers/cypress-chromium:cypress-9.1.1 | - | 9.1.1 | 95.0.4638.69
|
||||||
|
| browsers/cypress-chromium:cypress-9.2.0 | - | 9.2.0 | 95.0.4638.69
|
||||||
|
| browsers/cypress-chromium:cypress-9.2.1 | browsers/cypress-chromium:95.0 | 9.2.1 | 95.0.4638.69
|
||||||
|
| browsers/cypress-chromium:cypress-9.3.0 | - | 9.3.0 | 97.0.4692.71
|
||||||
|
| browsers/cypress-chromium:cypress-9.3.1 | - | 9.3.1 | 97.0.4692.71
|
||||||
|
| browsers/cypress-chromium:cypress-9.4.0 | - | 9.4.0 | 97.0.4692.71
|
||||||
|
| browsers/cypress-chromium:cypress-9.4.1 | - | 9.4.1 | 97.0.4692.71
|
||||||
|
| browsers/cypress-chromium:cypress-9.5.0 | - | 9.5.0 | 97.0.4692.71
|
||||||
|
| browsers/cypress-chromium:cypress-9.5.1 | browsers/cypress-chromium:97.0 | 9.5.1 | 97.0.4692.71
|
||||||
|
| browsers/cypress-chromium:cypress-9.5.2 | - | 9.5.2 | 99.0.4844.51
|
||||||
|
| browsers/cypress-chromium:cypress-9.5.3 | - | 9.5.3 | 99.0.4844.84
|
||||||
|
| browsers/cypress-chromium:cypress-9.5.4 | browsers/cypress-chromium:99.0 | 9.5.4 | 99.0.4844.84
|
||||||
|
| browsers/cypress-chromium:cypress-9.6.0 | - | 9.6.0 | 100.0.4896.127
|
||||||
|
| browsers/cypress-chromium:cypress-9.6.1 | browsers/cypress-chromium:100.0 | 9.6.1 | 100.0.4896.127
|
||||||
|
|===
|
||||||
|
|
||||||
|
.Chrome Images
|
||||||
|
|===
|
||||||
|
| Image | Aliases | Cypress version | Chrome version
|
||||||
|
|
||||||
|
| browsers/cypress-chrome:cypress-6.8.0 | - | 6.8.0 | 91.0.4472.114
|
||||||
|
| browsers/cypress-chrome:cypress-6.9.1 | - | 6.9.1 | 91.0.4472.114
|
||||||
|
| browsers/cypress-chrome:cypress-7.0.0 | - | 7.0.0 | 91.0.4472.114
|
||||||
|
| browsers/cypress-chrome:cypress-7.0.1 | - | 7.0.1 | 91.0.4472.114
|
||||||
|
| browsers/cypress-chrome:cypress-7.1.0 | - | 7.1.0 | 91.0.4472.114
|
||||||
|
| browsers/cypress-chrome:cypress-7.2.0 | - | 7.2.0 | 91.0.4472.114
|
||||||
|
| browsers/cypress-chrome:cypress-7.3.0 | - | 7.3.0 | 91.0.4472.114
|
||||||
|
| browsers/cypress-chrome:cypress-7.4.0 | - | 7.4.0 | 91.0.4472.114
|
||||||
|
| browsers/cypress-chrome:cypress-7.5.0 | - | 7.5.0 | 91.0.4472.114
|
||||||
|
| browsers/cypress-chrome:cypress-7.6.0 | - | 7.6.0 | 91.0.4472.114
|
||||||
|
| browsers/cypress-chrome:cypress-7.7.0 | - | 7.7.0 | 91.0.4472.114
|
||||||
|
| browsers/cypress-chrome:cypress-8.0.0 | browsers/cypress-chrome:91.0 | 8.0.0 | 91.0.4472.164
|
||||||
|
| browsers/cypress-chrome:cypress-8.1.0 | - | 8.1.0 | 92.0.4515.107
|
||||||
|
| browsers/cypress-chrome:cypress-8.2.0 | - | 8.2.0 | 92.0.4515.131
|
||||||
|
| browsers/cypress-chrome:cypress-8.3.0 | - | 8.3.0 | 92.0.4515.159
|
||||||
|
| browsers/cypress-chrome:cypress-8.3.1 | browsers/cypress-chrome:92.0 | 8.3.1 | 92.0.4515.159
|
||||||
|
| browsers/cypress-chrome:cypress-8.4.0 | - | 8.4.0 | 93.0.4577.82
|
||||||
|
| browsers/cypress-chrome:cypress-8.4.1 | browsers/cypress-chrome:93.0 | 8.4.1 | 93.0.4577.82
|
||||||
|
| browsers/cypress-chrome:cypress-8.5.0 | - | 8.5.0 | 94.0.4606.61
|
||||||
|
| browsers/cypress-chrome:cypress-8.6.0 | browsers/cypress-chrome:94.0 | 8.6.0 | 94.0.4606.81
|
||||||
|
| browsers/cypress-chrome:cypress-8.7.0 | - | 8.7.0 | 95.0.4638.54
|
||||||
|
| browsers/cypress-chrome:cypress-9.0.0 | - | 9.0.0 | 96.0.4664.45
|
||||||
|
| browsers/cypress-chrome:cypress-9.1.0 | - | 9.1.0 | 96.0.4664.45
|
||||||
|
| browsers/cypress-chrome:cypress-9.1.1 | - | 9.1.1 | 96.0.4664.45
|
||||||
|
| browsers/cypress-chrome:cypress-9.2.0 | browsers/cypress-chrome:96.0 | 9.2.0 | 96.0.4664.110
|
||||||
|
| browsers/cypress-chrome:cypress-9.2.1 | - | 9.2.1 | 97.0.4692.71
|
||||||
|
| browsers/cypress-chrome:cypress-9.3.0 | - | 9.3.0 | 97.0.4692.71
|
||||||
|
| browsers/cypress-chrome:cypress-9.3.1 | browsers/cypress-chrome:97.0 | 9.3.1 | 97.0.4692.71
|
||||||
|
| browsers/cypress-chrome:cypress-9.4.0 | - | 9.4.0 | 98.0.4758.80
|
||||||
|
| browsers/cypress-chrome:cypress-9.4.1 | - | 9.4.1 | 98.0.4758.80
|
||||||
|
| browsers/cypress-chrome:cypress-9.5.0 | browsers/cypress-chrome:98.0 | 9.5.0 | 98.0.4758.102
|
||||||
|
| browsers/cypress-chrome:cypress-9.5.1 | - | 9.5.1 | 99.0.4844.51
|
||||||
|
| browsers/cypress-chrome:cypress-9.5.2 | browsers/cypress-chrome:99.0 | 9.5.2 | 99.0.4844.74
|
||||||
|
| browsers/cypress-chrome:cypress-9.5.3 | - | 9.5.3 | 100.0.4896.60
|
||||||
|
| browsers/cypress-chrome:cypress-9.5.4 | - | 9.5.4 | 100.0.4896.88
|
||||||
|
| browsers/cypress-chrome:cypress-9.6.0 | browsers/cypress-chrome:100.0 | 9.6.0 | 100.0.4896.127
|
||||||
|
| browsers/cypress-chrome:cypress-9.6.1 | browsers/cypress-chrome:101.0 | 9.6.1 | 101.0.4951.64
|
||||||
|
|===
|
||||||
|
|
||||||
|
.Electron Images
|
||||||
|
|===
|
||||||
|
| Image | Cypress version
|
||||||
|
|
||||||
|
| browsers/cypress-electron:cypress-6.8.0 | 6.8.0
|
||||||
|
| browsers/cypress-electron:cypress-6.9.1 | 6.9.1
|
||||||
|
| browsers/cypress-electron:cypress-7.0.0 | 7.0.0
|
||||||
|
| browsers/cypress-electron:cypress-7.0.1 | 7.0.1
|
||||||
|
| browsers/cypress-electron:cypress-7.1.0 | 7.1.0
|
||||||
|
| browsers/cypress-electron:cypress-7.2.0 | 7.2.0
|
||||||
|
| browsers/cypress-electron:cypress-7.3.0 | 7.3.0
|
||||||
|
| browsers/cypress-electron:cypress-7.4.0 | 7.4.0
|
||||||
|
| browsers/cypress-electron:cypress-7.5.0 | 7.5.0
|
||||||
|
| browsers/cypress-electron:cypress-7.6.0 | 7.6.0
|
||||||
|
| browsers/cypress-electron:cypress-7.7.0 | 7.7.0
|
||||||
|
| browsers/cypress-electron:cypress-8.0.0 | 8.0.0
|
||||||
|
| browsers/cypress-electron:cypress-8.1.0 | 8.1.0
|
||||||
|
| browsers/cypress-electron:cypress-8.2.0 | 8.2.0
|
||||||
|
| browsers/cypress-electron:cypress-8.3.0 | 8.3.0
|
||||||
|
| browsers/cypress-electron:cypress-8.3.1 | 8.3.1
|
||||||
|
| browsers/cypress-electron:cypress-8.4.0 | 8.4.0
|
||||||
|
| browsers/cypress-electron:cypress-8.4.1 | 8.4.1
|
||||||
|
| browsers/cypress-electron:cypress-8.5.0 | 8.5.0
|
||||||
|
| browsers/cypress-electron:cypress-8.6.0 | 8.6.0
|
||||||
|
| browsers/cypress-electron:cypress-8.7.0 | 8.7.0
|
||||||
|
| browsers/cypress-electron:cypress-9.0.0 | 9.0.0
|
||||||
|
| browsers/cypress-electron:cypress-9.1.0 | 9.1.0
|
||||||
|
| browsers/cypress-electron:cypress-9.1.1 | 9.1.1
|
||||||
|
| browsers/cypress-electron:cypress-9.2.0 | 9.2.0
|
||||||
|
| browsers/cypress-electron:cypress-9.2.1 | 9.2.1
|
||||||
|
| browsers/cypress-electron:cypress-9.3.0 | 9.3.0
|
||||||
|
| browsers/cypress-electron:cypress-9.3.1 | 9.3.1
|
||||||
|
| browsers/cypress-electron:cypress-9.4.0 | 9.4.0
|
||||||
|
| browsers/cypress-electron:cypress-9.4.1 | 9.4.1
|
||||||
|
| browsers/cypress-electron:cypress-9.5.0 | 9.5.0
|
||||||
|
| browsers/cypress-electron:cypress-9.5.1 | 9.5.1
|
||||||
|
| browsers/cypress-electron:cypress-9.5.2 | 9.5.2
|
||||||
|
| browsers/cypress-electron:cypress-9.5.3 | 9.5.3
|
||||||
|
| browsers/cypress-electron:cypress-9.5.4 | 9.5.4
|
||||||
|
| browsers/cypress-electron:cypress-9.6.0 | 9.6.0
|
||||||
|
| browsers/cypress-electron:cypress-9.6.1 | 9.6.1
|
||||||
|
|===
|
||||||
|
|
||||||
|
.Microsoft Edge Images
|
||||||
|
|===
|
||||||
|
| Image | Aliases | Cypress version | Edge version
|
||||||
|
|
||||||
|
| browsers/cypress-edge:cypress-6.8.0 | - | 6.8.0 | 92.0.902.15
|
||||||
|
| browsers/cypress-edge:cypress-6.9.0 | - | 6.9.0 | 92.0.902.15
|
||||||
|
| browsers/cypress-edge:cypress-6.9.1 | - | 6.9.1 | 92.0.902.15
|
||||||
|
| browsers/cypress-edge:cypress-7.0.0 | - | 7.0.0 | 92.0.902.15
|
||||||
|
| browsers/cypress-edge:cypress-7.0.1 | - | 7.0.1 | 92.0.902.15
|
||||||
|
| browsers/cypress-edge:cypress-7.1.0 | - | 7.1.0 | 92.0.902.15
|
||||||
|
| browsers/cypress-edge:cypress-7.2.0 | - | 7.2.0 | 92.0.902.15
|
||||||
|
| browsers/cypress-edge:cypress-7.3.0 | - | 7.3.0 | 92.0.902.15
|
||||||
|
| browsers/cypress-edge:cypress-7.4.0 | - | 7.4.0 | 92.0.902.15
|
||||||
|
| browsers/cypress-edge:cypress-7.5.0 | - | 7.5.0 | 92.0.902.15
|
||||||
|
| browsers/cypress-edge:cypress-7.6.0 | - | 7.6.0 | 92.0.902.15
|
||||||
|
| browsers/cypress-edge:cypress-7.7.0 | - | 7.7.0 | 92.0.902.40
|
||||||
|
| browsers/cypress-edge:cypress-8.0.0 | - | 8.0.0 | 92.0.902.49
|
||||||
|
| browsers/cypress-edge:cypress-8.1.0 | browsers/cypress-edge:92.0 | 8.1.0 | 92.0.902.62
|
||||||
|
| browsers/cypress-edge:cypress-8.2.0 | - | 8.2.0 | 93.0.961.11
|
||||||
|
| browsers/cypress-edge:cypress-8.3.0 | - | 8.3.0 | 93.0.961.18
|
||||||
|
| browsers/cypress-edge:cypress-8.3.1 | browsers/cypress-edge:93.0 | 8.3.1 | 93.0.961.33
|
||||||
|
| browsers/cypress-edge:cypress-8.4.0 | - | 8.4.0 | 94.0.992.19
|
||||||
|
| browsers/cypress-edge:cypress-8.4.1 | browsers/cypress-edge:94.0 | 8.4.1 | 94.0.992.23
|
||||||
|
| browsers/cypress-edge:cypress-8.5.0 | - | 8.5.0 | 95.0.1020.9
|
||||||
|
| browsers/cypress-edge:cypress-8.6.0 | - | 8.6.0 | 95.0.1020.20
|
||||||
|
| browsers/cypress-edge:cypress-8.7.0 | - | 8.7.0 | 95.0.1020.30
|
||||||
|
| browsers/cypress-edge:cypress-9.0.0 | browsers/cypress-edge:95.0 | 9.0.0 | 95.0.1020.53
|
||||||
|
| browsers/cypress-edge:cypress-9.1.0 | - | 9.1.0 | 96.0.4664.45
|
||||||
|
| browsers/cypress-edge:cypress-9.1.1 | - | 9.1.1 | 96.0.1054.43
|
||||||
|
| browsers/cypress-edge:cypress-9.2.0 | browsers/cypress-edge:96.0 | 9.2.0 | 96.0.1054.62
|
||||||
|
| browsers/cypress-edge:cypress-9.2.1 | - | 9.2.1 | 97.0.1072.55
|
||||||
|
| browsers/cypress-edge:cypress-9.3.0 | - | 9.3.0 | 97.0.1072.62
|
||||||
|
| browsers/cypress-edge:cypress-9.3.1 | - | 9.3.1 | 97.0.1072.62
|
||||||
|
| browsers/cypress-edge:cypress-9.4.0 | - | 9.4.0 | 97.0.1072.76
|
||||||
|
| browsers/cypress-edge:cypress-9.4.1 | browsers/cypress-edge:97.0 | 9.4.1 | 97.0.1072.76
|
||||||
|
| browsers/cypress-edge:cypress-9.5.0 | - | 9.5.0 | 98.0.1108.56
|
||||||
|
| browsers/cypress-edge:cypress-9.5.1 | browsers/cypress-edge:98.0 | 9.5.1 | 98.0.1108.62
|
||||||
|
| browsers/cypress-edge:cypress-9.5.2 | - | 9.5.2 | 99.0.1150.38
|
||||||
|
| browsers/cypress-edge:cypress-9.5.3 | browsers/cypress-edge:99.0 | 9.5.3 | 99.0.1150.55
|
||||||
|
| browsers/cypress-edge:cypress-9.5.4 | - | 9.5.4 | 100.0.1185.39
|
||||||
|
| browsers/cypress-edge:cypress-9.6.0 | browsers/cypress-edge:100.0 | 9.6.0 | 100.0.1185.50
|
||||||
|
| browsers/cypress-edge:cypress-9.6.1 | browsers/cypress-edge:101.0 | 9.6.1 | 101.0.1210.39
|
||||||
|
|===
|
||||||
|
|
||||||
|
.Firefox Images
|
||||||
|
|===
|
||||||
|
| Image | Aliases | Cypress version | Edge version
|
||||||
|
|
||||||
|
| browsers/cypress-firefox:cypress-6.8.0 | - | 6.8.0 | 89.0.1
|
||||||
|
| browsers/cypress-firefox:cypress-6.9.1 | - | 6.9.1 | 89.0.1
|
||||||
|
| browsers/cypress-firefox:cypress-7.0.0 | - | 7.0.0 | 89.0.1
|
||||||
|
| browsers/cypress-firefox:cypress-7.0.1 | - | 7.0.1 | 89.0.1
|
||||||
|
| browsers/cypress-firefox:cypress-7.1.0 | - | 7.1.0 | 89.0.1
|
||||||
|
| browsers/cypress-firefox:cypress-7.2.0 | - | 7.2.0 | 89.0.1
|
||||||
|
| browsers/cypress-firefox:cypress-7.3.0 | - | 7.3.0 | 89.0.1
|
||||||
|
| browsers/cypress-firefox:cypress-7.4.0 | - | 7.4.0 | 89.0.1
|
||||||
|
| browsers/cypress-firefox:cypress-7.5.0 | - | 7.5.0 | 89.0.1
|
||||||
|
| browsers/cypress-firefox:cypress-7.6.0 | - | 7.6.0 | 89.0.1
|
||||||
|
| browsers/cypress-firefox:cypress-7.7.0 | browsers/cypress-firefox:89.0 | 7.7.0 | 89.0.2
|
||||||
|
| browsers/cypress-firefox:cypress-8.0.0 | - | 8.0.0 | 90.0
|
||||||
|
| browsers/cypress-firefox:cypress-8.1.0 | - | 8.1.0 | 90.0
|
||||||
|
| browsers/cypress-firefox:cypress-8.2.0 | browsers/cypress-firefox:90.0 | 8.2.0 | 90.0.2
|
||||||
|
| browsers/cypress-firefox:cypress-8.3.0 | - | 8.3.0 | 91.0
|
||||||
|
| browsers/cypress-firefox:cypress-8.3.1 | browsers/cypress-firefox:91.0 | 8.3.1 | 91.0.2
|
||||||
|
| browsers/cypress-firefox:cypress-8.4.0 | - | 8.4.0 | 92.0
|
||||||
|
| browsers/cypress-firefox:cypress-8.4.1 | - | 8.4.1 | 92.0
|
||||||
|
| browsers/cypress-firefox:cypress-8.5.0 | browsers/cypress-firefox:92.0 | 8.5.0 | 92.0
|
||||||
|
| browsers/cypress-firefox:cypress-8.6.0 | - | 8.6.0 | 93.0
|
||||||
|
| browsers/cypress-firefox:cypress-8.7.0 | browsers/cypress-firefox:93.0 | 8.7.0 | 93.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.0.0 | - | 9.0.0 | 94.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.1.0 | - | 9.1.0 | 94.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.1.1 | browsers/cypress-firefox:94.0 | 9.1.1 | 94.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.2.0 | - | 9.2.0 | 95.0.1
|
||||||
|
| browsers/cypress-firefox:cypress-9.2.1 | browsers/cypress-firefox:95.0 | 9.2.1 | 95.0.1
|
||||||
|
| browsers/cypress-firefox:cypress-9.3.0 | - | 9.3.0 | 96.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.3.1 | - | 9.3.1 | 96.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.4.0 | - | 9.4.0 | 96.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.4.1 | browsers/cypress-firefox:96.0 | 9.4.1 | 96.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.5.0 | - | 9.5.0 | 97.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.5.1 | browsers/cypress-firefox:97.0 | 9.5.1 | 97.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.5.2 | - | 9.5.2 | 98.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.5.3 | browsers/cypress-firefox:98.0 | 9.5.3 | 98.0.2
|
||||||
|
| browsers/cypress-firefox:cypress-9.5.4 | - | 9.5.4 | 99.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.6.0 | browsers/cypress-firefox:99.0 | 9.6.0 | 99.0
|
||||||
|
| browsers/cypress-firefox:cypress-9.6.1 | browsers/cypress-firefox:100.0 | 9.6.1 | 100.0
|
||||||
|
|===
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-23854888-2"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'UA-23854888-2');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Yandex.Metrika counter -->
|
||||||
|
<script type="text/javascript" >
|
||||||
|
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
||||||
|
m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
|
||||||
|
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
|
||||||
|
|
||||||
|
ym(43539754, "init", {
|
||||||
|
clickmap:true,
|
||||||
|
trackLinks:true,
|
||||||
|
accurateTrackBounce:true,
|
||||||
|
webvisor:true,
|
||||||
|
trackHash:true
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<noscript><div><img src="https://mc.yandex.ru/watch/43539754" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
|
||||||
|
<!-- /Yandex.Metrika counter -->
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<base href="https://aerokube.com/images/{revnumber}/">
|
||||||
|
<meta property="og:image:height" content="1106">
|
||||||
|
<meta property="og:image:width" content="2113">
|
||||||
|
<meta property="og:title"
|
||||||
|
content="Browser Images | A set of browser images for Selenoid and Moon">
|
||||||
|
<meta property="og:description"
|
||||||
|
content="The most popular browser environments you need to run your tests">
|
||||||
|
<meta property="og:url" content="https://aerokube.com/images/{revnumber}">
|
||||||
|
<meta property="og:image"
|
||||||
|
content="https://aerokube.com/images/{revnumber}/img/og-image.jpg">
|
||||||
|
<meta name="twitter:site" content="@aerokube">
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
== Features
|
||||||
|
|
||||||
|
=== Adding Custom Root Certification Authority
|
||||||
|
|
||||||
|
In corporate networks tested environments are often using self-signed https://en.wikipedia.org/wiki/Transport_Layer_Security[TLS] certificates. Such certificates are issued by a https://en.wikipedia.org/wiki/Root_certificate[root certification authority] not known to browsers. When trying to open an HTTPS web-page using a self-signed certificate, your browser by default will refuse to do this saying that "Your connection is not private" or "This connection is untrusted". In Selenium tests you can use a standard capability (`acceptInsecureCerts = true`) to ignore such certificate errors but this will not work when your web-page is using https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HSTS].
|
||||||
|
|
||||||
|
In order to work properly with self-signed certificates, you have to add your root certification authority certificate to a list of trusted certificates. Our images allow to do this using environment variables. For every root certificate stored in `cert.pem` file:
|
||||||
|
|
||||||
|
. Encode `cert.pem` file contents to https://en.wikipedia.org/wiki/Base64[Base64]:
|
||||||
|
|
||||||
|
CERT_CONTENTS=$(cat cert.pem | base64 -w0)
|
||||||
|
|
||||||
|
+
|
||||||
|
In case of MacOS respective command would be:
|
||||||
|
|
||||||
|
CERT_CONTENTS=$(cat cert.pem | base64)
|
||||||
|
|
||||||
|
. Set an environment variable to browser image:
|
||||||
|
|
||||||
|
ROOT_CA_<cert-name>="$CERT_CONTENTS"
|
||||||
|
|
||||||
|
+
|
||||||
|
In that case `<cert-name>` will be used as certificate name in the browser certificates storage. For example:
|
||||||
|
|
||||||
|
ROOT_CA_MY_CERT="LS0tL....=="
|
||||||
|
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
== Firefox
|
||||||
|
|
||||||
|
.Firefox Images with Selenium Server
|
||||||
|
|===
|
||||||
|
| Image | VNC Image | Selenium Version | Firefox Version | Client Version
|
||||||
|
|
||||||
|
| selenoid/firefox:3.6 | selenoid/vnc_firefox:3.6 | 2.20.0 | 3.6.16 i386 (dialogs may not work) .7+<.^|
|
||||||
|
**Java:** 2.53.1 and below
|
||||||
|
**Python:** not supported
|
||||||
|
**selenium-webdriver.js:** not supported
|
||||||
|
| selenoid/firefox:4.0 | selenoid/vnc_firefox:4.0 | 2.20.0 | 4.0.1 i386
|
||||||
|
| selenoid/firefox:5.0 | selenoid/vnc_firefox:5.0 | 2.20.0 | 5.0.1 i386
|
||||||
|
| selenoid/firefox:6.0 | selenoid/vnc_firefox:6.0 | 2.20.0 | 6.0.2 i386
|
||||||
|
| selenoid/firefox:7.0 | selenoid/vnc_firefox:7.0 | 2.20.0 | 7.0.1 i386
|
||||||
|
| selenoid/firefox:8.0 | selenoid/vnc_firefox:8.0 | 2.20.0 | 8.0.1 i386
|
||||||
|
| selenoid/firefox:9.0 | selenoid/vnc_firefox:9.0 | 2.20.0 | 9.0.1
|
||||||
|
| selenoid/firefox:10.0 | selenoid/vnc_firefox:10.0 | 2.32.0 | 10.0.2 .13+<.^|
|
||||||
|
**Java:** any modern version
|
||||||
|
**Python:** not supported
|
||||||
|
**selenium-webdriver.js:** not supported
|
||||||
|
| selenoid/firefox:11.0 | selenoid/vnc_firefox:11.0 | 2.32.0 | 11.0
|
||||||
|
| selenoid/firefox:12.0 | selenoid/vnc_firefox:12.0 | 2.32.0 | 12.0
|
||||||
|
| selenoid/firefox:13.0 | selenoid/vnc_firefox:13.0 | 2.32.0 | 13.0
|
||||||
|
| selenoid/firefox:14.0 | selenoid/vnc_firefox:14.0 | 2.32.0 | 14.0.1
|
||||||
|
| selenoid/firefox:15.0 | selenoid/vnc_firefox:15.0 | 2.32.0 | 15.0.1
|
||||||
|
| selenoid/firefox:16.0 | selenoid/vnc_firefox:16.0 | 2.32.0 | 16.0.2
|
||||||
|
| selenoid/firefox:17.0 | selenoid/vnc_firefox:17.0 | 2.32.0 | 17.0.1
|
||||||
|
| selenoid/firefox:18.0 | selenoid/vnc_firefox:18.0 | 2.32.0 | 18.0.2
|
||||||
|
| selenoid/firefox:19.0 | selenoid/vnc_firefox:19.0 | 2.32.0 | 19.0.2
|
||||||
|
| selenoid/firefox:20.0 | selenoid/vnc_firefox:20.0 | 2.32.0 | 20.0
|
||||||
|
| selenoid/firefox:21.0 | selenoid/vnc_firefox:21.0 | 2.32.0 | 21.0
|
||||||
|
| selenoid/firefox:22.0 | selenoid/vnc_firefox:22.0 | 2.32.0 | 22.0
|
||||||
|
| selenoid/firefox:23.0 | selenoid/vnc_firefox:23.0 | 2.35.0 | 23.0.1 .25+<.^| Any modern client version
|
||||||
|
| selenoid/firefox:24.0 | selenoid/vnc_firefox:24.0 | 2.39.0 | 24.0
|
||||||
|
| selenoid/firefox:25.0 | selenoid/vnc_firefox:25.0 | 2.39.0 | 25.0.1
|
||||||
|
| selenoid/firefox:26.0 | selenoid/vnc_firefox:26.0 | 2.39.0 | 26.0
|
||||||
|
| selenoid/firefox:27.0 | selenoid/vnc_firefox:27.0 | 2.40.0 | 27.0.1
|
||||||
|
| selenoid/firefox:28.0 | selenoid/vnc_firefox:28.0 | 2.41.0 | 28.0
|
||||||
|
| selenoid/firefox:29.0 | selenoid/vnc_firefox:29.0 | 2.43.1 | 29.0.1
|
||||||
|
| selenoid/firefox:30.0 | selenoid/vnc_firefox:30.0 | 2.43.1 | 30.0
|
||||||
|
| selenoid/firefox:31.0 | selenoid/vnc_firefox:31.0 | 2.44.0 | 31.0
|
||||||
|
| selenoid/firefox:32.0 | selenoid/vnc_firefox:32.0 | 2.44.0 | 32.0.3
|
||||||
|
| selenoid/firefox:33.0 | selenoid/vnc_firefox:33.0 | 2.44.0 | 33.0.3
|
||||||
|
| selenoid/firefox:34.0 | selenoid/vnc_firefox:34.0 | 2.45.0 | 34.0.5
|
||||||
|
| selenoid/firefox:35.0 | selenoid/vnc_firefox:35.0 | 2.45.0 | 35.0.1
|
||||||
|
| selenoid/firefox:36.0 | selenoid/vnc_firefox:36.0 | 2.45.0 | 36.0.1
|
||||||
|
| selenoid/firefox:37.0 | selenoid/vnc_firefox:37.0 | 2.45.0 | 37.0.2
|
||||||
|
| selenoid/firefox:38.0 | selenoid/vnc_firefox:38.0 | 2.45.0 | 38.0.5
|
||||||
|
| selenoid/firefox:39.0 | selenoid/vnc_firefox:39.0 | 2.45.0 | 39.0.3
|
||||||
|
| selenoid/firefox:40.0 | selenoid/vnc_firefox:40.0 | 2.45.0 | 40.0.3
|
||||||
|
| selenoid/firefox:41.0 | selenoid/vnc_firefox:41.0 | 2.45.0 | 41.0.2
|
||||||
|
| selenoid/firefox:42.0 | selenoid/vnc_firefox:42.0 | 2.47.1 | 42.0
|
||||||
|
| selenoid/firefox:43.0 | selenoid/vnc_firefox:43.0 | 2.53.1 | 43.0.4
|
||||||
|
| selenoid/firefox:44.0 | selenoid/vnc_firefox:44.0 | 2.53.1 | 44.0.2
|
||||||
|
| selenoid/firefox:45.0 | selenoid/vnc_firefox:45.0 | 2.53.1 | 45.0.2
|
||||||
|
| selenoid/firefox:46.0 | selenoid/vnc_firefox:46.0 | 2.53.1 | 46.0.1
|
||||||
|
| selenoid/firefox:47.0 | selenoid/vnc_firefox:47.0 | 2.53.1 | 47.0.1
|
||||||
|
|===
|
||||||
|
|
||||||
|
WARNING: Firefox 53.0+ images require Selenium client 3.4.0 or newer for Java and Node.js and 3.5.0 or newer for Python.
|
||||||
|
|
||||||
|
.Firefox Images with Selenoid
|
||||||
|
|===
|
||||||
|
| Image | VNC Image | Selenoid Version | Geckodriver Version | Firefox Version
|
||||||
|
|
||||||
|
| selenoid/firefox:48.0 | selenoid/vnc_firefox:48.0 | 1.10.0 | 0.13.0 | 48.0.2 (page load timeout, native events and proxies don't work)
|
||||||
|
| selenoid/firefox:49.0 | selenoid/vnc_firefox:49.0 | 1.10.0 | 0.13.0 | 49.0.2 (page load timeout, native events and switching between windows don't work)
|
||||||
|
| selenoid/firefox:50.0 | selenoid/vnc_firefox:50.0 | 1.10.0 | 0.13.0 | 50.0.2 (page load timeout, native events, switching windows and proxies don't work)
|
||||||
|
| selenoid/firefox:51.0 | selenoid/vnc_firefox:51.0 | 1.10.0 | 0.14.0 | 51.0.1 (page load timeout, native events, switching windows and proxies don't work)
|
||||||
|
| selenoid/firefox:52.0 | selenoid/vnc_firefox:52.0 | 1.10.0 | 0.15.0 | 52.0.2 (page load timeout, native events, switching windows and proxies don't work)
|
||||||
|
| selenoid/firefox:53.0 | selenoid/vnc_firefox:53.0 | 1.10.0 | 0.16.0 | 53.0.2 (switching windows may not work)
|
||||||
|
| selenoid/firefox:54.0 | selenoid/vnc_firefox:54.0 | 1.10.0 | 0.17.0 | 54.0.1 (switching windows may not work)
|
||||||
|
| selenoid/firefox:55.0 | selenoid/vnc_firefox:55.0 | 1.10.0 | 0.18.0 | 55.0.1 (switching windows may not work)
|
||||||
|
| selenoid/firefox:56.0 | selenoid/vnc_firefox:56.0 | 1.10.0 | 0.19.1 | 56.0.1
|
||||||
|
| selenoid/firefox:57.0 | selenoid/vnc_firefox:57.0 | 1.10.0 | 0.19.1 | 57.0
|
||||||
|
| selenoid/firefox:58.0 | selenoid/vnc_firefox:58.0 | 1.10.0 | 0.20.1 | 58.0
|
||||||
|
| selenoid/firefox:59.0 | selenoid/vnc_firefox:59.0 | 1.10.0 | 0.20.1 | 59.0.1
|
||||||
|
| selenoid/firefox:60.0 | selenoid/vnc_firefox:60.0 | 1.10.0 | 0.21.0 | 60.0.2
|
||||||
|
| selenoid/firefox:61.0 | selenoid/vnc_firefox:61.0 | 1.10.0 | 0.21.0 | 61.0
|
||||||
|
| selenoid/firefox:62.0 | selenoid/vnc_firefox:62.0 | 1.10.0 | 0.22.0 | 62.0
|
||||||
|
| selenoid/firefox:63.0 | selenoid/vnc_firefox:63.0 | 1.8.1 | 0.23.0 | 63.0
|
||||||
|
| selenoid/firefox:64.0 | selenoid/vnc_firefox:64.0 | 1.8.4 | 0.23.0 | 64.0
|
||||||
|
| selenoid/firefox:65.0 | selenoid/vnc_firefox:65.0 | 1.9.0 | 0.24.0 | 65.0
|
||||||
|
| selenoid/firefox:66.0 | selenoid/vnc_firefox:66.0 | 1.9.1 | 0.24.0 | 66.0.1
|
||||||
|
| selenoid/firefox:67.0 | selenoid/vnc_firefox:67.0 | 1.9.1 | 0.24.0 | 67.0
|
||||||
|
| selenoid/firefox:68.0 | selenoid/vnc_firefox:68.0 | 1.9.2 | 0.24.0 | 68.0
|
||||||
|
| selenoid/firefox:69.0 | selenoid/vnc_firefox:69.0 | 1.9.2 | 0.24.0 | 69.0
|
||||||
|
| selenoid/firefox:70.0 | selenoid/vnc_firefox:70.0 | 1.9.2 | 0.26.0 | 70.0
|
||||||
|
| selenoid/firefox:71.0 | selenoid/vnc_firefox:71.0 | 1.9.3 | 0.26.0 | 71.0
|
||||||
|
| selenoid/firefox:72.0 | selenoid/vnc_firefox:72.0 | 1.9.3 | 0.26.0 | 72.0
|
||||||
|
| selenoid/firefox:73.0 | selenoid/vnc_firefox:73.0 | 1.10.0 | 0.26.0 | 73.0
|
||||||
|
| selenoid/firefox:74.0 | selenoid/vnc_firefox:74.0 | 1.10.0 | 0.26.0 | 74.0.1
|
||||||
|
| selenoid/firefox:75.0 | selenoid/vnc_firefox:75.0 | 1.10.0 | 0.26.0 | 75.0
|
||||||
|
| selenoid/firefox:76.0 | selenoid/vnc_firefox:76.0 | 1.10.0 | 0.26.0 | 76.0
|
||||||
|
| selenoid/firefox:77.0 | selenoid/vnc_firefox:77.0 | 1.10.0 | 0.26.0 | 77.0.1
|
||||||
|
| selenoid/firefox:78.0 | selenoid/vnc_firefox:78.0 | 1.10.0 | 0.26.0 | 78.0.1
|
||||||
|
| selenoid/firefox:79.0 | selenoid/vnc_firefox:79.0 | 1.10.0 | 0.27.0 | 79.0
|
||||||
|
| selenoid/firefox:80.0 | selenoid/vnc_firefox:80.0 | 1.10.0 | 0.27.0 | 80.0
|
||||||
|
| selenoid/firefox:81.0 | selenoid/vnc_firefox:81.0 | 1.10.0 | 0.27.0 | 81.0
|
||||||
|
| selenoid/firefox:82.0 | selenoid/vnc_firefox:82.0 | 1.10.0 | 0.27.0 | 82.0
|
||||||
|
| selenoid/firefox:83.0 | selenoid/vnc_firefox:83.0 | 1.10.0 | 0.28.0 | 83.0
|
||||||
|
| selenoid/firefox:84.0 | selenoid/vnc_firefox:84.0 | 1.10.0 | 0.28.0 | 84.0
|
||||||
|
| selenoid/firefox:85.0 | selenoid/vnc_firefox:85.0 | 1.10.1 | 0.29.0 | 85.0.1
|
||||||
|
| selenoid/firefox:86.0 | selenoid/vnc_firefox:86.0 | 1.10.1 | 0.29.0 | 86.0
|
||||||
|
| selenoid/firefox:87.0 | selenoid/vnc_firefox:87.0 | 1.10.1 | 0.29.0 | 87.0
|
||||||
|
| selenoid/firefox:88.0 | selenoid/vnc_firefox:88.0 | 1.10.3 | 0.29.1 | 88.0
|
||||||
|
| selenoid/firefox:89.0 | selenoid/vnc_firefox:89.0 | 1.10.3 | 0.29.1 | 89.0
|
||||||
|
| selenoid/firefox:90.0 | selenoid/vnc_firefox:90.0 | 1.10.3 | 0.29.1 | 90.0
|
||||||
|
| selenoid/firefox:91.0 | selenoid/vnc_firefox:91.0 | 1.10.3 | 0.29.1 | 91.0
|
||||||
|
| selenoid/firefox:92.0 | selenoid/vnc_firefox:92.0 | 1.10.5 | 0.29.1 | 92.0
|
||||||
|
| selenoid/firefox:93.0 | selenoid/vnc_firefox:93.0 | 1.10.5 | 0.29.1 | 93.0
|
||||||
|
| selenoid/firefox:94.0 | selenoid/vnc_firefox:94.0 | 1.10.5 | 0.29.1 | 94.0
|
||||||
|
| selenoid/firefox:95.0 | selenoid/vnc_firefox:95.0 | 1.10.7 | 0.29.1 | 95.0
|
||||||
|
| selenoid/firefox:96.0 | selenoid/vnc_firefox:96.0 | 1.10.7 | 0.29.1 | 96.0
|
||||||
|
| selenoid/firefox:97.0 | selenoid/vnc_firefox:97.0 | 1.10.7 | 0.29.1 | 97.0
|
||||||
|
| selenoid/firefox:98.0 | selenoid/vnc_firefox:98.0 | 1.10.7 | 0.29.1 | 98.0
|
||||||
|
| selenoid/firefox:99.0 | selenoid/vnc_firefox:99.0 | 1.10.7 | 0.29.1 | 99.0
|
||||||
|
| selenoid/firefox:100.0 | selenoid/vnc_firefox:100.0 | 1.10.7 | 0.29.1 | 100.0
|
||||||
|
|===
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 114 KiB |
@@ -0,0 +1,37 @@
|
|||||||
|
= Browser Images
|
||||||
|
:revdate: {docdate}
|
||||||
|
:toc: left
|
||||||
|
:toclevels: 3
|
||||||
|
:sectnums:
|
||||||
|
:sectanchors:
|
||||||
|
:Author: Aerokube Software OÜ
|
||||||
|
:revnumber: latest
|
||||||
|
:icons: font
|
||||||
|
:source-highlighter: coderay
|
||||||
|
:docinfo: shared
|
||||||
|
:title: Browser Images - A set of browser images for Selenoid and Moon
|
||||||
|
:description: The most popular browser environments you need to run your tests
|
||||||
|
:favicon: img/favicon.png
|
||||||
|
|
||||||
|
This reference covers version: _{revnumber}_
|
||||||
|
|
||||||
|
This section contains information about browser images available for using with https://aerokube.com/selenoid/[Selenoid] and https://aerokube.com/moon/[Moon]. Please refer to https://github.com/aerokube/images[GitHub repository] if you need source code.
|
||||||
|
|
||||||
|
== Browser Image Information
|
||||||
|
include::cdtp.adoc[leveloffset=+1]
|
||||||
|
include::cypress.adoc[leveloffset=+1]
|
||||||
|
include::playwright.adoc[leveloffset=+1]
|
||||||
|
|
||||||
|
=== Selenium
|
||||||
|
include::android.adoc[leveloffset=+2]
|
||||||
|
include::chrome.adoc[leveloffset=+2]
|
||||||
|
include::chrome-mobile.adoc[leveloffset=+2]
|
||||||
|
include::firefox.adoc[leveloffset=+2]
|
||||||
|
include::msedge.adoc[leveloffset=+2]
|
||||||
|
include::opera.adoc[leveloffset=+2]
|
||||||
|
include::safari.adoc[leveloffset=+2]
|
||||||
|
include::windows.adoc[leveloffset=+2]
|
||||||
|
|
||||||
|
include::features.adoc[]
|
||||||
|
|
||||||
|
include::building-images.adoc[]
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
== Microsoft Edge
|
||||||
|
|
||||||
|
IMPORTANT: When adding Microsoft Edge to browsers.json - you have to use MicrosoftEdge as browser name.
|
||||||
|
|
||||||
|
.Microsoft Edge Images
|
||||||
|
|===
|
||||||
|
| Image | msedgedriver version | Edge version
|
||||||
|
|
||||||
|
| browsers/edge:88.0 | 88.0.673.0 | 88.0.673.0 (dev)
|
||||||
|
| browsers/edge:89.0 | 89.0.731.0 | 89.0.731.0 (dev)
|
||||||
|
| browsers/edge:90.0 | 90.0.789.1 | 90.0.789.1 (dev)
|
||||||
|
| browsers/edge:91.0 | 91.0.831.1 | 91.0.831.1 (dev)
|
||||||
|
| browsers/edge:92.0 | 92.0.902.9 | 92.0.902.62 (dev)
|
||||||
|
| browsers/edge:93.0 | 93.0.961.2 | 93.0.961.18 (dev)
|
||||||
|
| browsers/edge:94.0 | 94.0.992.19 | 94.0.992.19 (beta)
|
||||||
|
| browsers/edge:95.0 | 95.0.1020.5 | 95.0.1020.9 (beta)
|
||||||
|
| browsers/edge:96.0 | 96.0.1054.34 | 96.0.1054.34
|
||||||
|
| browsers/edge:97.0 | 97.0.1072.8 | 97.0.1072.55
|
||||||
|
| browsers/edge:98.0 | 98.0.1108.55 | 98.0.1108.56
|
||||||
|
| browsers/edge:99.0 | 99.0.1150.7 | 99.0.1150.30
|
||||||
|
| browsers/edge:100.0 | 100.0.1185.23 | 100.0.1185.39
|
||||||
|
| browsers/edge:101.0 | 100.0.1185.23 | 100.0.1185.39
|
||||||
|
|===
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
== Opera
|
||||||
|
|
||||||
|
.Opera Presto Images
|
||||||
|
|===
|
||||||
|
| Image | VNC Image | Selenium version | Opera version
|
||||||
|
|
||||||
|
| selenoid/opera:12.16 | selenoid/vnc_opera:12.16 | 2.37.0 | 12.16.1860 (dialogs and probably async JS don't work)
|
||||||
|
|===
|
||||||
|
|
||||||
|
[WARNING]
|
||||||
|
====
|
||||||
|
Due to bug in *Operadriver* to work with *Opera Blink* images you need to pass additional capability:
|
||||||
|
[source,javascript]
|
||||||
|
{"browserName": "opera", "operaOptions": {"binary": "/usr/bin/opera"}}
|
||||||
|
|
||||||
|
We do not consider these images really stable. Many of base operations like working with proxies may not work.
|
||||||
|
====
|
||||||
|
|
||||||
|
.Opera Blink Images
|
||||||
|
|===
|
||||||
|
| Image | VNC Image | Operadriver version | Opera version
|
||||||
|
|
||||||
|
| selenoid/opera:33.0 | selenoid/vnc_opera:33.0 | 0.2.2 | 33.0.1990.115
|
||||||
|
| selenoid/opera:34.0 | selenoid/vnc_opera:34.0 | 0.2.2 | 34.0.2036.50
|
||||||
|
| selenoid/opera:35.0 | selenoid/vnc_opera:35.0 | 0.2.2 | 35.0.2066.92
|
||||||
|
| selenoid/opera:36.0 | selenoid/vnc_opera:36.0 | 0.2.2 | 36.0.2130.65
|
||||||
|
| selenoid/opera:37.0 | selenoid/vnc_opera:37.0 | 0.2.2 | 37.0.2178.54
|
||||||
|
| selenoid/opera:38.0 | selenoid/vnc_opera:38.0 | 0.2.2 | 38.0.2220.41
|
||||||
|
| selenoid/opera:39.0 | selenoid/vnc_opera:39.0 | 0.2.2 | 39.0.2256.71
|
||||||
|
| selenoid/opera:40.0 | selenoid/vnc_opera:40.0 | 0.2.2 | 40.0.2308.90
|
||||||
|
| selenoid/opera:41.0 | selenoid/vnc_opera:41.0 | 2.27 | 41.0.2353.69
|
||||||
|
| selenoid/opera:42.0 | selenoid/vnc_opera:42.0 | 2.27 | 42.0.2393.94
|
||||||
|
| selenoid/opera:43.0 | selenoid/vnc_opera:43.0 | 2.27 | 43.0.2442.991
|
||||||
|
| selenoid/opera:44.0 | selenoid/vnc_opera:44.0 | 2.27 | 44.0.2510.857
|
||||||
|
| selenoid/opera:45.0 | selenoid/vnc_opera:45.0 | 2.27 | 45.0.2552.635
|
||||||
|
| selenoid/opera:46.0 | selenoid/vnc_opera:46.0 | 2.27 | 46.0.2597.26
|
||||||
|
| selenoid/opera:47.0 | selenoid/vnc_opera:47.0 | 2.29 | 47.0.2631.39
|
||||||
|
| selenoid/opera:48.0 | selenoid/vnc_opera:48.0 | 2.30 | 48.0.2685.35
|
||||||
|
| selenoid/opera:49.0 | selenoid/vnc_opera:49.0 | 2.32 | 49.0.2725.39
|
||||||
|
| selenoid/opera:50.0 | selenoid/vnc_opera:50.0 | 2.32 | 50.0.2762.45
|
||||||
|
| selenoid/opera:51.0 | selenoid/vnc_opera:51.0 | 2.33 | 51.0.2830.26
|
||||||
|
| selenoid/opera:52.0 | selenoid/vnc_opera:52.0 | 2.35 | 52.0.2871.37
|
||||||
|
| selenoid/opera:53.0 | selenoid/vnc_opera:53.0 | 2.36 | 53.0.2907.68
|
||||||
|
| selenoid/opera:54.0 | selenoid/vnc_opera:54.0 | 2.37 | 54.0.2952.46
|
||||||
|
| selenoid/opera:55.0 | selenoid/vnc_opera:55.0 | 2.37 | 55.0.2994.37
|
||||||
|
| selenoid/opera:56.0 | selenoid/vnc_opera:56.0 | 2.40 | 56.0.3051.31
|
||||||
|
| selenoid/opera:57.0 | selenoid/vnc_opera:57.0 | 2.41 | 57.0.3098.76
|
||||||
|
| selenoid/opera:58.0 | selenoid/vnc_opera:58.0 | 2.42 | 58.0.3135.79
|
||||||
|
| - | - | - | 59.0.x.x (no stable release exists)
|
||||||
|
| selenoid/opera:60.0 | selenoid/vnc_opera:60.0 | 2.45 | 60.0.3255.56
|
||||||
|
| - | - | - | 61.0.x.x (no stable release exists)
|
||||||
|
| selenoid/opera:62.0 | selenoid/vnc_opera:62.0 | 75.0.3770.100 | 62.0.3331.99 (need to use browserName = chrome)
|
||||||
|
| selenoid/opera:63.0 | selenoid/vnc_opera:63.0 | 76.0.3809.132 | 63.0.3368.91
|
||||||
|
| selenoid/opera:64.0 | selenoid/vnc_opera:64.0 | 77.0.3865.120 | 64.0.3417.73
|
||||||
|
| selenoid/opera:65.0 | selenoid/vnc_opera:65.0 | 78.0.3904.87 | 65.0.3467.42
|
||||||
|
| selenoid/opera:66.0 | selenoid/vnc_opera:66.0 | 79.0.3945.79 | 66.0.3515.36
|
||||||
|
| selenoid/opera:67.0 | selenoid/vnc_opera:67.0 | 80.0.3987.100 | 67.0.3575.53
|
||||||
|
| selenoid/opera:68.0 | selenoid/vnc_opera:68.0 | 81.0.4044.113 | 68.0.3618.104
|
||||||
|
| selenoid/opera:69.0 | selenoid/vnc_opera:69.0 | 83.0.4103.97 | 69.0.3686.49
|
||||||
|
| selenoid/opera:70.0 | selenoid/vnc_opera:70.0 | 84.0.4147.89 | 70.0.3728.133
|
||||||
|
| selenoid/opera:71.0 | selenoid/vnc_opera:71.0 | 85.0.4183.102 | 71.0.3770.228
|
||||||
|
| selenoid/opera:72.0 | selenoid/vnc_opera:72.0 | 86.0.4240.80 | 72.0.3815.378
|
||||||
|
| selenoid/opera:73.0 | selenoid/vnc_opera:73.0 | 87.0.4280.67 | 73.0.3856.329
|
||||||
|
| selenoid/opera:74.0 | selenoid/vnc_opera:74.0 | 88.0.4324.104 | 74.0.3911.107
|
||||||
|
| selenoid/opera:75.0 | selenoid/vnc_opera:75.0 | 89.0.4389.82 | 75.0.3969.93
|
||||||
|
| selenoid/opera:76.0 | selenoid/vnc_opera:76.0 | 90.0.4430.85 | 76.0.4017.107
|
||||||
|
| selenoid/opera:77.0 | selenoid/vnc_opera:77.0 | 91.0.4472.77 | 77.0.4054.90
|
||||||
|
| selenoid/opera:78.0 | selenoid/vnc_opera:78.0 | 92.0.4515.107 | 78.0.4093.184
|
||||||
|
| selenoid/opera:79.0 | selenoid/vnc_opera:79.0 | 93.0.4577.63 | 79.0.4143.22
|
||||||
|
| selenoid/opera:80.0 | selenoid/vnc_opera:80.0 | 94.0.4606.61 | 80.0.4170.16
|
||||||
|
| selenoid/opera:81.0 | selenoid/vnc_opera:81.0 | 95.0.4638.54 | 81.0.4196.31
|
||||||
|
| selenoid/opera:82.0 | selenoid/vnc_opera:82.0 | 96.0.4664.45 | 82.0.4227.23
|
||||||
|
| selenoid/opera:83.0 | selenoid/vnc_opera:83.0 | 97.0.4692.71 | 83.0.4254.19
|
||||||
|
| selenoid/opera:84.0 | selenoid/vnc_opera:84.0 | 98.0.4758.82 | 84.0.4316.21
|
||||||
|
| selenoid/opera:85.0 | selenoid/vnc_opera:85.0 | 99.0.4844.51 | 85.0.4341.18
|
||||||
|
| selenoid/opera:86.0 | selenoid/vnc_opera:86.0 | 100.0.4896.127 | 86.0.4363.50
|
||||||
|
|===
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
====
|
||||||
|
. These images work with any modern Selenium client version.
|
||||||
|
. Images for older Opera versions were not built because we have no Debian packages. If you have such packages - we could create more images.
|
||||||
|
====
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
== Playwright
|
||||||
|
|
||||||
|
These images are used to run https://github.com/microsoft/playwright[Playwright] tests in parallel.
|
||||||
|
|
||||||
|
.Chromium Images
|
||||||
|
|===
|
||||||
|
| Image | Aliases | Playwright version | Chromium version
|
||||||
|
|
||||||
|
| playwright/chromium:playwright-1.1.1 | - | 1.1.1 | 85.0.4165.0
|
||||||
|
| playwright/chromium:playwright-1.2.0 | playwright/chromium:85.0 | 1.2.0 | 85.0.4182.0
|
||||||
|
| playwright/chromium:playwright-1.3.0 | - | 1.3.0 | 86.0.4217.0
|
||||||
|
| playwright/chromium:playwright-1.4.0 | playwright/chromium:86.0 | 1.4.0 | 86.0.4238.0
|
||||||
|
| playwright/chromium:playwright-1.5.1 | - | 1.5.1 | 88.0.4287.0
|
||||||
|
| playwright/chromium:playwright-1.6.1 | playwright/chromium:88.0 | 1.6.1 | 88.0.4316.0
|
||||||
|
| playwright/chromium:playwright-1.7.0 | playwright/chromium:89.0 | 1.7.0 | 89.0.4344.0
|
||||||
|
| playwright/chromium:playwright-1.8.0 | - | 1.8.0 | 90.0.4392.0
|
||||||
|
| playwright/chromium:playwright-1.8.1 | - | 1.8.1 | 90.0.4392.0
|
||||||
|
| playwright/chromium:playwright-1.9.0 | - | 1.9.0 | 90.0.4421.0
|
||||||
|
| playwright/chromium:playwright-1.9.1 | - | 1.9.1 | 90.0.4421.0
|
||||||
|
| playwright/chromium:playwright-1.9.2 | - | 1.9.2 | 90.0.4421.0
|
||||||
|
| playwright/chromium:playwright-1.10.0 | playwright/chromium:90.0 | 1.10.0 | 90.0.4430.0
|
||||||
|
| playwright/chromium:playwright-1.11.0 | - | 1.11.0 | 92.0.4498.0
|
||||||
|
| playwright/chromium:playwright-1.11.1 | playwright/chromium:92.0 | 1.11.0 | 92.0.4498.0
|
||||||
|
| playwright/chromium:playwright-1.12.0 | - | 1.12.0 | 93.0.4530.0
|
||||||
|
| playwright/chromium:playwright-1.12.1 | - | 1.12.1 | 93.0.4530.0
|
||||||
|
| playwright/chromium:playwright-1.12.2 | - | 1.12.2 | 93.0.4530.0
|
||||||
|
| playwright/chromium:playwright-1.12.3 | - | 1.12.3 | 93.0.4530.0
|
||||||
|
| playwright/chromium:playwright-1.13.0 | - | 1.13.0 | 93.0.4576.0
|
||||||
|
| playwright/chromium:playwright-1.13.1 | playwright/chromium:93.0 | 1.13.1 | 93.0.4576.0
|
||||||
|
| playwright/chromium:playwright-1.14.0 | - | 1.14.0 | 94.0.4595.0
|
||||||
|
| playwright/chromium:playwright-1.14.1 | playwright/chromium:94.0 | 1.14.1 | 94.0.4595.0
|
||||||
|
| playwright/chromium:playwright-1.15.0 | - | 1.15.0 | 96.0.4641.0
|
||||||
|
| playwright/chromium:playwright-1.15.1 | - | 1.15.1 | 96.0.4641.0
|
||||||
|
| playwright/chromium:playwright-1.15.2 | playwright/chromium:96.0 | 1.15.2 | 96.0.4641.0
|
||||||
|
| playwright/chromium:playwright-1.16.0 | - | 1.16.0 | 97.0.4666.0
|
||||||
|
| playwright/chromium:playwright-1.16.1 | - | 1.16.1 | 97.0.4666.0
|
||||||
|
| playwright/chromium:playwright-1.16.2 | - | 1.16.2 | 97.0.4666.0
|
||||||
|
| playwright/chromium:playwright-1.16.3 | playwright/chromium:97.0 | 1.16.3 | 97.0.4666.0
|
||||||
|
| playwright/chromium:playwright-1.17.0 | - | 1.17.0 | 98.0.4695.0
|
||||||
|
| playwright/chromium:playwright-1.17.1 | - | 1.17.1 | 98.0.4695.0
|
||||||
|
| playwright/chromium:playwright-1.17.2 | playwright/chromium:98.0 | 1.17.2 | 98.0.4695.0
|
||||||
|
| playwright/chromium:playwright-1.18.0 | - | 1.18.0 | 99.0.4812.0
|
||||||
|
| playwright/chromium:playwright-1.18.1 | playwright/chromium:99.0 | 1.18.1 | 99.0.4812.0
|
||||||
|
| playwright/chromium:playwright-1.19.0 | - | 1.19.0 | 100.0.4863.0
|
||||||
|
| playwright/chromium:playwright-1.19.1 | - | 1.19.1 | 100.0.4863.0
|
||||||
|
| playwright/chromium:playwright-1.19.2 | playwright/chromium:100.0 | 1.19.2 | 100.0.4863.0
|
||||||
|
| playwright/chromium:playwright-1.20.0 | - | 1.20.0 | 101.0.4921.0
|
||||||
|
| playwright/chromium:playwright-1.20.1 | - | 1.20.1 | 101.0.4921.0
|
||||||
|
| playwright/chromium:playwright-1.20.2 | - | 1.20.2 | 101.0.4921.0
|
||||||
|
| playwright/chromium:playwright-1.21.0 | - | 1.21.0 | 101.0.4951.26
|
||||||
|
| playwright/chromium:playwright-1.21.1 | playwright/chromium:101.0 | 1.21.1 | 101.0.4951.26
|
||||||
|
| playwright/chromium:playwright-1.22.0 | playwright/chromium:102.0 | 1.22.0 | 102.0.5005.40
|
||||||
|
|===
|
||||||
|
|
||||||
|
.Chrome Images
|
||||||
|
|===
|
||||||
|
| Image | Aliases | Playwright version | Chrome version
|
||||||
|
| playwright/chrome:playwright-1.7.1 | playwright/chrome:89.0 | 1.7.1 | 89.0.4389.58
|
||||||
|
| playwright/chrome:playwright-1.8.1 | - | 1.8.1 | 90.0.4421.5
|
||||||
|
| playwright/chrome:playwright-1.9.0 | - | 1.9.0 | 90.0.4421.5
|
||||||
|
| playwright/chrome:playwright-1.9.1 | - | 1.9.1 | 90.0.4427.5
|
||||||
|
| playwright/chrome:playwright-1.9.2 | playwright/chrome:90.0 | 1.9.2 | 90.0.4430.19
|
||||||
|
| playwright/chrome:playwright-1.10.0 | - | 1.10.0 | 91.0.4455.2
|
||||||
|
| playwright/chrome:playwright-1.11.0 | - | 1.11.0 | 92.0.4503.0
|
||||||
|
| playwright/chrome:playwright-1.11.1 | playwright/chrome:92.0 | 1.11.0 | 92.0.4503.0
|
||||||
|
| playwright/chrome:playwright-1.12.0 | - | 1.12.0 | 91.0.4472.101
|
||||||
|
| playwright/chrome:playwright-1.12.1 | - | 1.12.1 | 91.0.4472.101
|
||||||
|
| playwright/chrome:playwright-1.12.2 | - | 1.12.2 | 91.0.4472.101
|
||||||
|
| playwright/chrome:playwright-1.12.3 | playwright/chrome:91.0 (switched to stable releases) | 1.12.3 | 91.0.4472.114
|
||||||
|
| playwright/chrome:playwright-1.13.0 | - | 1.13.0 | 92.0.4515.107
|
||||||
|
| playwright/chrome:playwright-1.13.1 | - | 1.13.1 | 92.0.4515.107
|
||||||
|
| playwright/chrome:playwright-1.14.0 | - | 1.14.0 | 92.0.4515.131
|
||||||
|
| playwright/chrome:playwright-1.14.1 | - | 1.14.1 | 93.0.4577.63
|
||||||
|
| playwright/chrome:playwright-1.15.0 | playwright/chrome:93.0 | 1.15.0 | 93.0.4577.82
|
||||||
|
| playwright/chrome:playwright-1.15.1 | - | 1.15.1 | 94.0.4606.71
|
||||||
|
| playwright/chrome:playwright-1.15.2 | playwright/chrome:94.0 | 1.15.2 | 94.0.4606.71
|
||||||
|
| playwright/chrome:playwright-1.16.0 | - | 1.16.0 | 95.0.4638.54
|
||||||
|
| playwright/chrome:playwright-1.16.1 | - | 1.16.1 | 95.0.4638.54
|
||||||
|
| playwright/chrome:playwright-1.16.2 | - | 1.16.2 | 95.0.4638.54
|
||||||
|
| playwright/chrome:playwright-1.16.3 | playwright/chrome:95.0 | 1.16.2 | 95.0.4638.69
|
||||||
|
| playwright/chrome:playwright-1.17.0 | - | 1.17.0 | 96.0.4664.45
|
||||||
|
| playwright/chrome:playwright-1.17.1 | playwright/chrome:96.0 | 1.17.1 | 96.0.4664.45
|
||||||
|
| playwright/chrome:playwright-1.17.2 | - | 1.17.2 | 97.0.4692.71
|
||||||
|
| playwright/chrome:playwright-1.18.0 | - | 1.18.0 | 97.0.4692.99
|
||||||
|
| playwright/chrome:playwright-1.18.1 | playwright/chrome:97.0 | 1.18.1 | 97.0.4692.99
|
||||||
|
| playwright/chrome:playwright-1.19.0 | - | 1.19.0 | 98.0.4758.80
|
||||||
|
| playwright/chrome:playwright-1.19.1 | - | 1.19.1 | 98.0.4758.102
|
||||||
|
| playwright/chrome:playwright-1.19.2 | playwright/chrome:98.0 | 1.19.2 | 98.0.4758.102
|
||||||
|
| playwright/chrome:playwright-1.20.0 | - | 1.20.0 | 99.0.4844.74
|
||||||
|
| playwright/chrome:playwright-1.20.1 | playwright/chrome:99.0 | 1.20.1 | 99.0.4844.82
|
||||||
|
| playwright/chrome:playwright-1.20.2 | - | 1.20.2 | 100.0.4896.75
|
||||||
|
| playwright/chrome:playwright-1.21.0 | - | 1.21.0 | 100.0.4896.127
|
||||||
|
| playwright/chrome:playwright-1.21.1 | playwright/chrome:100.0 | 1.21.1 | 100.0.4896.127
|
||||||
|
| playwright/chrome:playwright-1.22.0 | playwright/chrome:101.0 | 1.22.0 | 101.0.4951.64
|
||||||
|
|===
|
||||||
|
|
||||||
|
.Firefox Images
|
||||||
|
|===
|
||||||
|
| Image | Aliases | Playwright version | Firefox version
|
||||||
|
|
||||||
|
| playwright/firefox:playwright-1.1.1 | playwright/firefox:77.0 | 1.1.1 | 77.0b3
|
||||||
|
| playwright/firefox:playwright-1.2.0 | playwright/firefox:78.0 | 1.2.0 | 78.0b5
|
||||||
|
| playwright/firefox:playwright-1.3.0 | - | 1.3.0 | 78.0b5
|
||||||
|
| playwright/firefox:playwright-1.4.0 | playwright/firefox:80.0 | 1.4.0 | 80.0b8
|
||||||
|
| playwright/firefox:playwright-1.5.1 | playwright/firefox:82.0 | 1.5.1 | 82.0b9
|
||||||
|
| playwright/firefox:playwright-1.6.1 | playwright/firefox:83.0 | 1.6.1 | 83.0b8
|
||||||
|
| playwright/firefox:playwright-1.7.0 | playwright/firefox:84.0 | 1.7.0 | 84.0b9
|
||||||
|
| playwright/firefox:playwright-1.8.0 | - | 1.8.0 | 85.0b5
|
||||||
|
| playwright/firefox:playwright-1.8.1 | playwright/firefox:85.0 | 1.8.1 | 85.0b5
|
||||||
|
| playwright/firefox:playwright-1.9.0 | - | 1.9.0 | 86.0b10
|
||||||
|
| playwright/firefox:playwright-1.9.1 | - | 1.9.1 | 86.0b10
|
||||||
|
| playwright/firefox:playwright-1.9.2 | playwright/firefox:86.0 | 1.9.2 | 86.0b10
|
||||||
|
| playwright/firefox:playwright-1.10.0 | playwright/firefox:87.0 | 1.10.0 | 87.0b10
|
||||||
|
| playwright/firefox:playwright-1.11.0 | - | 1.11.0 | 89.0b6
|
||||||
|
| playwright/firefox:playwright-1.11.1 | - | 1.11.0 | 89.0b6
|
||||||
|
| playwright/firefox:playwright-1.12.0 | - | 1.12.0 | 89.0
|
||||||
|
| playwright/firefox:playwright-1.12.1 | - | 1.12.1 | 89.0
|
||||||
|
| playwright/firefox:playwright-1.12.2 | - | 1.12.2 | 89.0
|
||||||
|
| playwright/firefox:playwright-1.12.3 | playwright/firefox:89.0 | 1.12.3 | 89.0
|
||||||
|
| playwright/firefox:playwright-1.13.0 | - | 1.13.0 | 90.0
|
||||||
|
| playwright/firefox:playwright-1.13.1 | playwright/firefox:90.0 | 1.13.1 | 90.0
|
||||||
|
| playwright/firefox:playwright-1.14.0 | - | 1.14.0 | 91.0
|
||||||
|
| playwright/firefox:playwright-1.14.1 | playwright/firefox:91.0 | 1.14.1 | 91.0
|
||||||
|
| playwright/firefox:playwright-1.15.0 | - | 1.15.0 | 92.0
|
||||||
|
| playwright/firefox:playwright-1.15.1 | - | 1.15.1 | 92.0
|
||||||
|
| playwright/firefox:playwright-1.15.2 | playwright/firefox:92.0 | 1.15.2 | 92.0
|
||||||
|
| playwright/firefox:playwright-1.16.0 | - | 1.16.0 | 93.0
|
||||||
|
| playwright/firefox:playwright-1.16.1 | - | 1.16.1 | 93.0
|
||||||
|
| playwright/firefox:playwright-1.16.2 | - | 1.16.2 | 93.0
|
||||||
|
| playwright/firefox:playwright-1.16.3 | playwright/firefox:93.0 | 1.16.3 | 93.0
|
||||||
|
| playwright/firefox:playwright-1.17.0 | - | 1.17.0 | 94.0.1
|
||||||
|
| playwright/firefox:playwright-1.17.1 | - | 1.17.1 | 94.0.1
|
||||||
|
| playwright/firefox:playwright-1.17.2 | playwright/firefox:94.0 | 1.17.2 | 94.0.1
|
||||||
|
| playwright/firefox:playwright-1.18.0 | - | 1.18.0 | 95.0
|
||||||
|
| playwright/firefox:playwright-1.18.1 | playwright/firefox:95.0 | 1.18.1 | 95.0
|
||||||
|
| playwright/firefox:playwright-1.19.0 | - | 1.19.0 | 96.0.1
|
||||||
|
| playwright/firefox:playwright-1.19.1 | - | 1.19.1 | 96.0.1
|
||||||
|
| playwright/firefox:playwright-1.19.2 | playwright/firefox:96.0 | 1.19.2 | 96.0.1
|
||||||
|
| playwright/firefox:playwright-1.20.0 | - | 1.20.0 | 97.0.1
|
||||||
|
| playwright/firefox:playwright-1.20.1 | - | 1.20.1 | 97.0.1
|
||||||
|
| playwright/firefox:playwright-1.20.2 | playwright/firefox:97.0 | 1.20.2 | 97.0.1
|
||||||
|
| playwright/firefox:playwright-1.21.0 | - | 1.21.0 | 98.0.2
|
||||||
|
| playwright/firefox:playwright-1.21.1 | playwright/firefox:98.0 | 1.21.1 | 98.0.2
|
||||||
|
| playwright/firefox:playwright-1.22.0 | playwright/firefox:99.0 | 1.22.0 | 99.0.1
|
||||||
|
|===
|
||||||
|
|
||||||
|
.Webkit (Safari Desktop) Images
|
||||||
|
|===
|
||||||
|
| Image | Aliases | Playwright version | Safari version
|
||||||
|
|
||||||
|
| playwright/webkit:playwright-1.1.1 | - | 1.1.1 | 13.2
|
||||||
|
| playwright/webkit:playwright-1.2.0 | playwright/webkit:13.2 | 1.2.0 | 14.0
|
||||||
|
| playwright/webkit:playwright-1.3.0 | - | 1.3.0 | 14.0
|
||||||
|
| playwright/webkit:playwright-1.4.0 | - | 1.4.0 | 14.0
|
||||||
|
| playwright/webkit:playwright-1.5.1 | - | 1.5.1 | 14.0
|
||||||
|
| playwright/webkit:playwright-1.6.1 | playwright/webkit:14.0 | 1.6.1 | 14.0
|
||||||
|
| playwright/webkit:playwright-1.7.0 | - | 1.7.0 | 14.1
|
||||||
|
| playwright/webkit:playwright-1.8.0 | - | 1.8.0 | 14.1
|
||||||
|
| playwright/webkit:playwright-1.8.1 | - | 1.8.1 | 14.1
|
||||||
|
| playwright/webkit:playwright-1.9.0 | - | 1.9.0 | 14.1
|
||||||
|
| playwright/webkit:playwright-1.9.1 | - | 1.9.1 | 14.1
|
||||||
|
| playwright/webkit:playwright-1.9.2 | playwright/webkit:14.1 | 1.9.2 | 14.1
|
||||||
|
| playwright/webkit:playwright-1.10.0 | - | 1.10.0 | 14.2
|
||||||
|
| playwright/webkit:playwright-1.11.0 | - | 1.11.0 | 14.2
|
||||||
|
| playwright/webkit:playwright-1.11.1 | - | 1.11.0 | 14.2
|
||||||
|
| playwright/webkit:playwright-1.12.0 | - | 1.12.0 | 14.2
|
||||||
|
| playwright/webkit:playwright-1.12.1 | - | 1.12.1 | 14.2
|
||||||
|
| playwright/webkit:playwright-1.12.2 | - | 1.12.2 | 14.2
|
||||||
|
| playwright/webkit:playwright-1.12.3 | - | 1.12.3 | 14.2
|
||||||
|
| playwright/webkit:playwright-1.13.0 | - | 1.13.0 | 14.2
|
||||||
|
| playwright/webkit:playwright-1.13.1 | playwright/webkit:14.2 | 1.13.1 | 14.2
|
||||||
|
| playwright/webkit:playwright-1.14.0 | - | 1.14.0 | 15.0
|
||||||
|
| playwright/webkit:playwright-1.14.1 | - | 1.14.1 | 15.0
|
||||||
|
| playwright/webkit:playwright-1.15.0 | - | 1.15.0 | 15.0
|
||||||
|
| playwright/webkit:playwright-1.15.1 | - | 1.15.1 | 15.0
|
||||||
|
| playwright/webkit:playwright-1.15.2 | playwright/webkit:15.0 | 1.15.2 | 15.0
|
||||||
|
| playwright/webkit:playwright-1.16.0 | - | 1.16.0 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.16.1 | - | 1.16.1 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.16.2 | - | 1.16.2 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.16.3 | - | 1.16.3 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.17.0 | - | 1.17.0 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.17.1 | - | 1.17.1 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.17.2 | - | 1.17.2 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.18.0 | - | 1.18.0 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.18.1 | - | 1.18.1 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.19.0 | - | 1.19.0 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.19.1 | - | 1.19.1 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.19.2 | - | 1.19.2 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.20.0 | - | 1.20.0 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.20.1 | - | 1.20.1 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.20.2 | - | 1.20.2 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.21.0 | - | 1.21.0 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.21.1 | - | 1.21.1 | 15.4
|
||||||
|
| playwright/webkit:playwright-1.22.0 | playwright/webkit:15.4 | 1.22.0 | 15.4
|
||||||
|
|===
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
== Safari
|
||||||
|
|
||||||
|
IMPORTANT: Real Safari browser can only run under MacOS \ iOS. These Linux images are using WebKit engine used in Safari browser. From functional point of view real Safari and WebKit browser being used inside these images are equal. However fonts and pixel per pixel rendering could differ.
|
||||||
|
|
||||||
|
.Safari Images
|
||||||
|
|===
|
||||||
|
| Image | Webkit version
|
||||||
|
|
||||||
|
| browsers/safari:13.0 | 605.x
|
||||||
|
| browsers/safari:14.0 | 610.4.3.1.7
|
||||||
|
| browsers/safari:15.0 | 613.1.6.1
|
||||||
|
|===
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
== Windows Browsers
|
||||||
|
|
||||||
|
We don't build ready to use images for Internet Explorer and any other browser under Windows because of Windows license limitations. However we provide detailed instructions on building such images https://github.com/aerokube/windows-images[here]. The same approach can be used for preparing images with Microsoft Edge, Firefox, Chrome and Opera under Windows.
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
module github.com/aerokube/images
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/aandryashin/matchers v0.0.0-20161126170413-435295ea180e
|
||||||
|
github.com/fatih/color v1.9.0 // indirect
|
||||||
|
github.com/markbates/pkger v0.17.1
|
||||||
|
github.com/mattn/go-colorable v0.1.7 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||||
|
github.com/spf13/cobra v1.1.1
|
||||||
|
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gobuffalo/here v0.6.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,317 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/aandryashin/matchers v0.0.0-20161126170413-435295ea180e h1:ogUKYFNcdYUIBSLibE4+EjbTJazoHr5JsWWx21Lpn8c=
|
||||||
|
github.com/aandryashin/matchers v0.0.0-20161126170413-435295ea180e/go.mod h1:cbmYNkm9xeQlNoWEPtOUcvNok2gSD7ErMnYkRW+eHi8=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
|
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||||
|
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gobuffalo/here v0.6.0 h1:hYrd0a6gDmWxBM4TnrGw8mQg24iSVoIkHEk7FodQcBI=
|
||||||
|
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||||
|
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/markbates/pkger v0.17.1 h1:/MKEtWqtc0mZvu9OinB9UzVN9iYCwLWuyUv4Bw+PCno=
|
||||||
|
github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
|
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
|
||||||
|
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||||
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
|
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
||||||
|
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
|
||||||
|
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aerokube/images/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate pkger -include /static -o build
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cmd.Execute()
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
tmp/*
|
||||||
|
chromedriver*
|
||||||
|
*.apk
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
FROM golang:1.17 as go
|
||||||
|
|
||||||
|
COPY tmp/devtools /devtools
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y upx-ucl libx11-dev && \
|
||||||
|
cd /devtools && \
|
||||||
|
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" && \
|
||||||
|
upx /devtools/devtools
|
||||||
|
|
||||||
|
FROM ubuntu:18.04
|
||||||
|
|
||||||
|
ARG APPIUM_VERSION="1.8.1"
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt update && \
|
||||||
|
apt remove -y libcurl4 && \
|
||||||
|
apt install -y apt-transport-https ca-certificates tzdata locales libcurl4 curl gnupg && \
|
||||||
|
curl --silent --location https://deb.nodesource.com/setup_12.x | bash - && \
|
||||||
|
apt install -y --no-install-recommends \
|
||||||
|
curl \
|
||||||
|
iproute2 \
|
||||||
|
nodejs \
|
||||||
|
openjdk-8-jdk-headless \
|
||||||
|
unzip \
|
||||||
|
xvfb \
|
||||||
|
libpulse0 \
|
||||||
|
libxcomposite1 \
|
||||||
|
libxcursor1 \
|
||||||
|
libxi6 \
|
||||||
|
libasound2 \
|
||||||
|
fluxbox \
|
||||||
|
x11vnc \
|
||||||
|
feh \
|
||||||
|
wmctrl \
|
||||||
|
libglib2.0-0 && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -Rf /tmp/* && rm -Rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN cd / && npm install --prefix ./opt/ appium@$APPIUM_VERSION
|
||||||
|
|
||||||
|
COPY android.conf /etc/ld.so.conf.d/
|
||||||
|
COPY fluxbox/aerokube /usr/share/fluxbox/styles/
|
||||||
|
COPY fluxbox/init /root/.fluxbox/
|
||||||
|
COPY fluxbox/aerokube.png /usr/share/images/fluxbox/
|
||||||
|
COPY --from=go /devtools/devtools /usr/bin/
|
||||||
|
|
||||||
|
# Android SDK
|
||||||
|
ENV ANDROID_HOME /opt/android-sdk-linux
|
||||||
|
ENV PATH /opt/android-sdk-linux/platform-tools:/opt/android-sdk-linux/tools:/opt/android-sdk-linux/tools/bin:/opt/android-sdk-linux/emulator:$PATH
|
||||||
|
ENV LD_LIBRARY_PATH ${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/gles_swiftshader:${ANDROID_HOME}/emulator/lib64/qt/lib:${ANDROID_HOME}/emulator/lib64/vulkan:${LD_LIBRARY_PATH}
|
||||||
|
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64
|
||||||
|
|
||||||
|
ARG ANDROID_DEVICE=""
|
||||||
|
ARG REPLACE_IMG="y"
|
||||||
|
ARG AVD_NAME="android6.0-1"
|
||||||
|
ARG BUILD_TOOLS="build-tools;23.0.1"
|
||||||
|
ARG PLATFORM="android-23"
|
||||||
|
ARG EMULATOR_IMAGE="system-images;android-23;default;x86"
|
||||||
|
ARG EMULATOR_IMAGE_TYPE="default"
|
||||||
|
ARG ANDROID_ABI="x86"
|
||||||
|
ARG SDCARD_SIZE="500"
|
||||||
|
ARG USERDATA_SIZE="500"
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
curl -o sdk-tools.zip https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip && \
|
||||||
|
mkdir -p /opt/android-sdk-linux /root/.config/Android\ Open\ Source\ Project/ && \
|
||||||
|
unzip -q sdk-tools.zip -d /opt/android-sdk-linux && \
|
||||||
|
rm sdk-tools.zip && \
|
||||||
|
yes | sdkmanager --licenses
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
sdkmanager "emulator" "tools" "platform-tools" "$BUILD_TOOLS" "platforms;$PLATFORM" "$EMULATOR_IMAGE" && \
|
||||||
|
mksdcard "$SDCARD_SIZE"M sdcard.img && \
|
||||||
|
echo "no" | ( \
|
||||||
|
([ -n "$ANDROID_DEVICE" ] && avdmanager create avd -n "$AVD_NAME" -k "$EMULATOR_IMAGE" --abi "$ANDROID_ABI" --device "$ANDROID_DEVICE" --sdcard /sdcard.img ) || \
|
||||||
|
avdmanager create avd -n "$AVD_NAME" -k "$EMULATOR_IMAGE" --abi "$ANDROID_ABI" --sdcard /sdcard.img \
|
||||||
|
) && \
|
||||||
|
ldconfig && \
|
||||||
|
( \
|
||||||
|
resize2fs /root/.android/avd/$AVD_NAME.avd/userdata.img "$USERDATA_SIZE"M || \
|
||||||
|
/opt/android-sdk-linux/emulator/qemu-img resize -f raw /root/.android/avd/$AVD_NAME.avd/userdata.img "$USERDATA_SIZE"M \
|
||||||
|
) && \
|
||||||
|
([ -z "$REPLACE_IMG" ] || mv /root/.android/avd/$AVD_NAME.avd/userdata.img /root/.android/avd/$AVD_NAME.avd/userdata-qemu.img ) && \
|
||||||
|
rm /opt/android-sdk-linux/system-images/$PLATFORM/$EMULATOR_IMAGE_TYPE/"$ANDROID_ABI"/userdata.img
|
||||||
|
|
||||||
|
COPY ["Emulator.conf", "/root/.config/Android Open Source Project/Emulator.conf"]
|
||||||
|
COPY emulator-snapshot.sh tmp/chromedriver* *.apk /usr/bin/
|
||||||
|
|
||||||
|
# Entrypoint
|
||||||
|
COPY tmp/entrypoint.sh /
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[General]
|
||||||
|
showNestedWarning=false
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/opt/android-sdk-linux/emulator/lib64
|
||||||
|
/opt/android-sdk-linux/emulator/lib64/gles_swiftshader
|
||||||
Executable
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
MAX_ATTEMPTS=5
|
||||||
|
adb root
|
||||||
|
adb devices | grep emulator | cut -f1 | while read id; do
|
||||||
|
apks=(/usr/bin/*.apk)
|
||||||
|
for apk in "${apks[@]}"; do
|
||||||
|
if [ -r "$apk" ]; then
|
||||||
|
for i in `seq 1 ${MAX_ATTEMPTS}`; do
|
||||||
|
echo "Installing $apk (attempt #$i of $MAX_ATTEMPTS)"
|
||||||
|
adb -s "$id" install -r "$apk" && break || sleep 15 && echo "Retrying to install $apk"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
adb -s "$id" emu kill -2 || true
|
||||||
|
done
|
||||||
|
rm -f /tmp/.X99-lock || true
|
||||||
Executable
+88
@@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
CHROMEDRIVER_PORT=9515
|
||||||
|
BOOTSTRAP_PORT=4725
|
||||||
|
EMULATOR=emulator-5554
|
||||||
|
APPIUM_ARGS=${APPIUM_ARGS:-""}
|
||||||
|
EMULATOR_ARGS=${EMULATOR_ARGS:-""}
|
||||||
|
PORT=${PORT:-"4444"}
|
||||||
|
DISPLAY_NUM=99
|
||||||
|
export DISPLAY=":$DISPLAY_NUM"
|
||||||
|
SCREEN_RESOLUTION=${SCREEN_RESOLUTION:-"1920x1080x24"}
|
||||||
|
SKIN=${SKIN:-"1080x1920"}
|
||||||
|
STOP=""
|
||||||
|
VERBOSE=${VERBOSE:-""}
|
||||||
|
|
||||||
|
if [ -z "$VERBOSE" ]; then
|
||||||
|
if [ -z "$APPIUM_ARGS" ]; then
|
||||||
|
APPIUM_ARGS="--log-level error"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
EMULATOR_ARGS="$EMULATOR_ARGS -verbose"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
clean() {
|
||||||
|
STOP="yes"
|
||||||
|
if [ -n "$APPIUM_PID" ]; then
|
||||||
|
kill -TERM "$APPIUM_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$EMULATOR_PID" ]; then
|
||||||
|
kill -TERM "$EMULATOR_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$X11VNC_PID" ]; then
|
||||||
|
kill -TERM "$X11VNC_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$DEVTOOLS_PID" ]; then
|
||||||
|
kill -TERM "$DEVTOOLS_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$XVFB_PID" ]; then
|
||||||
|
kill -TERM "$XVFB_PID"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
trap clean SIGINT SIGTERM
|
||||||
|
|
||||||
|
/usr/bin/xvfb-run -e /dev/stdout -l -n "$DISPLAY_NUM" -s "-ac -screen 0 $SCREEN_RESOLUTION -noreset -listen tcp" /usr/bin/fluxbox -display "$DISPLAY" -log /tmp/fluxbox.log 2>/dev/null &
|
||||||
|
XVFB_PID=$!
|
||||||
|
|
||||||
|
retcode=1
|
||||||
|
until [ $retcode -eq 0 ] || [ -n "$STOP" ]; do
|
||||||
|
DISPLAY="$DISPLAY" wmctrl -m >/dev/null 2>&1
|
||||||
|
retcode=$?
|
||||||
|
if [ $retcode -ne 0 ]; then
|
||||||
|
echo Waiting X server...
|
||||||
|
sleep 0.1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ -n "$STOP" ]; then exit 0; fi
|
||||||
|
|
||||||
|
if [ "$ENABLE_VNC" != "true" ] && [ "$ENABLE_VIDEO" != "true" ]; then
|
||||||
|
EMULATOR_ARGS="$EMULATOR_ARGS -no-window"
|
||||||
|
fi
|
||||||
|
ANDROID_AVD_HOME=/root/.android/avd DISPLAY="$DISPLAY" /opt/android-sdk-linux/emulator/emulator ${EMULATOR_ARGS} -writable-system -no-boot-anim -no-audio -no-jni -avd @AVD_NAME@ -sdcard /sdcard.img -skin "$SKIN" -skindir /opt/android-sdk-linux/platforms/@PLATFORM@/skins/ -gpu swiftshader_indirect -ranchu -qemu -enable-kvm &
|
||||||
|
EMULATOR_PID=$!
|
||||||
|
|
||||||
|
if [ "$ENABLE_VNC" == "true" ]; then
|
||||||
|
x11vnc -display "$DISPLAY" -passwd selenoid -shared -forever -loop500 -rfbport 5900 -rfbportv6 5900 -logfile /tmp/x11vnc.log &
|
||||||
|
X11VNC_PID=$!
|
||||||
|
fi
|
||||||
|
|
||||||
|
while [ "$(adb shell getprop sys.boot_completed | tr -d '\r')" != "1" ] && [ -z "$STOP" ] ; do sleep 1; done
|
||||||
|
if [ -n "$STOP" ]; then exit 0; fi
|
||||||
|
|
||||||
|
DEFAULT_CAPABILITIES='"androidNaturalOrientation": true, "deviceName": "android", "platformName": "Android", "noReset": true, "udid":"'$EMULATOR'"'
|
||||||
|
if [ -n "@CHROME_MOBILE@" ]; then
|
||||||
|
while ip addr | grep inet | grep -q tentative > /dev/null; do sleep 0.1; done
|
||||||
|
DEFAULT_CAPABILITIES=$DEFAULT_CAPABILITIES', "chromedriverPort": '$CHROMEDRIVER_PORT
|
||||||
|
/usr/bin/devtools --android &
|
||||||
|
DEVTOOLS_PID=$!
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -x "/usr/bin/chromedriver" ]; then
|
||||||
|
DEFAULT_CAPABILITIES=$DEFAULT_CAPABILITIES',"chromedriverExecutable": "/usr/bin/chromedriver"'
|
||||||
|
fi
|
||||||
|
|
||||||
|
/opt/node_modules/.bin/appium -a 0.0.0.0 -p "$PORT" -bp "$BOOTSTRAP_PORT" --log-timestamp --log-no-colors --command-timeout 90 --no-reset ${APPIUM_ARGS} --default-capabilities "{$DEFAULT_CAPABILITIES}" &
|
||||||
|
APPIUM_PID=$!
|
||||||
|
|
||||||
|
wait
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# name: aerokube (based on ubuntu_light)
|
||||||
|
# made: paultag
|
||||||
|
# date: 16-10-2010
|
||||||
|
# http://pault.ag/
|
||||||
|
#
|
||||||
|
############################################################## BACKGROUND ######
|
||||||
|
|
||||||
|
# background: flat
|
||||||
|
# background.color: #2c001e
|
||||||
|
# background.colorTo: #2c001e
|
||||||
|
|
||||||
|
background: fullscreen
|
||||||
|
background.pixmap: /usr/share/images/fluxbox/aerokube.png
|
||||||
|
|
||||||
|
############################################################## FONTS ##########
|
||||||
|
|
||||||
|
|
||||||
|
menu.frame.font: Ubuntu-10:bold
|
||||||
|
menu.title.font: Ubuntu-12:bold
|
||||||
|
toolbar.clock.font: Ubuntu-10:bold
|
||||||
|
toolbar.workspace.font: Ubuntu-12:bold
|
||||||
|
toolbar.iconbar.focused.font: Ubuntu-10:bold
|
||||||
|
toolbar.iconbar.unfocused.font: Ubuntu-10
|
||||||
|
window.font: Ubuntu-10
|
||||||
|
|
||||||
|
############################################################## MENU ###########
|
||||||
|
|
||||||
|
menu.bevelWidth: 1
|
||||||
|
|
||||||
|
#menu.itemHeight: 35
|
||||||
|
#menu.titleHeight: 21
|
||||||
|
menu.borderColor: #525252
|
||||||
|
menu.borderWidth: 1
|
||||||
|
|
||||||
|
menu.bullet: Triangle
|
||||||
|
menu.bullet.position: Right
|
||||||
|
menu.frame.underlineColor: #ffffff
|
||||||
|
|
||||||
|
menu.title: flat gradient rectangle
|
||||||
|
menu.title.justify: center
|
||||||
|
menu.title.color: #FFFFFF
|
||||||
|
menu.title.colorTo: #FFFFFF
|
||||||
|
menu.title.textColor: #333333
|
||||||
|
|
||||||
|
menu.frame: flat gradient crossdiagonal
|
||||||
|
menu.frame.justify: left
|
||||||
|
menu.frame.color: #FFFFFF
|
||||||
|
menu.frame.colorTo: #FFFFFF
|
||||||
|
menu.frame.textColor: #333333
|
||||||
|
menu.frame.disableColor: #aea79f
|
||||||
|
|
||||||
|
menu.hilite: flat gradient rectangle
|
||||||
|
menu.hilite.color: #aea79f
|
||||||
|
menu.hilite.colorTo: #aea79f
|
||||||
|
menu.hilite.textColor: #000000
|
||||||
|
|
||||||
|
############################################################## TOOLBAR ########
|
||||||
|
|
||||||
|
toolbar.bevelWidth: 0
|
||||||
|
|
||||||
|
toolbar.borderWidth: 1
|
||||||
|
toolbar.borderColor: #aea79f
|
||||||
|
|
||||||
|
toolbar.height: 20
|
||||||
|
|
||||||
|
toolbar.justify: center
|
||||||
|
|
||||||
|
toolbar: flat gradient rectangle
|
||||||
|
toolbar.pixmap:
|
||||||
|
toolbar.color: #aea79f
|
||||||
|
toolbar.colorTo: #aea79f
|
||||||
|
|
||||||
|
toolbar.clock: parentrelative
|
||||||
|
toolbar.clock.justify: center
|
||||||
|
toolbar.clock.color: #
|
||||||
|
toolbar.clock.colorTo: #
|
||||||
|
toolbar.clock.textColor: #333333
|
||||||
|
|
||||||
|
toolbar.workspace: parentrelative
|
||||||
|
toolbar.workspace.justify: Center
|
||||||
|
toolbar.workspace.color: #
|
||||||
|
toolbar.workspace.colorTo: #
|
||||||
|
toolbar.workspace.textColor: #333333
|
||||||
|
|
||||||
|
toolbar.button: parentrelative
|
||||||
|
toolbar.button.color: #333333
|
||||||
|
toolbar.button.colorTo: #333333
|
||||||
|
toolbar.button.picColor: #333333
|
||||||
|
toolbar.button.pressed: parentrelative
|
||||||
|
toolbar.button.pressed.color: #aea79f
|
||||||
|
toolbar.button.pressed.colorTo: #aea79f
|
||||||
|
toolbar.button.pressed.picColor: #000000
|
||||||
|
|
||||||
|
toolbar.iconbar.borderWidth: 1
|
||||||
|
toolbar.iconbar.borderColor: #333333
|
||||||
|
|
||||||
|
toolbar.iconbar.empty: parentrelative
|
||||||
|
toolbar.iconbar.empty.color: #
|
||||||
|
toolbar.iconbar.empty.colorTo: #
|
||||||
|
|
||||||
|
toolbar.iconbar.focused.borderWidth: 1
|
||||||
|
toolbar.iconbar.focused.borderColor: #333333
|
||||||
|
toolbar.iconbar.focused: flat gradient rectangle
|
||||||
|
toolbar.iconbar.focused.color: #FFFFFF
|
||||||
|
toolbar.iconbar.focused.colorTo: #FFFFFF
|
||||||
|
toolbar.iconbar.focused.textColor: #333333
|
||||||
|
toolbar.iconbar.focused.justify: center
|
||||||
|
|
||||||
|
toolbar.iconbar.unfocused.borderWidth: 1
|
||||||
|
toolbar.iconbar.unfocused.borderColor: #525252
|
||||||
|
toolbar.iconbar.unfocused: flat gradient rectangle
|
||||||
|
toolbar.iconbar.unfocused.color: #aea79f
|
||||||
|
toolbar.iconbar.unfocused.colorTo: #aea79f
|
||||||
|
toolbar.iconbar.unfocused.textColor: #444444
|
||||||
|
toolbar.iconbar.unfocused.justify: center
|
||||||
|
|
||||||
|
############################################################## WINDOW #########
|
||||||
|
|
||||||
|
window.roundCorners: TopRight TopLeft
|
||||||
|
window.bevelWidth: 4
|
||||||
|
window.shade: false
|
||||||
|
window.borderWidth: 1
|
||||||
|
window.borderColor: #333333
|
||||||
|
|
||||||
|
window.justify: Center
|
||||||
|
window.title.height: 21
|
||||||
|
|
||||||
|
window.title.focus: flat gradient rectangle
|
||||||
|
window.title.focus.color: #000000
|
||||||
|
window.title.focus.colorTo: #333333
|
||||||
|
window.title.unfocus: flat gradient rectangle
|
||||||
|
window.title.unfocus.color: #111111
|
||||||
|
window.title.unfocus.colorTo: #444444
|
||||||
|
|
||||||
|
window.label.focus: parentrelative
|
||||||
|
window.label.focus.color: #
|
||||||
|
window.label.focus.colorTo: #
|
||||||
|
window.label.focus.textColor: #FFFFFF
|
||||||
|
window.label.unfocus: parentrelative
|
||||||
|
window.label.unfocus.color: #
|
||||||
|
window.label.unfocus.colorTo: #
|
||||||
|
window.label.unfocus.textColor: #aea79f
|
||||||
|
|
||||||
|
window.button.focus: parentrelative
|
||||||
|
window.button.focus.color: #
|
||||||
|
window.button.focus.colorTo: #
|
||||||
|
window.button.focus.picColor: #FFFFFF
|
||||||
|
window.button.unfocus: parentrelative
|
||||||
|
window.button.unfocus.Color: #
|
||||||
|
window.button.unfocus.ColorTo: #
|
||||||
|
window.button.unfocus.picColor: #888888
|
||||||
|
window.button.pressed: parentrelative
|
||||||
|
window.button.pressed.color: #
|
||||||
|
window.button.pressed.colorTo: #
|
||||||
|
window.button.pressed.picColor: #000000
|
||||||
|
|
||||||
|
window.handle.focus: flat
|
||||||
|
window.handle.focus.color: #1f1f1f
|
||||||
|
window.handle.focus.colorTo: #1f1f1f
|
||||||
|
window.handle.unfocus: lat
|
||||||
|
window.handle.unfocus.color: #1f1f1f
|
||||||
|
window.handle.unfocus.colorTo: #1f1f1f
|
||||||
|
window.handleWidth: 2
|
||||||
|
|
||||||
|
window.grip.focus: flat
|
||||||
|
window.grip.focus.color: #525252
|
||||||
|
window.grip.focus.colorTo: #525252
|
||||||
|
window.grip.unfocus: flat
|
||||||
|
window.grip.unfocus.color: #1f1f1f
|
||||||
|
window.grip.unfocus.colorTo: #1f1f1f
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# EOF
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
@@ -0,0 +1,7 @@
|
|||||||
|
session.menuFile: ~/.fluxbox/menu
|
||||||
|
session.keyFile: ~/.fluxbox/keys
|
||||||
|
session.styleFile: /usr/share/fluxbox/styles/aerokube
|
||||||
|
session.configVersion: 13
|
||||||
|
session.screen0.strftimeFormat: %d %b, %a %02k:%M:%S
|
||||||
|
session.screen0.toolbar.visible: false
|
||||||
|
session.screen0.toolbar.tools:
|
||||||
Executable
+251
@@ -0,0 +1,251 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
die(){
|
||||||
|
echo "$1"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
require_command(){
|
||||||
|
if [ -z "$(command -v $1)" ]; then
|
||||||
|
die "$1 command required for this script to run"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
request_answer(){
|
||||||
|
prompt=$1
|
||||||
|
default_value=$2
|
||||||
|
if [ -n "$default_value" ]; then
|
||||||
|
prompt="$prompt [$default_value]"
|
||||||
|
fi
|
||||||
|
read -r -p "$prompt " value
|
||||||
|
if [ -z "$value" ] && [ -n "$default_value" ]; then
|
||||||
|
value="$default_value"
|
||||||
|
fi
|
||||||
|
echo "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_android_version(){
|
||||||
|
version="$1"
|
||||||
|
type=${2:-"default"}
|
||||||
|
abi=${3:-"x86"}
|
||||||
|
avd_name="android$version-1"
|
||||||
|
build_tools="build-tools;29.0.2"
|
||||||
|
replace_img="y"
|
||||||
|
case "$version" in
|
||||||
|
4.4)
|
||||||
|
platform="android-19"
|
||||||
|
emulator_image="system-images;android-19;$type;$abi"
|
||||||
|
;;
|
||||||
|
5.0)
|
||||||
|
platform="android-21"
|
||||||
|
emulator_image="system-images;android-21;$type;$abi"
|
||||||
|
;;
|
||||||
|
5.1)
|
||||||
|
platform="android-22"
|
||||||
|
emulator_image="system-images;android-22;$type;$abi"
|
||||||
|
;;
|
||||||
|
6.0)
|
||||||
|
platform="android-23"
|
||||||
|
emulator_image="system-images;android-23;$type;$abi"
|
||||||
|
;;
|
||||||
|
7.0)
|
||||||
|
platform="android-24"
|
||||||
|
emulator_image="system-images;android-24;$type;$abi"
|
||||||
|
;;
|
||||||
|
7.1)
|
||||||
|
platform="android-25"
|
||||||
|
emulator_image="system-images;android-25;$type;$abi"
|
||||||
|
;;
|
||||||
|
8.0)
|
||||||
|
platform="android-26"
|
||||||
|
emulator_image="system-images;android-26;$type;$abi"
|
||||||
|
;;
|
||||||
|
8.1)
|
||||||
|
platform="android-27"
|
||||||
|
emulator_image="system-images;android-27;$type;$abi"
|
||||||
|
;;
|
||||||
|
9.0)
|
||||||
|
platform="android-28"
|
||||||
|
emulator_image="system-images;android-28;$type;$abi"
|
||||||
|
replace_img=""
|
||||||
|
;;
|
||||||
|
10.0)
|
||||||
|
platform="android-29"
|
||||||
|
emulator_image="system-images;android-29;$type;$abi"
|
||||||
|
replace_img=""
|
||||||
|
;;
|
||||||
|
11.0)
|
||||||
|
platform="android-30"
|
||||||
|
emulator_image="system-images;android-30;$type;$abi"
|
||||||
|
replace_img=""
|
||||||
|
;;
|
||||||
|
12.0)
|
||||||
|
platform="android-31"
|
||||||
|
emulator_image="system-images;android-31;$type;$abi"
|
||||||
|
replace_img=""
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported Android version"
|
||||||
|
false
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_android_image_type(){
|
||||||
|
type="$1"
|
||||||
|
case "$type" in
|
||||||
|
"default" | "google_apis" | "google_apis_playstore" | "android-wear" | "android-tv" )
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported Android image type"
|
||||||
|
false
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_android_abi(){
|
||||||
|
abi="$1"
|
||||||
|
case "$abi" in
|
||||||
|
"armeabi-v7a" | "arm64-v8a" | "x86" | "x86_64" )
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported Application Binary Interface"
|
||||||
|
false
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
download_chromedriver() {
|
||||||
|
pushd "$TMP_DIR"
|
||||||
|
wget -O chromedriver.zip http://chromedriver.storage.googleapis.com/$1/chromedriver_linux64.zip
|
||||||
|
unzip chromedriver.zip
|
||||||
|
rm chromedriver.zip
|
||||||
|
popd
|
||||||
|
}
|
||||||
|
|
||||||
|
test_image(){
|
||||||
|
tests_dir=../../selenoid-container-tests/
|
||||||
|
if [ -d "$tests_dir" ]; then
|
||||||
|
echo "Running test suite on image."
|
||||||
|
docker rm -f selenium || true
|
||||||
|
docker run -d --privileged --name selenium -p 4445:4444 $1
|
||||||
|
echo "Waiting for image to start..."
|
||||||
|
sleep 20
|
||||||
|
pushd "$tests_dir"
|
||||||
|
mvn clean test -Dgrid.connection.url="http://localhost:4445/wd/hub" -Dgrid.browser.name=chrome || true
|
||||||
|
popd
|
||||||
|
docker rm -f selenium || true
|
||||||
|
else
|
||||||
|
echo "Skipping tests as $tests_dir does not exist."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_command "docker"
|
||||||
|
require_command "sed"
|
||||||
|
require_command "true"
|
||||||
|
require_command "false"
|
||||||
|
require_command "wget"
|
||||||
|
require_command "unzip"
|
||||||
|
require_command "cut"
|
||||||
|
|
||||||
|
TMP_DIR="android/tmp"
|
||||||
|
rm -Rf ./"$TMP_DIR" || true
|
||||||
|
mkdir -p "$TMP_DIR"
|
||||||
|
cp android/entrypoint.sh "$TMP_DIR/entrypoint.sh"
|
||||||
|
cp -r ../static/chrome/devtools "$TMP_DIR/devtools"
|
||||||
|
|
||||||
|
appium_version=$(request_answer "Specify Appium version:" "1.18.1")
|
||||||
|
|
||||||
|
until [ "$?" -ne 0 ]; do
|
||||||
|
android_image_type=$(request_answer "Specify Android image type (possible values: \"default\", \"google_apis\", \"google_apis_playstore\", \"android-tv\", \"android-wear\"):" "default")
|
||||||
|
if validate_android_image_type "$android_image_type"; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
until [ "$?" -ne 0 ]; do
|
||||||
|
android_abi=$(request_answer "Specify Application Binary Interface (possible values: \"armeabi-v7a\", \"arm64-v8a\", \"x86\", \"x86_64\"):" "x86")
|
||||||
|
if validate_android_abi "$android_abi"; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
until [ "$?" -ne 0 ]; do
|
||||||
|
android_version=$(request_answer "Specify Android version:" "8.1")
|
||||||
|
if validate_android_version "$android_version" "$android_image_type" "$android_abi"; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
IFS=';' read -ra emulator_image_info <<< "$emulator_image"
|
||||||
|
emulator_image_type=${emulator_image_info[2]}
|
||||||
|
sed -i.bak "s|@AVD_NAME@|$avd_name|g" "$TMP_DIR/entrypoint.sh"
|
||||||
|
sed -i.bak "s|@PLATFORM@|$platform|g" "$TMP_DIR/entrypoint.sh"
|
||||||
|
|
||||||
|
android_device=$(request_answer "Specify device preset name if needed (e.g. \"Nexus 4\"):")
|
||||||
|
sdcard_size=$(request_answer "Specify SD card size, Mb:" 500)
|
||||||
|
userdata_size=$(request_answer "Specify userdata.img size, Mb:" 500)
|
||||||
|
|
||||||
|
image_name="android"
|
||||||
|
default_tag="$android_version"
|
||||||
|
chrome_mobile=$(request_answer "Are you building a Chrome Mobile image (for mobile web testing):" "n")
|
||||||
|
if [ "y" == "$chrome_mobile" ]; then
|
||||||
|
sed -i.bak 's|@CHROME_MOBILE@|yes|g' "$TMP_DIR/entrypoint.sh"
|
||||||
|
image_name="chrome-mobile"
|
||||||
|
else
|
||||||
|
sed -i.bak 's|@CHROME_MOBILE@||g' "$TMP_DIR/entrypoint.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
chromedriver_version=$(request_answer "Specify Chromedriver version if needed (required for Chrome Mobile):")
|
||||||
|
if [ -n "$chromedriver_version" ]; then
|
||||||
|
chrome_major_version="$(cut -d'.' -f1 <<<${chromedriver_version})"
|
||||||
|
chrome_minor_version="$(cut -d'.' -f2 <<<${chromedriver_version})"
|
||||||
|
if [ -n "$chrome_major_version" ] && [ -n "$chrome_minor_version" ]; then
|
||||||
|
default_tag="$chrome_major_version.$chrome_minor_version"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
tag=$(request_answer "Specify image tag:" "selenoid/$image_name:$default_tag")
|
||||||
|
need_quickboot=$(request_answer "Add Android quick boot snapshot?" "y")
|
||||||
|
|
||||||
|
if [ -n "$chromedriver_version" ]; then
|
||||||
|
download_chromedriver "$chromedriver_version"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -Rf *.bak || true
|
||||||
|
set -x
|
||||||
|
|
||||||
|
tmp_tag="$tag"_tmp
|
||||||
|
docker build -t "$tmp_tag" \
|
||||||
|
--build-arg APPIUM_VERSION="$appium_version" \
|
||||||
|
--build-arg ANDROID_DEVICE="$android_device" \
|
||||||
|
--build-arg REPLACE_IMG="$replace_img" \
|
||||||
|
--build-arg AVD_NAME="$avd_name" \
|
||||||
|
--build-arg BUILD_TOOLS="$build_tools" \
|
||||||
|
--build-arg PLATFORM="$platform" \
|
||||||
|
--build-arg EMULATOR_IMAGE="$emulator_image" \
|
||||||
|
--build-arg EMULATOR_IMAGE_TYPE="$emulator_image_type" \
|
||||||
|
--build-arg ANDROID_ABI="$android_abi" \
|
||||||
|
--build-arg SDCARD_SIZE="$sdcard_size" \
|
||||||
|
--build-arg USERDATA_SIZE="$userdata_size" android
|
||||||
|
|
||||||
|
if [ "$need_quickboot" == "y" ]; then
|
||||||
|
id=$(docker run -e CHROME_MOBILE="$chrome_mobile" -d --privileged "$tmp_tag")
|
||||||
|
sleep 60
|
||||||
|
docker exec "$id" "/usr/bin/emulator-snapshot.sh"
|
||||||
|
sleep 30 # Wait for snapshot to save
|
||||||
|
docker commit "$id" "$tag"
|
||||||
|
docker rm -f "$id" || true
|
||||||
|
else
|
||||||
|
docker tag "$tmp_tag" "$tag"
|
||||||
|
fi
|
||||||
|
docker rmi -f "$tmp_tag" || true
|
||||||
|
set +x
|
||||||
|
|
||||||
|
if [ "y" == "$chrome_mobile" ]; then
|
||||||
|
test_image "$tag"
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r -p "Push?" yn
|
||||||
|
if [ "$yn" == "y" ]; then
|
||||||
|
docker push "$tag"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
listen string
|
||||||
|
gracePeriod time.Duration
|
||||||
|
target string
|
||||||
|
queue = make(chan struct{}, 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.StringVar(&listen, "listen", ":4444", "host and port to listen to")
|
||||||
|
flag.DurationVar(&gracePeriod, "grace-period", 300*time.Second, "graceful shutdown period")
|
||||||
|
flag.StringVar(&target, "target", "http://localhost:4723", "target proxy url")
|
||||||
|
flag.Parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func signalContext() (context.Context, context.CancelFunc) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
go func() {
|
||||||
|
defer cancel()
|
||||||
|
stop := make(chan os.Signal)
|
||||||
|
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||||
|
<-stop
|
||||||
|
}()
|
||||||
|
return ctx, cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := signalContext()
|
||||||
|
defer cancel()
|
||||||
|
err := mainCtx(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mux(ctx context.Context, u *url.URL) http.Handler {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/wd/hub/session", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
select {
|
||||||
|
case <-r.Context().Done():
|
||||||
|
return
|
||||||
|
case <-ctx.Done():
|
||||||
|
http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
case queue <- struct{}{}:
|
||||||
|
}
|
||||||
|
(&httputil.ReverseProxy{
|
||||||
|
Director: func(r *http.Request) {
|
||||||
|
r.URL.Scheme, r.URL.Host = u.Scheme, u.Host
|
||||||
|
},
|
||||||
|
ModifyResponse: func(resp *http.Response) error {
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
<-queue
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
|
<-queue
|
||||||
|
log.Printf("proxy to %s: %v", u.String(), err)
|
||||||
|
http.Error(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
|
||||||
|
|
||||||
|
},
|
||||||
|
}).ServeHTTP(w, r)
|
||||||
|
}))
|
||||||
|
mux.HandleFunc("/wd/hub/session/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
(&httputil.ReverseProxy{
|
||||||
|
Director: func(r *http.Request) {
|
||||||
|
r.URL.Scheme, r.URL.Host = u.Scheme, u.Host
|
||||||
|
},
|
||||||
|
}).ServeHTTP(w, r)
|
||||||
|
fragments := strings.Split(r.URL.Path, "/")
|
||||||
|
if r.Method == http.MethodDelete && len(fragments) == 5 {
|
||||||
|
<-queue
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
return mux
|
||||||
|
}
|
||||||
|
|
||||||
|
func mainCtx(ctx context.Context) error {
|
||||||
|
target, err := url.Parse(target)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parse target url: %v", err)
|
||||||
|
}
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: listen,
|
||||||
|
Handler: mux(ctx, target),
|
||||||
|
}
|
||||||
|
e := make(chan error)
|
||||||
|
go func() {
|
||||||
|
e <- server.ListenAndServe()
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case err := <-e:
|
||||||
|
return err
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
|
log.Printf("starting graceful shutdown in %v]", gracePeriod)
|
||||||
|
shCtx, cancel := context.WithTimeout(context.Background(), gracePeriod)
|
||||||
|
defer cancel()
|
||||||
|
if err := server.Shutdown(shCtx); err != nil {
|
||||||
|
return fmt.Errorf("graceful shutdown: %v", err)
|
||||||
|
}
|
||||||
|
log.Printf("stopped")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
FROM golang:1.17 as go
|
||||||
|
|
||||||
|
COPY xseld /xseld
|
||||||
|
|
||||||
|
COPY fileserver /fileserver
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y upx-ucl libx11-dev && \
|
||||||
|
cd /xseld && \
|
||||||
|
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" && \
|
||||||
|
upx /xseld/xseld && \
|
||||||
|
cd /fileserver && \
|
||||||
|
go test -race && \
|
||||||
|
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" && \
|
||||||
|
upx /fileserver/fileserver
|
||||||
|
|
||||||
|
FROM ubuntu:20.04
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt update && \
|
||||||
|
apt remove -y libcurl4 && \
|
||||||
|
apt install -y apt-transport-https ca-certificates tzdata locales libcurl4 curl gnupg && \
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt -y upgrade && \
|
||||||
|
echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections && \
|
||||||
|
echo 'UTC' | tee /etc/timezone && \
|
||||||
|
dpkg-reconfigure -f noninteractive tzdata && \
|
||||||
|
echo "gtk-cursor-blink=0" > /root/.gtkrc-2.0 && \
|
||||||
|
apt update && \
|
||||||
|
apt install -y ttf-mscorefonts-installer \
|
||||||
|
ttf-dejavu-core \
|
||||||
|
fontconfig \
|
||||||
|
fontconfig-config \
|
||||||
|
fonts-dejavu-core \
|
||||||
|
fonts-liberation \
|
||||||
|
fonts-ubuntu-font-family-console \
|
||||||
|
fonts-wqy-zenhei \
|
||||||
|
fonts-thai-tlwg-ttf \
|
||||||
|
fonts-ipafont-mincho \
|
||||||
|
fonts-sahadeva \
|
||||||
|
fonts-noto-unhinted \
|
||||||
|
fonts-noto-color-emoji \
|
||||||
|
libfontconfig1 \
|
||||||
|
libfontenc1 \
|
||||||
|
libfreetype6 \
|
||||||
|
libxfont2 \
|
||||||
|
libxft2 \
|
||||||
|
libnss3-tools \
|
||||||
|
xfonts-base \
|
||||||
|
xfonts-encodings \
|
||||||
|
xfonts-utils \
|
||||||
|
flashplugin-installer \
|
||||||
|
xvfb \
|
||||||
|
pulseaudio \
|
||||||
|
fluxbox \
|
||||||
|
x11vnc \
|
||||||
|
feh \
|
||||||
|
wmctrl \
|
||||||
|
libnss-wrapper \
|
||||||
|
xsel && \
|
||||||
|
mkdir -p /var/lib/locales/supported.d/ && grep UTF-8 /usr/share/i18n/SUPPORTED > /var/lib/locales/supported.d/all && \
|
||||||
|
locale-gen && update-locale && \
|
||||||
|
fc-cache -f -v && \
|
||||||
|
adduser --system --home /home/selenium --uid 4096 \
|
||||||
|
--ingroup root --disabled-password --shell /bin/bash selenium && \
|
||||||
|
mkdir -p /home/selenium/Downloads && \
|
||||||
|
mkdir -p /home/selenium/.fluxbox && \
|
||||||
|
chgrp -R 0 /home/selenium && \
|
||||||
|
chmod -R g=u /home/selenium && \
|
||||||
|
ln -sf /bin/true /usr/bin/xdg-open && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -Rf /tmp/* && rm -Rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY fluxbox /usr/share/fluxbox/styles/
|
||||||
|
COPY --chown=selenium:root fluxbox /home/selenium/.fluxbox/
|
||||||
|
COPY aerokube.png /usr/share/images/fluxbox/
|
||||||
|
|
||||||
|
COPY --from=go /fileserver/fileserver /usr/bin/
|
||||||
|
COPY --from=go /xseld/xseld /usr/bin/
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
module fileserver
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require github.com/aandryashin/matchers v0.0.0-20161126170413-435295ea180e
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
github.com/aandryashin/matchers v0.0.0-20161126170413-435295ea180e h1:ogUKYFNcdYUIBSLibE4+EjbTJazoHr5JsWWx21Lpn8c=
|
||||||
|
github.com/aandryashin/matchers v0.0.0-20161126170413-435295ea180e/go.mod h1:cbmYNkm9xeQlNoWEPtOUcvNok2gSD7ErMnYkRW+eHi8=
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
dir, err := downloadsDir()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Fatal(http.ListenAndServe(":8080", mux(dir)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadsDir() (string, error) {
|
||||||
|
homeDir := os.Getenv("HOME")
|
||||||
|
if homeDir == "" {
|
||||||
|
homeDir = "/home/selenium"
|
||||||
|
}
|
||||||
|
dir := filepath.Join(homeDir, "Downloads")
|
||||||
|
err := os.MkdirAll(dir, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create downloads dir: %v", err)
|
||||||
|
}
|
||||||
|
return dir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsonParam = "json"
|
||||||
|
|
||||||
|
func mux(dir string) http.Handler {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == http.MethodDelete {
|
||||||
|
deleteFileIfExists(w, r, dir)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok := r.URL.Query()[jsonParam]; ok {
|
||||||
|
listFilesAsJson(w, dir)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.FileServer(http.Dir(dir)).ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
return mux
|
||||||
|
}
|
||||||
|
|
||||||
|
func listFilesAsJson(w http.ResponseWriter, dir string) {
|
||||||
|
files, err := ioutil.ReadDir(dir)
|
||||||
|
sort.Slice(files, func(i, j int) bool {
|
||||||
|
return files[i].ModTime().After(files[j].ModTime())
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret := []string{}
|
||||||
|
for _, f := range files {
|
||||||
|
ret = append(ret, f.Name())
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteFileIfExists(w http.ResponseWriter, r *http.Request, dir string) {
|
||||||
|
fileName := strings.TrimPrefix(r.URL.Path, "/")
|
||||||
|
filePath := filepath.Join(dir, fileName)
|
||||||
|
_, err := os.Stat(filePath)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Unknown file %s", fileName), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = os.Remove(filePath)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Failed to delete file %s: %v", fileName, err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/aandryashin/matchers"
|
||||||
|
. "github.com/aandryashin/matchers/httpresp"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dir string
|
||||||
|
srv *httptest.Server
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dir, _ = ioutil.TempDir("", "fileserver")
|
||||||
|
srv = httptest.NewServer(mux(dir))
|
||||||
|
}
|
||||||
|
|
||||||
|
func withUrl(path string) string {
|
||||||
|
return srv.URL + path
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDownloadAndRemoveFile(t *testing.T) {
|
||||||
|
tempFile, _ := ioutil.TempFile(dir, "fileserver")
|
||||||
|
ioutil.WriteFile(tempFile.Name(), []byte("test-data"), 0644)
|
||||||
|
tempFileName := filepath.Base(tempFile.Name())
|
||||||
|
resp, err := http.Get(withUrl("/" + tempFileName))
|
||||||
|
AssertThat(t, err, Is{nil})
|
||||||
|
AssertThat(t, resp, Code{200})
|
||||||
|
_, err = os.Stat(tempFile.Name())
|
||||||
|
AssertThat(t, err, Is{nil})
|
||||||
|
|
||||||
|
rsp, err := http.Get(withUrl("/?json"))
|
||||||
|
AssertThat(t, err, Is{nil})
|
||||||
|
AssertThat(t, rsp, Code{http.StatusOK})
|
||||||
|
var files []string
|
||||||
|
AssertThat(t, rsp, IsJson{&files})
|
||||||
|
AssertThat(t, files, EqualTo{[]string{tempFileName}})
|
||||||
|
|
||||||
|
req, _ := http.NewRequest(http.MethodDelete, withUrl("/" + tempFileName), nil)
|
||||||
|
resp, err = http.DefaultClient.Do(req)
|
||||||
|
AssertThat(t, err, Is{nil})
|
||||||
|
AssertThat(t, resp, Code{200})
|
||||||
|
_, err = os.Stat(tempFile.Name())
|
||||||
|
AssertThat(t, err, Not{nil})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveMissingFile(t *testing.T) {
|
||||||
|
req, _ := http.NewRequest(http.MethodDelete, withUrl("/missing-file"), nil)
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
AssertThat(t, err, Is{nil})
|
||||||
|
AssertThat(t, resp, Code{404})
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# name: aerokube (based on ubuntu_light)
|
||||||
|
# made: paultag
|
||||||
|
# date: 16-10-2010
|
||||||
|
# http://pault.ag/
|
||||||
|
#
|
||||||
|
############################################################## BACKGROUND ######
|
||||||
|
|
||||||
|
# background: flat
|
||||||
|
# background.color: #2c001e
|
||||||
|
# background.colorTo: #2c001e
|
||||||
|
|
||||||
|
background: fullscreen
|
||||||
|
background.pixmap: /usr/share/images/fluxbox/aerokube.png
|
||||||
|
|
||||||
|
############################################################## FONTS ##########
|
||||||
|
|
||||||
|
|
||||||
|
menu.frame.font: Ubuntu-10:bold
|
||||||
|
menu.title.font: Ubuntu-12:bold
|
||||||
|
toolbar.clock.font: Ubuntu-10:bold
|
||||||
|
toolbar.workspace.font: Ubuntu-12:bold
|
||||||
|
toolbar.iconbar.focused.font: Ubuntu-10:bold
|
||||||
|
toolbar.iconbar.unfocused.font: Ubuntu-10
|
||||||
|
window.font: Ubuntu-10
|
||||||
|
|
||||||
|
############################################################## MENU ###########
|
||||||
|
|
||||||
|
menu.bevelWidth: 1
|
||||||
|
|
||||||
|
#menu.itemHeight: 35
|
||||||
|
#menu.titleHeight: 21
|
||||||
|
menu.borderColor: #525252
|
||||||
|
menu.borderWidth: 1
|
||||||
|
|
||||||
|
menu.bullet: Triangle
|
||||||
|
menu.bullet.position: Right
|
||||||
|
menu.frame.underlineColor: #ffffff
|
||||||
|
|
||||||
|
menu.title: flat gradient rectangle
|
||||||
|
menu.title.justify: center
|
||||||
|
menu.title.color: #FFFFFF
|
||||||
|
menu.title.colorTo: #FFFFFF
|
||||||
|
menu.title.textColor: #333333
|
||||||
|
|
||||||
|
menu.frame: flat gradient crossdiagonal
|
||||||
|
menu.frame.justify: left
|
||||||
|
menu.frame.color: #FFFFFF
|
||||||
|
menu.frame.colorTo: #FFFFFF
|
||||||
|
menu.frame.textColor: #333333
|
||||||
|
menu.frame.disableColor: #aea79f
|
||||||
|
|
||||||
|
menu.hilite: flat gradient rectangle
|
||||||
|
menu.hilite.color: #aea79f
|
||||||
|
menu.hilite.colorTo: #aea79f
|
||||||
|
menu.hilite.textColor: #000000
|
||||||
|
|
||||||
|
############################################################## TOOLBAR ########
|
||||||
|
|
||||||
|
toolbar.bevelWidth: 0
|
||||||
|
|
||||||
|
toolbar.borderWidth: 1
|
||||||
|
toolbar.borderColor: #aea79f
|
||||||
|
|
||||||
|
toolbar.height: 20
|
||||||
|
|
||||||
|
toolbar.justify: center
|
||||||
|
|
||||||
|
toolbar: flat gradient rectangle
|
||||||
|
toolbar.pixmap:
|
||||||
|
toolbar.color: #aea79f
|
||||||
|
toolbar.colorTo: #aea79f
|
||||||
|
|
||||||
|
toolbar.clock: parentrelative
|
||||||
|
toolbar.clock.justify: center
|
||||||
|
toolbar.clock.color: #
|
||||||
|
toolbar.clock.colorTo: #
|
||||||
|
toolbar.clock.textColor: #333333
|
||||||
|
|
||||||
|
toolbar.workspace: parentrelative
|
||||||
|
toolbar.workspace.justify: Center
|
||||||
|
toolbar.workspace.color: #
|
||||||
|
toolbar.workspace.colorTo: #
|
||||||
|
toolbar.workspace.textColor: #333333
|
||||||
|
|
||||||
|
toolbar.button: parentrelative
|
||||||
|
toolbar.button.color: #333333
|
||||||
|
toolbar.button.colorTo: #333333
|
||||||
|
toolbar.button.picColor: #333333
|
||||||
|
toolbar.button.pressed: parentrelative
|
||||||
|
toolbar.button.pressed.color: #aea79f
|
||||||
|
toolbar.button.pressed.colorTo: #aea79f
|
||||||
|
toolbar.button.pressed.picColor: #000000
|
||||||
|
|
||||||
|
toolbar.iconbar.borderWidth: 1
|
||||||
|
toolbar.iconbar.borderColor: #333333
|
||||||
|
|
||||||
|
toolbar.iconbar.empty: parentrelative
|
||||||
|
toolbar.iconbar.empty.color: #
|
||||||
|
toolbar.iconbar.empty.colorTo: #
|
||||||
|
|
||||||
|
toolbar.iconbar.focused.borderWidth: 1
|
||||||
|
toolbar.iconbar.focused.borderColor: #333333
|
||||||
|
toolbar.iconbar.focused: flat gradient rectangle
|
||||||
|
toolbar.iconbar.focused.color: #FFFFFF
|
||||||
|
toolbar.iconbar.focused.colorTo: #FFFFFF
|
||||||
|
toolbar.iconbar.focused.textColor: #333333
|
||||||
|
toolbar.iconbar.focused.justify: center
|
||||||
|
|
||||||
|
toolbar.iconbar.unfocused.borderWidth: 1
|
||||||
|
toolbar.iconbar.unfocused.borderColor: #525252
|
||||||
|
toolbar.iconbar.unfocused: flat gradient rectangle
|
||||||
|
toolbar.iconbar.unfocused.color: #aea79f
|
||||||
|
toolbar.iconbar.unfocused.colorTo: #aea79f
|
||||||
|
toolbar.iconbar.unfocused.textColor: #444444
|
||||||
|
toolbar.iconbar.unfocused.justify: center
|
||||||
|
|
||||||
|
############################################################## WINDOW #########
|
||||||
|
|
||||||
|
window.roundCorners: TopRight TopLeft
|
||||||
|
window.bevelWidth: 4
|
||||||
|
window.shade: false
|
||||||
|
window.borderWidth: 1
|
||||||
|
window.borderColor: #333333
|
||||||
|
|
||||||
|
window.justify: Center
|
||||||
|
window.title.height: 21
|
||||||
|
|
||||||
|
window.title.focus: flat gradient rectangle
|
||||||
|
window.title.focus.color: #000000
|
||||||
|
window.title.focus.colorTo: #333333
|
||||||
|
window.title.unfocus: flat gradient rectangle
|
||||||
|
window.title.unfocus.color: #111111
|
||||||
|
window.title.unfocus.colorTo: #444444
|
||||||
|
|
||||||
|
window.label.focus: parentrelative
|
||||||
|
window.label.focus.color: #
|
||||||
|
window.label.focus.colorTo: #
|
||||||
|
window.label.focus.textColor: #FFFFFF
|
||||||
|
window.label.unfocus: parentrelative
|
||||||
|
window.label.unfocus.color: #
|
||||||
|
window.label.unfocus.colorTo: #
|
||||||
|
window.label.unfocus.textColor: #aea79f
|
||||||
|
|
||||||
|
window.button.focus: parentrelative
|
||||||
|
window.button.focus.color: #
|
||||||
|
window.button.focus.colorTo: #
|
||||||
|
window.button.focus.picColor: #FFFFFF
|
||||||
|
window.button.unfocus: parentrelative
|
||||||
|
window.button.unfocus.Color: #
|
||||||
|
window.button.unfocus.ColorTo: #
|
||||||
|
window.button.unfocus.picColor: #888888
|
||||||
|
window.button.pressed: parentrelative
|
||||||
|
window.button.pressed.color: #
|
||||||
|
window.button.pressed.colorTo: #
|
||||||
|
window.button.pressed.picColor: #000000
|
||||||
|
|
||||||
|
window.handle.focus: flat
|
||||||
|
window.handle.focus.color: #1f1f1f
|
||||||
|
window.handle.focus.colorTo: #1f1f1f
|
||||||
|
window.handle.unfocus: lat
|
||||||
|
window.handle.unfocus.color: #1f1f1f
|
||||||
|
window.handle.unfocus.colorTo: #1f1f1f
|
||||||
|
window.handleWidth: 2
|
||||||
|
|
||||||
|
window.grip.focus: flat
|
||||||
|
window.grip.focus.color: #525252
|
||||||
|
window.grip.focus.colorTo: #525252
|
||||||
|
window.grip.unfocus: flat
|
||||||
|
window.grip.unfocus.color: #1f1f1f
|
||||||
|
window.grip.unfocus.colorTo: #1f1f1f
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# EOF
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
session.menuFile: ~/.fluxbox/menu
|
||||||
|
session.keyFile: ~/.fluxbox/keys
|
||||||
|
session.styleFile: /usr/share/fluxbox/styles/aerokube
|
||||||
|
session.configVersion: 13
|
||||||
|
session.screen0.strftimeFormat: %d %b, %a %02k:%M:%S
|
||||||
|
session.screen0.toolbar.visible: false
|
||||||
|
session.screen0.toolbar.tools:
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module xseld
|
||||||
|
|
||||||
|
go 1.17
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
cmd := exec.Command("xsel", "-b")
|
||||||
|
switch r.Method {
|
||||||
|
case http.MethodGet:
|
||||||
|
cmd.Args = append(cmd.Args, "-o")
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go io.Copy(w, stdout)
|
||||||
|
case http.MethodPost:
|
||||||
|
cmd.Args = append(cmd.Args, "-i")
|
||||||
|
stdin, err := cmd.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
defer stdin.Close()
|
||||||
|
io.Copy(stdin, r.Body)
|
||||||
|
}()
|
||||||
|
default:
|
||||||
|
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
log.Fatal(http.ListenAndServe(":9090", nil))
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
FROM golang:1.17 as go
|
||||||
|
|
||||||
|
COPY choose /choose
|
||||||
|
|
||||||
|
COPY rproxy /rproxy
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y upx-ucl && \
|
||||||
|
cd /choose && \
|
||||||
|
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" && \
|
||||||
|
upx /choose/choose && \
|
||||||
|
cd /rproxy && \
|
||||||
|
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" && \
|
||||||
|
upx /rproxy/rproxy
|
||||||
|
|
||||||
|
FROM ubuntu:18.04
|
||||||
|
|
||||||
|
COPY --from=go /choose/choose /usr/local/bin/
|
||||||
|
COPY --from=go /rproxy/rproxy /usr/local/bin/
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt update && \
|
||||||
|
apt -y install xvfb x11-utils x11vnc tigervnc-common tigervnc-viewer
|
||||||
|
|
||||||
|
COPY entrypoint.sh /
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#browsers.json
|
||||||
|
|
||||||
|
```
|
||||||
|
"MicrosoftEdge": {
|
||||||
|
"default": "77",
|
||||||
|
"versions": {
|
||||||
|
"77": {
|
||||||
|
"image": "selenoid/external-host:1.0.0",
|
||||||
|
"port": "4444",
|
||||||
|
"path": "/",
|
||||||
|
"env": ["URLS=[\"http://172.17.0.1:4444/\"]","VNC_PASSWORD=longpassword","SCREEN_RESOLUTION=1921x1080x24"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
"fmt"
|
||||||
|
"encoding/json"
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
s rand.Source
|
||||||
|
r *rand.Rand
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
s = rand.NewSource(time.Now().Unix())
|
||||||
|
r = rand.New(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var hosts []string
|
||||||
|
if err := json.Unmarshal([]byte(os.Getenv("URLS")), &hosts); err != nil {
|
||||||
|
fmt.Printf("unmarshal json: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if l := len(hosts); l > 0 {
|
||||||
|
fmt.Println(hosts[r.Intn(l)])
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+51
@@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
SCREEN_RESOLUTION=${SCREEN_RESOLUTION:-"1920x1080x24"}
|
||||||
|
DISPLAY_NUM=99
|
||||||
|
URLS=${URLS:-'["http://172.17.0.1:4444/"]'}
|
||||||
|
VNC_PASSWORD=${VNC_PASSWORD:-"password"}
|
||||||
|
export URLS
|
||||||
|
export VNC_PASSWORD
|
||||||
|
export DISPLAY=":$DISPLAY_NUM"
|
||||||
|
|
||||||
|
clean() {
|
||||||
|
if [ -n "$XVFB_PID" ]; then
|
||||||
|
kill -TERM "$XVFB_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$X11VNC_PID" ]; then
|
||||||
|
kill -TERM "$X11VNC_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$RPROXY_PID" ]; then
|
||||||
|
kill -TERM "$RPROXY_PID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
trap clean SIGINT SIGTERM
|
||||||
|
|
||||||
|
URL=$(choose $URL)
|
||||||
|
HOST=$(echo $URL | sed -e 's@\(.*\)://@@' -e 's@[:/]\(.*\)$@@')
|
||||||
|
|
||||||
|
/bin/bash -c 'echo -e "$VNC_PASSWORD\n$VNC_PASSWORD\nn\n" | vncpasswd file.passwd'>/dev/null
|
||||||
|
|
||||||
|
xvfb-run -l -n $DISPLAY_NUM -s "-ac -screen 0 $SCREEN_RESOLUTION -noreset -listen tcp" \
|
||||||
|
vncviewer passwd=file.passwd $HOST &
|
||||||
|
XVFB_PID=$!
|
||||||
|
|
||||||
|
retcode=1
|
||||||
|
until [ $retcode -eq 0 ]; do
|
||||||
|
xdpyinfo -display $DISPLAY >/dev/null 2>&1
|
||||||
|
retcode=$?
|
||||||
|
if [ $retcode -ne 0 ]; then
|
||||||
|
echo Waiting xvfb...
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
x11vnc -display $DISPLAY -passwd selenoid -shared -forever -loop500 -rfbport 5900 -rfbportv6 5900 -logfile /dev/null &
|
||||||
|
X11VNC_PID=$!
|
||||||
|
|
||||||
|
rproxy $URL &
|
||||||
|
RPROXY_PID=$!
|
||||||
|
|
||||||
|
wait
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
u, err := url.Parse(os.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("parse url: %v", err)
|
||||||
|
}
|
||||||
|
log.Fatal(http.ListenAndServe(":4444", httputil.NewSingleHostReverseProxy(u)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
FROM browsers/base:7.3.6
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
curl -s https://deb.opera.com/archive.key | apt-key add - && \
|
||||||
|
echo 'deb https://deb.opera.com/opera/ stable non-free' >> /etc/apt/sources.list.d/opera.list && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get -y install opera=12.16.1860 openjdk-8-jre-headless && \
|
||||||
|
rm -Rf /tmp/* && rm -Rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY selenium-server-standalone.jar /usr/share/selenium/
|
||||||
|
COPY entrypoint.sh /
|
||||||
|
|
||||||
|
USER selenium
|
||||||
|
|
||||||
|
EXPOSE 4444
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
Executable
+54
@@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
SCREEN_RESOLUTION=${SCREEN_RESOLUTION:-"1920x1080x24"}
|
||||||
|
ENABLE_WINDOW_MANAGER=${ENABLE_WINDOW_MANAGER:-""}
|
||||||
|
DISPLAY_NUM=99
|
||||||
|
export DISPLAY=":$DISPLAY_NUM"
|
||||||
|
|
||||||
|
clean() {
|
||||||
|
if [ -n "$FILESERVER_PID" ]; then
|
||||||
|
kill -TERM "$FILESERVER_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$XSELD_PID" ]; then
|
||||||
|
kill -TERM "$XSELD_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$XVFB_PID" ]; then
|
||||||
|
kill -TERM "$XVFB_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$SELENIUM_PID" ]; then
|
||||||
|
kill -TERM "$SELENIUM_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$X11VNC_PID" ]; then
|
||||||
|
kill -TERM "$X11VNC_PID"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
trap clean SIGINT SIGTERM
|
||||||
|
|
||||||
|
/usr/bin/fileserver &
|
||||||
|
FILESERVER_PID=$!
|
||||||
|
|
||||||
|
DISPLAY="$DISPLAY" /usr/bin/xseld &
|
||||||
|
XSELD_PID=$!
|
||||||
|
|
||||||
|
/usr/bin/xvfb-run -l -n "$DISPLAY_NUM" -s "-ac -screen 0 $SCREEN_RESOLUTION -noreset -listen tcp" /usr/bin/fluxbox -display "$DISPLAY" -log /tmp/fluxbox.log 2>/dev/null &
|
||||||
|
XVFB_PID=$!
|
||||||
|
|
||||||
|
retcode=1
|
||||||
|
until [ $retcode -eq 0 ]; do
|
||||||
|
DISPLAY="$DISPLAY" wmctrl -m >/dev/null 2>&1
|
||||||
|
retcode=$?
|
||||||
|
if [ $retcode -ne 0 ]; then
|
||||||
|
echo Waiting X server...
|
||||||
|
sleep 0.1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$ENABLE_VNC" == "true" ]; then
|
||||||
|
x11vnc -display "$DISPLAY" -passwd selenoid -shared -forever -loop500 -rfbport 5900 -rfbportv6 5900 -logfile /tmp/x11vnc.log &
|
||||||
|
X11VNC_PID=$!
|
||||||
|
fi
|
||||||
|
|
||||||
|
/usr/bin/java -Xmx256m -Djava.security.egd=file:/dev/./urandom -jar /usr/share/selenium/selenium-server-standalone.jar -port 4444 -browserTimeout 120 &
|
||||||
|
SELENIUM_PID=$!
|
||||||
|
|
||||||
|
wait
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
FROM wernight/phantomjs:2.1.1
|
||||||
|
EXPOSE 4444
|
||||||
|
CMD ["/usr/local/bin/phantomjs", "--webdriver=4444"]
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Rebuilding Debian Packages
|
||||||
|
|
||||||
|
1. Download package from APT-repository:
|
||||||
|
```
|
||||||
|
$ apt-get download google-chrome-stable=48.0.2564.109-1+suffix0
|
||||||
|
```
|
||||||
|
2. Rebuild package to new owner using script:
|
||||||
|
```
|
||||||
|
$ rebuild-deb.sh -f google-chrome-stable=48.0.2564.109-1+suffix0_amd64.deb -r 'suffix0' -a 'aerokube0'
|
||||||
|
```
|
||||||
Executable
+94
@@ -0,0 +1,94 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
usage()
|
||||||
|
{
|
||||||
|
cat << EOF
|
||||||
|
usage: $0 options
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-h Show this message
|
||||||
|
-f Filename
|
||||||
|
-r Remove version postfix
|
||||||
|
-a Add version postfix
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
filename=''
|
||||||
|
remove_postfix=''
|
||||||
|
add_postfix=''
|
||||||
|
|
||||||
|
while getopts 'f:r:a:' OPTION
|
||||||
|
do
|
||||||
|
case $OPTION in
|
||||||
|
h)
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
f)
|
||||||
|
filename=$OPTARG
|
||||||
|
;;
|
||||||
|
r)
|
||||||
|
remove_postfix=$OPTARG
|
||||||
|
;;
|
||||||
|
a)
|
||||||
|
add_postfix=$OPTARG
|
||||||
|
;;
|
||||||
|
?)
|
||||||
|
usage
|
||||||
|
exit
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$filename" ]; then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
set -x
|
||||||
|
|
||||||
|
cp "$filename" .
|
||||||
|
spackage=$(echo "$filename" | awk -F '/' '{print $NF}')
|
||||||
|
package=$(echo "$spackage" | awk -F '_' '{print $1}')
|
||||||
|
version=$(echo "$spackage" | awk -F '_' '{print $2}')
|
||||||
|
rm -Rf "$package" control changelog
|
||||||
|
|
||||||
|
# Set variables
|
||||||
|
export DH_ALWAYS_EXCLUDE="CVS:.svn:.git"
|
||||||
|
|
||||||
|
# Extract package content
|
||||||
|
dpkg-deb -x $spackage $package/
|
||||||
|
|
||||||
|
# Extract package control
|
||||||
|
dpkg-deb -e $spackage $package/DEBIAN
|
||||||
|
|
||||||
|
# Extract package changelog
|
||||||
|
changelog=$(find $package/usr/share/doc -name 'changelog*.gz' 2>/dev/null| head -n 1)
|
||||||
|
if [ -n "$changelog" -a -f "$changelog" ]; then
|
||||||
|
rm -f "$changelog"
|
||||||
|
fi
|
||||||
|
old_version=$version
|
||||||
|
if [ -n "$remove_postfix" ]; then
|
||||||
|
version=$(echo $version | sed -e "s|+$remove_postfix||g")
|
||||||
|
old_version=$version"+"$remove_postfix
|
||||||
|
fi
|
||||||
|
new_version=$version
|
||||||
|
if [ -n "$add_postfix" ]; then
|
||||||
|
new_version=$new_version"+"$add_postfix
|
||||||
|
fi
|
||||||
|
dch --create --force-distribution --distribution unstable --package "$package" --newversion "$new_version" -c changelog 'Package rebuilt'
|
||||||
|
|
||||||
|
# Fix control
|
||||||
|
section=$(awk '/Section:/ {print $NF}' $package/DEBIAN/control 2>/dev/null || echo 'misc')
|
||||||
|
priority=$(awk '/Priority:/ {print $NF}' $package/DEBIAN/control 2>/dev/null || echo 'extra')
|
||||||
|
echo "$(echo $spackage | sed "s/$old_version/$new_version/") $section $priority" > files
|
||||||
|
echo "Source: $package" > control
|
||||||
|
grep -iv "Source:" $package/DEBIAN/control >> control
|
||||||
|
cp control $package/DEBIAN/control
|
||||||
|
|
||||||
|
# Build package
|
||||||
|
sed -i "s/$old_version/$new_version/g" $package/DEBIAN/control
|
||||||
|
dpkg-deb -b $package/ ./
|
||||||
|
|
||||||
|
# Remove source package
|
||||||
|
rm -f "./$spackage"
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# Contributor: Anton Dzyk <anton.dzyk@yandex.ru>
|
||||||
|
# Maintainer: Ivan Krutov <vania-pooh@aerokube.com>
|
||||||
|
pkgname=ffmpeg-x11grab
|
||||||
|
pkgsource=ffmpeg
|
||||||
|
pkgver=4.3
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="FFMPEG version optimized for x11grab to x264"
|
||||||
|
url="https://ffmpeg.org"
|
||||||
|
arch="all"
|
||||||
|
license="LGPL-2.1-or-later GPL-2.0-or-later"
|
||||||
|
makedepends="gnutls-dev lame-dev libvorbis-dev xvidcore-dev zlib-dev libvdpau-dev
|
||||||
|
imlib2-dev x264-dev libtheora-dev coreutils bzip2-dev perl-dev libvpx-dev
|
||||||
|
libvpx-dev sdl2-dev libxfixes-dev libva-dev v4l-utils-dev yasm opus-dev libass-dev pulseaudio-dev"
|
||||||
|
source="https://ffmpeg.org/releases/ffmpeg-$pkgver.tar.xz
|
||||||
|
0001-libavutil-clean-up-unused-FF_SYMVER-macro.patch
|
||||||
|
"
|
||||||
|
builddir="$srcdir/$pkgsource-$pkgver"
|
||||||
|
|
||||||
|
build() {
|
||||||
|
local _dbg="--disable-debug"
|
||||||
|
local _asm=""
|
||||||
|
[ -n "$DEBUG" ] && _dbg="--enable-debug"
|
||||||
|
|
||||||
|
case "$CARCH" in
|
||||||
|
x86 | arm*) _asm="--disable-asm" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
cd "$builddir"
|
||||||
|
./configure \
|
||||||
|
--prefix=/usr \
|
||||||
|
--enable-avfilter \
|
||||||
|
--enable-gpl \
|
||||||
|
--enable-libx264 \
|
||||||
|
--enable-libpulse \
|
||||||
|
--enable-static \
|
||||||
|
--enable-small \
|
||||||
|
--disable-ffplay \
|
||||||
|
--disable-ffprobe \
|
||||||
|
--disable-doc \
|
||||||
|
--disable-htmlpages \
|
||||||
|
--disable-manpages \
|
||||||
|
--disable-podpages \
|
||||||
|
--disable-txtpages \
|
||||||
|
--disable-w32threads \
|
||||||
|
--disable-alsa \
|
||||||
|
--disable-audiotoolbox \
|
||||||
|
--disable-cuda \
|
||||||
|
--disable-cuvid \
|
||||||
|
--disable-d3d11va \
|
||||||
|
--disable-dxva2 \
|
||||||
|
--disable-nvenc \
|
||||||
|
--disable-vaapi \
|
||||||
|
--disable-vdpau \
|
||||||
|
--disable-videotoolbox \
|
||||||
|
--disable-librtmp \
|
||||||
|
--disable-devices \
|
||||||
|
--enable-indev=xcbgrab \
|
||||||
|
--enable-indev=pulse \
|
||||||
|
$_asm $_dbg
|
||||||
|
make
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "$builddir"
|
||||||
|
make DESTDIR="$pkgdir" install
|
||||||
|
}
|
||||||
|
|
||||||
|
libs() {
|
||||||
|
pkgdesc="Libraries for ffmpeg"
|
||||||
|
replaces="ffmpeg"
|
||||||
|
mkdir -p "$subpkgdir"/usr
|
||||||
|
mv "$pkgdir"/usr/lib "$subpkgdir"/usr
|
||||||
|
}
|
||||||
|
|
||||||
|
sha512sums="f031eb6c4423887af323ab7d1f431234d4e30993a52db45dccf427b41eb442a3bd020dcbc13e83cbf813fad0f36c849cb651203570148387c864507aa19f313a ffmpeg-4.3.tar.xz
|
||||||
|
1047a23eda51b576ac200d5106a1cd318d1d5291643b3a69e025c0a7b6f3dbc9f6eb0e1e6faa231b7e38c8dd4e49a54f7431f87a93664da35825cc2e9e8aedf4 0001-libavutil-clean-up-unused-FF_SYMVER-macro.patch"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
FROM alpine:3.12
|
||||||
|
|
||||||
|
RUN apk add -U alpine-sdk && adduser -g "" -D -G abuild selenoid
|
||||||
|
USER selenoid
|
||||||
|
RUN git clone git://git.alpinelinux.org/aports /home/selenoid/aports
|
||||||
|
ADD APKBUILD /home/selenoid/aports/community/ffmpeg/
|
||||||
|
RUN cd /home/selenoid/aports/community/ffmpeg/ && \
|
||||||
|
abuild checksum && \
|
||||||
|
abuild unpack && \
|
||||||
|
abuild deps && \
|
||||||
|
abuild build
|
||||||
|
USER root
|
||||||
|
|
||||||
|
FROM alpine:3.12
|
||||||
|
|
||||||
|
RUN apk add -U x264-libs sdl2 libxcb libbz2 xset pulseaudio-utils && \
|
||||||
|
rm -rf /var/cache/apk/*
|
||||||
|
COPY --from=0 /home/selenoid/aports/community/ffmpeg/src/ffmpeg-4.3/ffmpeg /usr/bin/ffmpeg
|
||||||
|
|
||||||
|
COPY entrypoint.sh /
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
VIDEO_SIZE=${VIDEO_SIZE:-"1920x1080"}
|
||||||
|
BROWSER_CONTAINER_NAME=${BROWSER_CONTAINER_NAME:-"browser"}
|
||||||
|
DISPLAY=${DISPLAY:-"99"}
|
||||||
|
FILE_NAME=${FILE_NAME:-"video.mp4"}
|
||||||
|
FRAME_RATE=${FRAME_RATE:-"12"}
|
||||||
|
CODEC=${CODEC:-"libx264"}
|
||||||
|
PRESET=${PRESET:-""}
|
||||||
|
if [ "$CODEC" == "libx264" -a -n "$PRESET" ]; then
|
||||||
|
PRESET="-preset $PRESET"
|
||||||
|
fi
|
||||||
|
INPUT_OPTIONS=${INPUT_OPTIONS:-""}
|
||||||
|
HIDE_CURSOR=${HIDE_CURSOR:-""}
|
||||||
|
if [ -n "$HIDE_CURSOR" ]; then
|
||||||
|
INPUT_OPTIONS="$INPUT_OPTIONS -draw_mouse 0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
retcode=1
|
||||||
|
max_attempts=300
|
||||||
|
attempts=0
|
||||||
|
echo 'Waiting for display to open...'
|
||||||
|
until [ $retcode -eq 0 -o $attempts -eq $max_attempts ]; do
|
||||||
|
xset -display ${BROWSER_CONTAINER_NAME}:${DISPLAY} b off > /dev/null 2>&1
|
||||||
|
retcode=$?
|
||||||
|
if [ $retcode -ne 0 ]; then
|
||||||
|
echo 'Sleeping before next attempt...'
|
||||||
|
sleep 0.1
|
||||||
|
fi
|
||||||
|
attempts=$((attempts+1))
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p ~/.config/pulse
|
||||||
|
|
||||||
|
echo -n 'gIvST5iz2S0J1+JlXC1lD3HWvg61vDTV1xbmiGxZnjB6E3psXsjWUVQS4SRrch6rygQgtpw7qmghDFTaekt8qWiCjGvB0LNzQbvhfs1SFYDMakmIXuoqYoWFqTJ+GOXYByxpgCMylMKwpOoANEDePUCj36nwGaJNTNSjL8WBv+Bf3rJXqWnJ/43a0hUhmBBt28Dhiz6Yqowa83Y4iDRNJbxih6rB1vRNDKqRr/J9XJV+dOlM0dI+K6Vf5Ag+2LGZ3rc5sPVqgHgKK0mcNcsn+yCmO+XLQHD1K+QgL8RITs7nNeF1ikYPVgEYnc0CGzHTMvFR7JLgwL2gTXulCdwPbg=='| base64 -d>~/.config/pulse/cookie
|
||||||
|
|
||||||
|
export PULSE_SERVER=${BROWSER_CONTAINER_NAME}
|
||||||
|
|
||||||
|
if pactl info >/dev/null 2>&1; then
|
||||||
|
exec ffmpeg -f pulse -i default -y -f x11grab -video_size ${VIDEO_SIZE} -r ${FRAME_RATE} ${INPUT_OPTIONS} -i ${BROWSER_CONTAINER_NAME}:${DISPLAY} -codec:v ${CODEC} ${PRESET} -pix_fmt yuv420p -filter:v "pad=ceil(iw/2)*2:ceil(ih/2)*2" "/data/$FILE_NAME"
|
||||||
|
else
|
||||||
|
exec ffmpeg -y -f x11grab -video_size ${VIDEO_SIZE} -r ${FRAME_RATE} ${INPUT_OPTIONS} -i ${BROWSER_CONTAINER_NAME}:${DISPLAY} -codec:v ${CODEC} ${PRESET} -pix_fmt yuv420p -filter:v "pad=ceil(iw/2)*2:ceil(ih/2)*2" "/data/$FILE_NAME"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
ARG VERSION
|
||||||
|
FROM golang:1.17 as go
|
||||||
|
|
||||||
|
COPY devtools /devtools
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y upx-ucl && \
|
||||||
|
cd /devtools && \
|
||||||
|
go test -race && \
|
||||||
|
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" && \
|
||||||
|
upx /devtools/devtools
|
||||||
|
|
||||||
|
FROM selenoid/dev_chrome:$VERSION
|
||||||
|
|
||||||
|
ENV DBUS_SESSION_BUS_ADDRESS=/dev/null
|
||||||
|
COPY --from=go /devtools/devtools /usr/bin/
|
||||||
|
COPY chromedriver /usr/bin/
|
||||||
|
COPY entrypoint.sh /
|
||||||
|
|
||||||
|
RUN chmod +x /usr/bin/chromedriver
|
||||||
|
USER selenium
|
||||||
|
|
||||||
|
EXPOSE 4444
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
FROM browsers/base:7.3.6
|
||||||
|
|
||||||
|
ARG VERSION
|
||||||
|
ARG PACKAGE=google-chrome-stable
|
||||||
|
ARG INSTALL_DIR=chrome
|
||||||
|
|
||||||
|
LABEL browser=$PACKAGE:$VERSION
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
curl -s https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
|
||||||
|
echo 'deb [arch=amd64] https://dl.google.com/linux/chrome/deb/ stable main' > /etc/apt/sources.list.d/google.list && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get -y --no-install-recommends install iproute2 $PACKAGE=$VERSION && \
|
||||||
|
sed -i -e 's@exec -a "$0" "$HERE/chrome"@& --no-sandbox --disable-gpu@' /opt/google/$INSTALL_DIR/google-chrome && \
|
||||||
|
chown root:root /opt/google/$INSTALL_DIR/chrome-sandbox && \
|
||||||
|
chmod 4755 /opt/google/$INSTALL_DIR/chrome-sandbox && \
|
||||||
|
google-chrome --version && \
|
||||||
|
rm -Rf /tmp/* && rm -Rf /var/lib/apt/lists/*
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
devtools
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/mafredri/cdp/devtool"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
devtoolsBaseDir = "/tmp"
|
||||||
|
slash = "/"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultDevtoolsHost = "127.0.0.1:9222"
|
||||||
|
)
|
||||||
|
|
||||||
|
func root() http.Handler {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/browser", browser)
|
||||||
|
mux.HandleFunc("/json/protocol", protocol)
|
||||||
|
mux.HandleFunc("/page", page)
|
||||||
|
mux.HandleFunc("/page/", page)
|
||||||
|
mux.HandleFunc("/", browser)
|
||||||
|
return mux
|
||||||
|
}
|
||||||
|
|
||||||
|
func browser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
u, err := getBrowserWebSocketUrl()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[BROWSER_URL_ERROR] [%v]", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("[BROWSER] [%s]", u.String())
|
||||||
|
proxyWebSocket(w, r, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func proxyWebSocket(w http.ResponseWriter, r *http.Request, u *url.URL) {
|
||||||
|
u.Scheme = "http"
|
||||||
|
(&httputil.ReverseProxy{
|
||||||
|
Director: func(r *http.Request) {
|
||||||
|
r.Host = "localhost"
|
||||||
|
r.URL = u
|
||||||
|
},
|
||||||
|
}).ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func page(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fragments := strings.Split(r.URL.Path, slash)
|
||||||
|
targetId := ""
|
||||||
|
if len(fragments) == 3 {
|
||||||
|
targetId = fragments[2]
|
||||||
|
}
|
||||||
|
u, err := getPageWebSocketUrl(targetId)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[PAGE_URL_ERROR] [%v]", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("[PAGE] [%s]", u.String())
|
||||||
|
proxyWebSocket(w, r, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func protocol(w http.ResponseWriter, r *http.Request) {
|
||||||
|
h, err := devtoolsHost()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[DEVTOOLS_HOST_ERROR] [%v]", err)
|
||||||
|
http.Error(w, fmt.Sprintf("Failed to detect devtools host: %v", err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := &url.URL{
|
||||||
|
Host: h,
|
||||||
|
Scheme: "http",
|
||||||
|
Path: "/json/protocol",
|
||||||
|
}
|
||||||
|
log.Printf("[PROTOCOL] [%s]", u.String())
|
||||||
|
(&httputil.ReverseProxy{
|
||||||
|
Director: func(r *http.Request) {
|
||||||
|
r.Host = "localhost"
|
||||||
|
r.URL = u
|
||||||
|
},
|
||||||
|
}).ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBrowserWebSocketUrl() (*url.URL, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
h, err := devtoolsHost()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to detect devtools port: %v", err)
|
||||||
|
}
|
||||||
|
dt := devtool.New(fmt.Sprintf("http://%s", h))
|
||||||
|
ver, err := dt.Version(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get browser websocket url: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wsUrl, err := url.Parse(ver.WebSocketDebuggerURL)
|
||||||
|
if err == nil {
|
||||||
|
return wsUrl, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("browser websocket URL information not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPageWebSocketUrl(targetId string) (*url.URL, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
h, err := devtoolsHost()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to detect devtools port: %v", err)
|
||||||
|
}
|
||||||
|
dt := devtool.New(fmt.Sprintf("http://%s", h))
|
||||||
|
targets, err := dt.List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to list targets: %v", err)
|
||||||
|
}
|
||||||
|
for _, t := range targets {
|
||||||
|
if (targetId == "" && t.Type == devtool.Page) || targetId == t.ID {
|
||||||
|
wsUrl, err := url.Parse(t.WebSocketDebuggerURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid websocket URL for matched target %s: %v", t.ID, err)
|
||||||
|
}
|
||||||
|
return wsUrl, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errors.New("no matching target found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func devtoolsHost() (string, error) {
|
||||||
|
if android {
|
||||||
|
return androidDevtoolsHost()
|
||||||
|
}
|
||||||
|
return detectDevtoolsHost(devtoolsBaseDir), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func androidDevtoolsHost() (string, error) {
|
||||||
|
const androidDevtoolsPort = 9333
|
||||||
|
cmd := exec.Command("adb", "forward", fmt.Sprintf("tcp:%d", androidDevtoolsPort), "localabstract:chrome_devtools_remote")
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to forward devtools port: %v", err)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("localhost:%d", androidDevtoolsPort), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectDevtoolsHost(baseDir string) string {
|
||||||
|
var candidates []string
|
||||||
|
for _, glob := range []string{".com.google.Chrome*", ".org.chromium.Chromium*"} {
|
||||||
|
cds, err := filepath.Glob(filepath.Join(baseDir, glob))
|
||||||
|
if err == nil {
|
||||||
|
candidates = append(candidates, cds...)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
for _, c := range candidates {
|
||||||
|
f, err := os.Stat(c)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !f.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
portFile := filepath.Join(c, "DevToolsActivePort")
|
||||||
|
data, err := ioutil.ReadFile(portFile)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(data), "\n")
|
||||||
|
if len(lines) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
port, err := strconv.Atoi(lines[0])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("127.0.0.1:%d", port)
|
||||||
|
}
|
||||||
|
return defaultDevtoolsHost
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
. "github.com/aandryashin/matchers"
|
||||||
|
. "github.com/aandryashin/matchers/httpresp"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/mafredri/cdp"
|
||||||
|
"github.com/mafredri/cdp/protocol/target"
|
||||||
|
"github.com/mafredri/cdp/rpcc"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
srv *httptest.Server
|
||||||
|
devtoolsSrv *httptest.Server
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
srv = httptest.NewServer(root())
|
||||||
|
listen = srv.Listener.Addr().String()
|
||||||
|
devtoolsSrv = httptest.NewServer(mockDevtoolsMux())
|
||||||
|
defaultDevtoolsHost = devtoolsSrv.Listener.Addr().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockDevtoolsMux() http.Handler {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
version := func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.Write([]byte(fmt.Sprintf(`{
|
||||||
|
"Browser": "Chrome/72.0.3601.0",
|
||||||
|
"Protocol-Version": "1.3",
|
||||||
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3601.0 Safari/537.36",
|
||||||
|
"V8-Version": "7.2.233",
|
||||||
|
"WebKit-Version": "537.36 (@cfede9db1d154de0468cb0538479f34c0755a0f4)",
|
||||||
|
"webSocketDebuggerUrl": "ws://%s/devtools/browser/b0b8a4fb-bb17-4359-9533-a8d9f3908bd8"
|
||||||
|
}`, defaultDevtoolsHost)))
|
||||||
|
}
|
||||||
|
mux.HandleFunc("/json/version", version)
|
||||||
|
listTargets := func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.Write([]byte(fmt.Sprintf(`[ {
|
||||||
|
"description": "",
|
||||||
|
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/one",
|
||||||
|
"id": "one",
|
||||||
|
"title": "Aerokube",
|
||||||
|
"type": "page",
|
||||||
|
"url": "https://www.aerokube.com/",
|
||||||
|
"webSocketDebuggerUrl": "ws://%s/devtools/page/one"
|
||||||
|
}, {
|
||||||
|
"description": "",
|
||||||
|
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/two",
|
||||||
|
"id": "two",
|
||||||
|
"title": "Aerokube Selenoid",
|
||||||
|
"type": "page",
|
||||||
|
"url": "https://selenoid.aerokube.com/",
|
||||||
|
"webSocketDebuggerUrl": "ws://%s/devtools/page/two"
|
||||||
|
} ]`, defaultDevtoolsHost, defaultDevtoolsHost)))
|
||||||
|
|
||||||
|
}
|
||||||
|
mux.HandleFunc("/json/list", listTargets)
|
||||||
|
mux.HandleFunc("/json", listTargets)
|
||||||
|
|
||||||
|
mux.HandleFunc("/json/protocol", func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.Write([]byte("{}"))
|
||||||
|
})
|
||||||
|
|
||||||
|
upgrader := websocket.Upgrader{
|
||||||
|
CheckOrigin: func(_ *http.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
devtoolsEcho := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
//Echo request ID but omit Method
|
||||||
|
c, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
for {
|
||||||
|
mt, message, err := c.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var req rpcc.Request
|
||||||
|
err = json.Unmarshal(message, &req)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
resp := rpcc.Response{
|
||||||
|
ID: req.ID,
|
||||||
|
}
|
||||||
|
if req.Method == "Target.getTargets" {
|
||||||
|
result := target.GetTargetsReply{
|
||||||
|
TargetInfos: []target.Info{
|
||||||
|
{TargetID: "one", Type: "page"},
|
||||||
|
{TargetID: "two", Type: "page"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resBytes, _ := json.Marshal(result)
|
||||||
|
resp.Result = json.RawMessage(resBytes)
|
||||||
|
}
|
||||||
|
output, err := json.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = c.WriteMessage(mt, output)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/devtools/browser/", devtoolsEcho)
|
||||||
|
mux.HandleFunc("/devtools/page/", devtoolsEcho)
|
||||||
|
return mux
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProtocol(t *testing.T) {
|
||||||
|
u := fmt.Sprintf("http://%s/json/protocol", srv.Listener.Addr().String())
|
||||||
|
resp, err := http.Get(u)
|
||||||
|
AssertThat(t, err, Is{nil})
|
||||||
|
AssertThat(t, resp, Code{200})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevtools(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
browserWs := fmt.Sprintf("ws://%s/", srv.Listener.Addr().String())
|
||||||
|
browserConn, err := rpcc.DialContext(ctx, browserWs)
|
||||||
|
AssertThat(t, err, Is{nil})
|
||||||
|
defer browserConn.Close()
|
||||||
|
|
||||||
|
browserClient := cdp.NewClient(browserConn)
|
||||||
|
targets, err := browserClient.Target.GetTargets(ctx)
|
||||||
|
AssertThat(t, err, Is{nil})
|
||||||
|
AssertThat(t, len(targets.TargetInfos), EqualTo{2})
|
||||||
|
|
||||||
|
defaultPageWs := fmt.Sprintf("ws://%s/page", srv.Listener.Addr().String())
|
||||||
|
defaultPageConn, err := rpcc.DialContext(ctx, defaultPageWs)
|
||||||
|
AssertThat(t, err, Is{nil})
|
||||||
|
defer defaultPageConn.Close()
|
||||||
|
defaultPageClient := cdp.NewClient(defaultPageConn)
|
||||||
|
err = defaultPageClient.Page.Enable(ctx)
|
||||||
|
AssertThat(t, err, Is{nil})
|
||||||
|
|
||||||
|
selectedPageWs := fmt.Sprintf("ws://%s/page/%s", srv.Listener.Addr().String(), targets.TargetInfos[1].TargetID)
|
||||||
|
selectedPageConn, err := rpcc.DialContext(ctx, selectedPageWs)
|
||||||
|
AssertThat(t, err, Is{nil})
|
||||||
|
defer selectedPageConn.Close()
|
||||||
|
selectedPageClient := cdp.NewClient(selectedPageConn)
|
||||||
|
err = selectedPageClient.Page.Enable(ctx)
|
||||||
|
AssertThat(t, err, Is{nil})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDetectDevtoolsHost(t *testing.T) {
|
||||||
|
name, _ := ioutil.TempDir("", "devtools")
|
||||||
|
defer os.RemoveAll(name)
|
||||||
|
profilePath := filepath.Join(name, ".org.chromium.Chromium.deadbee")
|
||||||
|
os.MkdirAll(profilePath, os.ModePerm)
|
||||||
|
portFile := filepath.Join(profilePath, "DevToolsActivePort")
|
||||||
|
ioutil.WriteFile(portFile, []byte("12345\n/devtools/browser/6f37c7fe-a0a6-4346-a6e2-80c5da0687f0"), 0644)
|
||||||
|
|
||||||
|
AssertThat(t, detectDevtoolsHost(name), EqualTo{"127.0.0.1:12345"})
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
module github.com/aerokube/devtools
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/aandryashin/matchers v0.0.0-20161126170413-435295ea180e
|
||||||
|
github.com/google/go-cmp v0.2.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.4.2
|
||||||
|
github.com/mafredri/cdp v0.23.4
|
||||||
|
golang.org/x/net v0.0.0-20190107210223-45ffb0cd1ba0 // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
github.com/aandryashin/matchers v0.0.0-20161126170413-435295ea180e h1:ogUKYFNcdYUIBSLibE4+EjbTJazoHr5JsWWx21Lpn8c=
|
||||||
|
github.com/aandryashin/matchers v0.0.0-20161126170413-435295ea180e/go.mod h1:cbmYNkm9xeQlNoWEPtOUcvNok2gSD7ErMnYkRW+eHi8=
|
||||||
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/mafredri/cdp v0.23.4 h1:ffp4qq6slfCL4rFWBDeRHapkLE776gER4tX5Z3LS8CY=
|
||||||
|
github.com/mafredri/cdp v0.23.4/go.mod h1:hgdiA0yp1uqhSaDOHJWPgXpMbh+LAfUdD9vbN2AM8gE=
|
||||||
|
golang.org/x/net v0.0.0-20190107210223-45ffb0cd1ba0 h1:1DW40AJQ7AP4nY6ORUGUdkpXyEC9W2GAXcOPaMZK0K8=
|
||||||
|
golang.org/x/net v0.0.0-20190107210223-45ffb0cd1ba0/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
listen string
|
||||||
|
android bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.StringVar(&listen, "listen", ":7070", "Network address to accept connections")
|
||||||
|
flag.BoolVar(&android, "android", false, "Whether we need to forward Android Emulator devtools port")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
log.Printf("[INIT] [Listening on %s]", listen)
|
||||||
|
log.Fatal(http.ListenAndServe(listen, root()))
|
||||||
|
}
|
||||||
Executable
+93
@@ -0,0 +1,93 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
SCREEN_RESOLUTION=${SCREEN_RESOLUTION:-"1920x1080x24"}
|
||||||
|
DISPLAY_NUM=99
|
||||||
|
export DISPLAY=":$DISPLAY_NUM"
|
||||||
|
|
||||||
|
VERBOSE=${VERBOSE:-""}
|
||||||
|
DRIVER_ARGS=${DRIVER_ARGS:-""}
|
||||||
|
if [ -n "$VERBOSE" ]; then
|
||||||
|
DRIVER_ARGS="$DRIVER_ARGS --verbose"
|
||||||
|
fi
|
||||||
|
|
||||||
|
clean() {
|
||||||
|
if [ -n "$FILESERVER_PID" ]; then
|
||||||
|
kill -TERM "$FILESERVER_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$XSELD_PID" ]; then
|
||||||
|
kill -TERM "$XSELD_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$XVFB_PID" ]; then
|
||||||
|
kill -TERM "$XVFB_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$DRIVER_PID" ]; then
|
||||||
|
kill -TERM "$DRIVER_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$X11VNC_PID" ]; then
|
||||||
|
kill -TERM "$X11VNC_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$DEVTOOLS_PID" ]; then
|
||||||
|
kill -TERM "$DEVTOOLS_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$PULSE_PID" ]; then
|
||||||
|
kill -TERM "$PULSE_PID"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
trap clean SIGINT SIGTERM
|
||||||
|
|
||||||
|
if env | grep -q ROOT_CA_; then
|
||||||
|
mkdir -p $HOME/.pki/nssdb
|
||||||
|
certutil -N --empty-password -d sql:$HOME/.pki/nssdb
|
||||||
|
for e in $(env | grep ROOT_CA_ | sed -e 's/=.*$//'); do
|
||||||
|
certname=$(echo -n $e | sed -e 's/ROOT_CA_//')
|
||||||
|
echo ${!e} | base64 -d >/tmp/cert.pem
|
||||||
|
certutil -A -n ${certname} -t "TC,C,T" -i /tmp/cert.pem -d sql:$HOME/.pki/nssdb
|
||||||
|
if cat tmp/cert.pem | grep -q "PRIVATE KEY"; then
|
||||||
|
PRIVATE_KEY_PASS=${PRIVATE_KEY_PASS:-\'\'}
|
||||||
|
openssl pkcs12 -export -in /tmp/cert.pem -clcerts -nodes -out /tmp/key.p12 -passout pass:${PRIVATE_KEY_PASS} -passin pass:${PRIVATE_KEY_PASS}
|
||||||
|
pk12util -d sql:$HOME/.pki/nssdb -i /tmp/key.p12 -W ${PRIVATE_KEY_PASS}
|
||||||
|
rm /tmp/key.p12
|
||||||
|
fi
|
||||||
|
rm /tmp/cert.pem
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
/usr/bin/fileserver &
|
||||||
|
FILESERVER_PID=$!
|
||||||
|
|
||||||
|
/usr/bin/devtools &
|
||||||
|
DEVTOOLS_PID=$!
|
||||||
|
|
||||||
|
DISPLAY="$DISPLAY" /usr/bin/xseld &
|
||||||
|
XSELD_PID=$!
|
||||||
|
|
||||||
|
while ip addr | grep inet | grep -q tentative > /dev/null; do sleep 0.1; done
|
||||||
|
|
||||||
|
mkdir -p ~/pulse/.config/pulse
|
||||||
|
echo -n 'gIvST5iz2S0J1+JlXC1lD3HWvg61vDTV1xbmiGxZnjB6E3psXsjWUVQS4SRrch6rygQgtpw7qmghDFTaekt8qWiCjGvB0LNzQbvhfs1SFYDMakmIXuoqYoWFqTJ+GOXYByxpgCMylMKwpOoANEDePUCj36nwGaJNTNSjL8WBv+Bf3rJXqWnJ/43a0hUhmBBt28Dhiz6Yqowa83Y4iDRNJbxih6rB1vRNDKqRr/J9XJV+dOlM0dI+K6Vf5Ag+2LGZ3rc5sPVqgHgKK0mcNcsn+yCmO+XLQHD1K+QgL8RITs7nNeF1ikYPVgEYnc0CGzHTMvFR7JLgwL2gTXulCdwPbg=='| base64 -d>~/pulse/.config/pulse/cookie
|
||||||
|
HOME=$HOME/pulse pulseaudio --start --exit-idle-time=-1
|
||||||
|
HOME=$HOME/pulse pactl load-module module-native-protocol-tcp
|
||||||
|
PULSE_PID=$(ps --no-headers -C pulseaudio -o pid | sed -r 's/( )+//g')
|
||||||
|
|
||||||
|
/usr/bin/xvfb-run -l -n "$DISPLAY_NUM" -s "-ac -screen 0 $SCREEN_RESOLUTION -noreset -listen tcp" /usr/bin/fluxbox -display "$DISPLAY" -log /dev/null 2>/dev/null &
|
||||||
|
XVFB_PID=$!
|
||||||
|
|
||||||
|
retcode=1
|
||||||
|
until [ $retcode -eq 0 ]; do
|
||||||
|
DISPLAY="$DISPLAY" wmctrl -m >/dev/null 2>&1
|
||||||
|
retcode=$?
|
||||||
|
if [ $retcode -ne 0 ]; then
|
||||||
|
echo Waiting X server...
|
||||||
|
sleep 0.1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$ENABLE_VNC" == "true" ]; then
|
||||||
|
x11vnc -display "$DISPLAY" -passwd selenoid -shared -forever -loop500 -rfbport 5900 -rfbportv6 5900 -logfile /dev/null &
|
||||||
|
X11VNC_PID=$!
|
||||||
|
fi
|
||||||
|
|
||||||
|
DISPLAY="$DISPLAY" /usr/bin/chromedriver --port=4444 --allowed-ips='' --allowed-origins='*' ${DRIVER_ARGS} &
|
||||||
|
DRIVER_PID=$!
|
||||||
|
|
||||||
|
wait
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
FROM browsers/base:7.3.6
|
||||||
|
|
||||||
|
ARG VERSION=noop
|
||||||
|
ARG PACKAGE=google-chrome-stable
|
||||||
|
ARG INSTALL_DIR=chrome
|
||||||
|
|
||||||
|
LABEL browser=$PACKAGE:$VERSION
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get -y --no-install-recommends install gconf-service \
|
||||||
|
libasound2 \
|
||||||
|
libatk1.0-0 \
|
||||||
|
libc6 \
|
||||||
|
libcairo2 \
|
||||||
|
libcups2 \
|
||||||
|
libdbus-1-3 \
|
||||||
|
libexpat1 \
|
||||||
|
libfontconfig1 \
|
||||||
|
libfreetype6 \
|
||||||
|
libgcc1 \
|
||||||
|
libgconf-2-4 \
|
||||||
|
libgdk-pixbuf2.0-0 \
|
||||||
|
libglib2.0-0 \
|
||||||
|
libgtk2.0-0 \
|
||||||
|
libgtk-3-0 \
|
||||||
|
libnspr4 \
|
||||||
|
libnss3 \
|
||||||
|
libpango1.0-0 \
|
||||||
|
libstdc++6 \
|
||||||
|
libx11-6 \
|
||||||
|
libx11-xcb1 \
|
||||||
|
libxcb1 \
|
||||||
|
libxcomposite1 \
|
||||||
|
libxcursor1 \
|
||||||
|
libxdamage1 \
|
||||||
|
libxext6 \
|
||||||
|
libxfixes3 \
|
||||||
|
libxi6 \
|
||||||
|
libxrandr2 \
|
||||||
|
libxrender1 \
|
||||||
|
libxss1 \
|
||||||
|
libxtst6 \
|
||||||
|
ca-certificates \
|
||||||
|
fonts-liberation \
|
||||||
|
libappindicator3-1 \
|
||||||
|
libnss3 \
|
||||||
|
lsb-base \
|
||||||
|
xdg-utils \
|
||||||
|
libcurl4 \
|
||||||
|
iproute2 \
|
||||||
|
curl && \
|
||||||
|
curl -O http://host.docker.internal:8080/google-chrome.deb && \
|
||||||
|
apt-get -y purge curl && \
|
||||||
|
dpkg -i google-chrome.deb && \
|
||||||
|
sed -i -e 's@exec -a "$0" "$HERE/chrome"@& --no-sandbox --disable-gpu@' /opt/google/$INSTALL_DIR/$PACKAGE && \
|
||||||
|
rm google-chrome.deb && \
|
||||||
|
chown root:root /opt/google/$INSTALL_DIR/chrome-sandbox && \
|
||||||
|
chmod 4755 /opt/google/$INSTALL_DIR/chrome-sandbox && \
|
||||||
|
google-chrome --version && \
|
||||||
|
rm -Rf /tmp/* && \
|
||||||
|
rm -Rf /var/lib/apt/lists/*
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
ARG VERSION
|
||||||
|
FROM selenoid/dev_edge:$VERSION
|
||||||
|
|
||||||
|
ENV DBUS_SESSION_BUS_ADDRESS=/dev/null
|
||||||
|
COPY msedgedriver /usr/bin/
|
||||||
|
COPY entrypoint.sh /
|
||||||
|
|
||||||
|
RUN chmod +x /usr/bin/msedgedriver
|
||||||
|
USER selenium
|
||||||
|
|
||||||
|
EXPOSE 4444
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
FROM browsers/base:7.3.6
|
||||||
|
|
||||||
|
ARG VERSION
|
||||||
|
ARG PACKAGE=microsoft-edge-stable
|
||||||
|
ARG INSTALL_DIR=msedge
|
||||||
|
|
||||||
|
LABEL browser=$PACKAGE:$VERSION
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
curl -s https://packages.microsoft.com/keys/microsoft.asc | apt-key add - && \
|
||||||
|
echo 'deb [arch=amd64] https://packages.microsoft.com/repos/edge stable main' > /etc/apt/sources.list.d/microsoft-edge.list && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get -y --no-install-recommends install $PACKAGE=$VERSION && \
|
||||||
|
( \
|
||||||
|
sed -i -e 's@exec -a "$0" "$HERE/msedge"@& --no-sandbox --disable-gpu@' /opt/microsoft/$INSTALL_DIR/$PACKAGE || \
|
||||||
|
sed -i -e 's@exec -a "$0" "$HERE/msedge"@& --no-sandbox --disable-gpu@' /opt/microsoft/$INSTALL_DIR/microsoft-edge \
|
||||||
|
) && \
|
||||||
|
chown root:root /opt/microsoft/$INSTALL_DIR/msedge-sandbox && \
|
||||||
|
chmod 4755 /opt/microsoft/$INSTALL_DIR/msedge-sandbox && \
|
||||||
|
microsoft-edge --version && \
|
||||||
|
rm -Rf /tmp/* && rm -Rf /var/lib/apt/lists/*
|
||||||
Executable
+86
@@ -0,0 +1,86 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
SCREEN_RESOLUTION=${SCREEN_RESOLUTION:-"1920x1080x24"}
|
||||||
|
DISPLAY_NUM=99
|
||||||
|
export DISPLAY=":$DISPLAY_NUM"
|
||||||
|
|
||||||
|
VERBOSE=${VERBOSE:-""}
|
||||||
|
DRIVER_ARGS=${DRIVER_ARGS:-""}
|
||||||
|
if [ -n "$VERBOSE" ]; then
|
||||||
|
DRIVER_ARGS="$DRIVER_ARGS --verbose"
|
||||||
|
fi
|
||||||
|
|
||||||
|
clean() {
|
||||||
|
if [ -n "$FILESERVER_PID" ]; then
|
||||||
|
kill -TERM "$FILESERVER_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$XSELD_PID" ]; then
|
||||||
|
kill -TERM "$XSELD_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$PULSE_PID" ]; then
|
||||||
|
kill -TERM "$PULSE_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$XVFB_PID" ]; then
|
||||||
|
kill -TERM "$XVFB_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$DRIVER_PID" ]; then
|
||||||
|
kill -TERM "$DRIVER_PID"
|
||||||
|
fi
|
||||||
|
if [ -n "$X11VNC_PID" ]; then
|
||||||
|
kill -TERM "$X11VNC_PID"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
trap clean SIGINT SIGTERM
|
||||||
|
|
||||||
|
if env | grep -q ROOT_CA_; then
|
||||||
|
mkdir -p $HOME/.pki/nssdb
|
||||||
|
certutil -N --empty-password -d sql:$HOME/.pki/nssdb
|
||||||
|
for e in $(env | grep ROOT_CA_ | sed -e 's/=.*$//'); do
|
||||||
|
certname=$(echo -n $e | sed -e 's/ROOT_CA_//')
|
||||||
|
echo ${!e} | base64 -d >/tmp/cert.pem
|
||||||
|
certutil -A -n ${certname} -t "TC,C,T" -i /tmp/cert.pem -d sql:$HOME/.pki/nssdb
|
||||||
|
if cat tmp/cert.pem | grep -q "PRIVATE KEY"; then
|
||||||
|
PRIVATE_KEY_PASS=${PRIVATE_KEY_PASS:-\'\'}
|
||||||
|
openssl pkcs12 -export -in /tmp/cert.pem -clcerts -nodes -out /tmp/key.p12 -passout pass:${PRIVATE_KEY_PASS} -passin pass:${PRIVATE_KEY_PASS}
|
||||||
|
pk12util -d sql:$HOME/.pki/nssdb -i /tmp/key.p12 -W ${PRIVATE_KEY_PASS}
|
||||||
|
rm /tmp/key.p12
|
||||||
|
fi
|
||||||
|
rm /tmp/cert.pem
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
/usr/bin/fileserver &
|
||||||
|
FILESERVER_PID=$!
|
||||||
|
|
||||||
|
DISPLAY="$DISPLAY" /usr/bin/xseld &
|
||||||
|
XSELD_PID=$!
|
||||||
|
|
||||||
|
mkdir -p ~/pulse/.config/pulse
|
||||||
|
echo -n 'gIvST5iz2S0J1+JlXC1lD3HWvg61vDTV1xbmiGxZnjB6E3psXsjWUVQS4SRrch6rygQgtpw7qmghDFTaekt8qWiCjGvB0LNzQbvhfs1SFYDMakmIXuoqYoWFqTJ+GOXYByxpgCMylMKwpOoANEDePUCj36nwGaJNTNSjL8WBv+Bf3rJXqWnJ/43a0hUhmBBt28Dhiz6Yqowa83Y4iDRNJbxih6rB1vRNDKqRr/J9XJV+dOlM0dI+K6Vf5Ag+2LGZ3rc5sPVqgHgKK0mcNcsn+yCmO+XLQHD1K+QgL8RITs7nNeF1ikYPVgEYnc0CGzHTMvFR7JLgwL2gTXulCdwPbg=='| base64 -d>~/pulse/.config/pulse/cookie
|
||||||
|
HOME=$HOME/pulse pulseaudio --start --exit-idle-time=-1
|
||||||
|
HOME=$HOME/pulse pactl load-module module-native-protocol-tcp
|
||||||
|
PULSE_PID=$(ps --no-headers -C pulseaudio -o pid | sed -r 's/( )+//g')
|
||||||
|
|
||||||
|
/usr/bin/xvfb-run -l -n "$DISPLAY_NUM" -s "-ac -screen 0 $SCREEN_RESOLUTION -noreset -listen tcp" /usr/bin/fluxbox -display "$DISPLAY" -log /dev/null 2>/dev/null &
|
||||||
|
XVFB_PID=$!
|
||||||
|
|
||||||
|
retcode=1
|
||||||
|
until [ $retcode -eq 0 ]; do
|
||||||
|
DISPLAY="$DISPLAY" wmctrl -m >/dev/null 2>&1
|
||||||
|
retcode=$?
|
||||||
|
if [ $retcode -ne 0 ]; then
|
||||||
|
echo Waiting X server...
|
||||||
|
sleep 0.1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$ENABLE_VNC" == "true" ]; then
|
||||||
|
x11vnc -display "$DISPLAY" -passwd selenoid -shared -forever -loop500 -rfbport 5900 -rfbportv6 5900 -logfile /dev/null &
|
||||||
|
X11VNC_PID=$!
|
||||||
|
fi
|
||||||
|
|
||||||
|
DISPLAY="$DISPLAY" /usr/bin/msedgedriver --port=4444 --allowed-ips='' ${DRIVER_ARGS} &
|
||||||
|
DRIVER_PID=$!
|
||||||
|
|
||||||
|
wait
|
||||||
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
FROM browsers/base:7.3.6
|
||||||
|
|
||||||
|
ARG VERSION=noop
|
||||||
|
ARG PACKAGE=microsoft-edge-stable
|
||||||
|
ARG INSTALL_DIR=msedge
|
||||||
|
|
||||||
|
LABEL browser=$PACKAGE:$VERSION
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get -y --no-install-recommends install libatk-bridge2.0-0 \
|
||||||
|
libatomic1 \
|
||||||
|
libatspi2.0-0 \
|
||||||
|
libcairo-gobject2 \
|
||||||
|
libcolord2 \
|
||||||
|
libepoxy0 \
|
||||||
|
libgbm1 \
|
||||||
|
libgtk-3-0 \
|
||||||
|
libgtk-3-common \
|
||||||
|
liblcms2-2 \
|
||||||
|
librest-0.7-0 \
|
||||||
|
libsoup-gnome2.4-1 \
|
||||||
|
libwayland-client0 \
|
||||||
|
libwayland-cursor0 \
|
||||||
|
libwayland-egl1 \
|
||||||
|
libwayland-server0 \
|
||||||
|
libxkbcommon0 \
|
||||||
|
xdg-utils \
|
||||||
|
ca-certificates \
|
||||||
|
fonts-liberation \
|
||||||
|
libappindicator3-1 \
|
||||||
|
libnss3 \
|
||||||
|
lsb-base \
|
||||||
|
libcurl4 \
|
||||||
|
curl && \
|
||||||
|
curl -O http://host.docker.internal:8080/microsoft-edge.deb && \
|
||||||
|
apt-get -y purge curl && \
|
||||||
|
dpkg -i microsoft-edge.deb && \
|
||||||
|
( \
|
||||||
|
sed -i -e 's@exec -a "$0" "$HERE/msedge"@& --no-sandbox --disable-gpu@' /opt/microsoft/$INSTALL_DIR/$PACKAGE || \
|
||||||
|
sed -i -e 's@exec -a "$0" "$HERE/msedge"@& --no-sandbox --disable-gpu@' /opt/microsoft/$INSTALL_DIR/microsoft-edge \
|
||||||
|
) && \
|
||||||
|
rm microsoft-edge.deb && \
|
||||||
|
chown root:root /opt/microsoft/$INSTALL_DIR/msedge-sandbox && \
|
||||||
|
chmod 4755 /opt/microsoft/$INSTALL_DIR/msedge-sandbox && \
|
||||||
|
microsoft-edge --version && \
|
||||||
|
rm -Rf /tmp/* && rm -Rf /var/lib/apt/lists/*
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
FROM browsers/base:7.3.6
|
||||||
|
|
||||||
|
ARG VERSION
|
||||||
|
ARG PACKAGE=firefox
|
||||||
|
ARG PPA
|
||||||
|
|
||||||
|
LABEL browser=$PACKAGE:$VERSION
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
( [ "$PPA" != "" ] && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y software-properties-common && \
|
||||||
|
apt-get update && \
|
||||||
|
add-apt-repository -y $PPA \
|
||||||
|
) || true && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get -y --no-install-recommends install iproute2 libavcodec58 $PACKAGE=$VERSION && \
|
||||||
|
( [ "$PACKAGE" != "firefox" ] && ln /usr/bin/$PACKAGE /usr/bin/firefox ) || true && \
|
||||||
|
firefox --version && \
|
||||||
|
rm -Rf /tmp/* && rm -Rf /var/lib/apt/lists/*
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
FROM browsers/base:7.3.6
|
||||||
|
|
||||||
|
ARG VERSION=noop
|
||||||
|
ARG PACKAGE=firefox
|
||||||
|
ARG PPA=noop
|
||||||
|
|
||||||
|
LABEL browser=$PACKAGE:$VERSION
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
( \
|
||||||
|
( \
|
||||||
|
curl -O http://host.docker.internal:8080/firefox.deb && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get -y --no-install-recommends install iproute2 libavcodec58 && \
|
||||||
|
apt-get -y install libgtk-3-0 libstartup-notification0 libdbus-glib-1-2 \
|
||||||
|
) || \
|
||||||
|
( \
|
||||||
|
curl -O http://host.docker.internal:8080/firefox.deb && \
|
||||||
|
dpkg --add-architecture i386 && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get -y --no-install-recommends install iproute2 libavcodec58 && \
|
||||||
|
apt-get -y install libc6:i386 libncurses5:i386 libstdc++6:i386 libgtk-3-0:i386 libasound2:i386 libdbus-glib-1-2:i386 libxt6:i386 libatomic1:i386 libcairo-gobject2:i386 libstartup-notification0:i386 libx11-xcb1:i386 && \
|
||||||
|
mkdir flashplayer && \
|
||||||
|
cd flashplayer && \
|
||||||
|
curl -o flashplayer.tar.gz https://fpdownload.adobe.com/pub/flashplayer/pdc/32.0.0.387/flash_player_ppapi_linux.i386.tar.gz && \
|
||||||
|
tar xvzf flashplayer.tar.gz && \
|
||||||
|
cp libpepflashplayer.so /usr/lib/flashplugin-installer/libflashplayer.so && \
|
||||||
|
cd .. && \
|
||||||
|
rm -Rf flashplayer \
|
||||||
|
) \
|
||||||
|
) && \
|
||||||
|
dpkg -i firefox.deb && \
|
||||||
|
( [ "$PACKAGE" != "firefox" ] && ln /usr/bin/$PACKAGE /usr/bin/firefox ) || true && \
|
||||||
|
rm firefox.deb && \
|
||||||
|
firefox --version && \
|
||||||
|
rm -Rf /tmp/* && rm -Rf /var/lib/apt/lists/* && rm -f firefox*.deb
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
ARG VERSION
|
||||||
|
FROM selenoid/dev_firefox:$VERSION
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y openjdk-8-jre-headless && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -Rf /tmp/*
|
||||||
|
|
||||||
|
COPY selenium-server-standalone.jar /usr/share/selenium/
|
||||||
|
COPY entrypoint.sh /
|
||||||
|
|
||||||
|
USER selenium
|
||||||
|
|
||||||
|
EXPOSE 4444
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user