This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new b3371d7bc1 Add GHA to update chromedriver  (#7365)
b3371d7bc1 is described below

commit b3371d7bc112166f07af577d852a11e3693f51e8
Author: Steve Hamrick <[email protected]>
AuthorDate: Fri Mar 3 14:02:05 2023 -0700

    Add GHA to update chromedriver  (#7365)
    
    * Add GHA to update chromedriver
    
    * Update old reference
    
    * Clean up & feedback
    
    * pylint fixes
---
 .github/actions/chromedriver-updater/README.rst    |  56 ++++++
 .github/actions/chromedriver-updater/action.yml    |  24 +++
 .../chromedriver_updater/__main__.py               | 210 +++++++++++++++++++++
 .../chromedriver_updater/constants.py              |  36 ++++
 .../chromedriver_updater/templates/pr.md           |  33 ++++
 .github/actions/chromedriver-updater/entrypoint.sh |  55 ++++++
 .../actions/chromedriver-updater/requirements.txt  |  13 ++
 .github/actions/chromedriver-updater/setup.cfg     |  36 ++++
 .github/actions/chromedriver-updater/setup.py      |  20 ++
 .github/workflows/chromdriver-update.yml           |  51 +++++
 10 files changed, 534 insertions(+)

diff --git a/.github/actions/chromedriver-updater/README.rst 
b/.github/actions/chromedriver-updater/README.rst
new file mode 100644
index 0000000000..d21046dcf5
--- /dev/null
+++ b/.github/actions/chromedriver-updater/README.rst
@@ -0,0 +1,56 @@
+..
+..
+.. 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.
+..
+
+********************
+chromedriver-updater
+********************
+
+Assigns the GitHub Triage role to non-committers who have fixed 2 Issues in 
the past month.
+
+Environment Variables
+=====================
+
++----------------------------+----------------------------------------------------------------------------------+
+| Environment Variable Name  | Value                                           
                                 |
++============================+==================================================================================+
+| ``GITHUB_TOKEN``           | Required. ``${{ github.token }}`` or ``${{ 
secrets.GITHUB_TOKEN }}``             |
++----------------------------+----------------------------------------------------------------------------------+
+| ``PR_GITHUB_TOKEN``        | Required. ``${{ github.token }}`` or another 
token                               |
++----------------------------+----------------------------------------------------------------------------------+
+| ``GIT_AUTHOR_NAME``        | Optional. The username to associate with the 
commit that updates the Go version. |
++----------------------------+----------------------------------------------------------------------------------+
+
+Outputs
+=======
+
+``exit-code``
+-------------
+
+Exit code is 0 unless an error was encountered.
+
+Inputs
+======
+Optionally takes a file that contains a line for each project update of the 
form: `{project path}:{old version},{new version}`
+
+Example usage
+=============
+
+.. code-block:: yaml
+
+    - name: Update Chromedriver Versions
+      run: python3 -m chromedriver_updater
+      env:
+        GIT_AUTHOR_NAME: asf-ci
+        GITHUB_TOKEN: ${{ github.token }}
diff --git a/.github/actions/chromedriver-updater/action.yml 
b/.github/actions/chromedriver-updater/action.yml
new file mode 100644
index 0000000000..2ee47a5100
--- /dev/null
+++ b/.github/actions/chromedriver-updater/action.yml
@@ -0,0 +1,24 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+name: 'chromedriver-updater'
+description: 'Runs chromedriver updater'
+runs:
+  using: 'composite'
+  steps:
+    - run: "${{ github.action_path }}/entrypoint.sh"
+      shell: bash
diff --git 
a/.github/actions/chromedriver-updater/chromedriver_updater/__main__.py 
b/.github/actions/chromedriver-updater/chromedriver_updater/__main__.py
new file mode 100644
index 0000000000..61fc65f599
--- /dev/null
+++ b/.github/actions/chromedriver-updater/chromedriver_updater/__main__.py
@@ -0,0 +1,210 @@
+#  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.
+"""
+Utility for automatically updating chromedriver. See README.rst for more 
details.
+"""
+import os
+import sys
+from typing import Optional, NamedTuple
+
+from github.MainClass import Github
+from github.Repository import Repository
+from github.GithubException import UnknownObjectException
+from github.GitRef import GitRef
+from github.PullRequest import PullRequest
+from github.InputGitTreeElement import InputGitTreeElement
+
+try:
+       from chromedriver_updater.constants import PR_GITHUB_TOKEN, 
GITHUB_REPO, GITHUB_REF_NAME, \
+               BRANCH_NAME, GIT_AUTHOR_NAME, TRAFFIC_PORTAL, TRAFFIC_PORTAL_V2
+except ModuleNotFoundError:
+       from constants import PR_GITHUB_TOKEN, GITHUB_REPO, GITHUB_REF_NAME, 
BRANCH_NAME, \
+               GIT_AUTHOR_NAME, TRAFFIC_PORTAL, TRAFFIC_PORTAL_V2
+
+
+class UpdateEntry(NamedTuple):
+       """
+       Named tuple surrounding each line in the updates file
+       """
+       path: str
+       old_version: str
+       new_version: str
+
+       def __str__(self) -> str:
+               return f"{self.path}: {self.old_version} -> 
{self.new_version}\n"
+
+
+def parse_update_entry(entry: str) -> UpdateEntry:
+       """
+       Parses string to UpdateEntry
+       :param entry: String to parse
+       :return: Parsed string as UpdateEntry
+       """
+       if ":" not in entry:
+               print(f"Invalid update entry '{entry}', expected format 
{{path}}:{{old}},{{new}}",
+                         file=sys.stderr)
+               sys.exit(1)
+       parts = entry.split(":")
+       if len(parts) != 2 or "," not in parts[1]:
+               print(f"Invalid update entry '{entry}', expected format 
{{path}}:{{old}},{{new}}",
+                         file=sys.stderr)
+               sys.exit(1)
+       if parts[0].endswith("/"):
+               project = parts[0][:-1]
+       else:
+               project = parts[0]
+       parts = parts[1].split(",")
+       if len(parts) != 2:
+               print(f"Invalid update entry '{entry}', expected format 
{{path}}:{{old}},{{new}}",
+                         file=sys.stderr)
+               sys.exit(1)
+
+       return UpdateEntry(path=project, old_version=parts[0], 
new_version=parts[1])
+
+
+def path_to_project(path: str) -> Optional[str]:
+       """
+       Converts paths to projects
+       :param path: Path to check
+       :return: The project string, or None
+       """
+       if "traffic_portal" in path:
+               return TRAFFIC_PORTAL
+       if "traffic-portal" in path:
+               return TRAFFIC_PORTAL_V2
+       return None
+
+
+class PRCreator(Github):
+       """
+       Creates the PR to update chromedriver
+       """
+       repo: Repository
+
+       def __init__(self, *args, **kwargs):
+               super().__init__(*args, **kwargs)
+               self.repo = self.get_repo(GITHUB_REPO)
+
+       def get_pr(self) -> Optional[PullRequest]:
+               """
+               Retrieve the PR opened by this script if available
+               :return:
+               """
+               for issue in self.search_issues("repo:%s is:pr is:open head:%s" 
%
+                                                                        
(self.repo.full_name, BRANCH_NAME)):
+                       return issue.as_pull_request()
+               return None
+
+       def get_branch(self) -> Optional[GitRef]:
+               """
+               Retrieve the git reference to this scripts branch if available
+               :return:
+               """
+               try:
+                       return self.repo.get_git_ref(f"heads/{BRANCH_NAME}")
+               except UnknownObjectException:
+                       return None
+
+       def create_git_tree_from_entry(self, entry: UpdateEntry, file: str) -> 
InputGitTreeElement:
+               """
+               Creates a git element for the purposes of committing
+               :param entry:
+               :param file:
+               :return:
+               """
+               file_path = os.path.join(entry.path, file)
+               if not os.path.exists(file_path):
+                       print(f"Could not find '{file_path}' to commit", 
file=sys.stderr)
+                       sys.exit(1)
+               with open(file_path, "r", encoding="utf-8") as changed_file:
+                       change_file = changed_file.read()
+               blob = self.repo.create_git_blob(change_file, "utf-8")
+               return InputGitTreeElement(path=file_path, mode='100644', 
type='blob', sha=blob.sha)
+
+       def create_pull_request(self, update_entries: [UpdateEntry]) -> 
PullRequest:
+               """
+               Create a pull request based on update entries
+               :param update_entries:
+               :return:
+               """
+               with open(os.path.join(os.path.dirname(__file__), "templates", 
"pr.md"),
+                                 encoding="utf-8") as template_file:
+                       pr_template = template_file.read()
+               tree_elements = []
+               projects = ""
+               updated = ""
+               for update in update_entries:
+                       
tree_elements.append(self.create_git_tree_from_entry(update, "package.json"))
+                       
tree_elements.append(self.create_git_tree_from_entry(update, 
"package-lock.json"))
+                       updated += str(update)
+                       project = path_to_project(update.path)
+                       if project is None:
+                               print(f"Unknown project from path 
{update.path}")
+                       elif project not in projects:
+                               projects += f"* {project}\n"
+               ref = self.repo.create_git_ref(ref=f"refs/heads/{BRANCH_NAME}",
+                                                                          
sha=self.repo.get_branch(GITHUB_REF_NAME).commit.sha)
+               branch = self.repo.get_branch(BRANCH_NAME)
+               base_tree = self.repo.get_git_tree(sha=branch.commit.sha)
+               commit = self.repo.create_git_commit("Update chromedriver",
+                                                                               
         self.repo.create_git_tree(tree_elements, base_tree),
+                                                                               
         [self.repo.get_git_commit(sha=branch.commit.sha)])
+               ref.edit(sha=commit.sha)
+
+               pull = self.repo.create_pull(title="Update Chromedriver 
Versions", maintainer_can_modify=True,
+                                                                        
body=pr_template.format(UPDATES=updated, PROJECTS=projects),
+                                                                        
head=f"{GIT_AUTHOR_NAME}:{BRANCH_NAME}", base=GITHUB_REF_NAME)
+
+               try:
+                       labels = [self.repo.get_label("tests"), 
self.repo.get_label("dependencies")]
+                       if TRAFFIC_PORTAL in projects:
+                               
labels.append(self.repo.get_label(TRAFFIC_PORTAL))
+                       if TRAFFIC_PORTAL_V2 in projects:
+                               
labels.append(self.repo.get_label(TRAFFIC_PORTAL_V2))
+                       pull.add_to_labels(*labels)
+               except UnknownObjectException:
+                       print("Could not find labels", file=sys.stderr)
+
+               return pull
+
+
+if __name__ == "__main__":
+       print("Logging into github")
+       gh = PRCreator(login_or_token=PR_GITHUB_TOKEN)
+
+       if len(sys.argv) > 2:
+               print("chromedriver_updater [updates file]", file=sys.stderr)
+               sys.exit(1)
+
+       pull_request = gh.get_pr()
+       if pull_request is not None:
+               print(f"PR already exists; number: {pull_request.number}, url: 
{pull_request.html_url}")
+               sys.exit(0)
+
+       if len(sys.argv) == 1:
+               sys.exit(0)
+
+       if not os.path.exists(sys.argv[1]):
+               print(f"File '{sys.argv[1]}' does not exist", file=sys.stderr)
+               sys.exit(1)
+       with open(sys.argv[1], "r", encoding="utf-8") as f:
+               updates = [line.rstrip() for line in f.readlines()]
+       if len(updates) == 0:
+               print("Nothing to update")
+               sys.exit(0)
+       branch_ref = gh.get_branch()
+       if branch_ref is not None:
+               print("Branch already exists (but not a pull request), 
recreating")
+               branch_ref.delete()
+
+       updates = [parse_update_entry(update) for update in updates if update 
!= ""]
+       gh.create_pull_request(updates)
diff --git 
a/.github/actions/chromedriver-updater/chromedriver_updater/constants.py 
b/.github/actions/chromedriver-updater/chromedriver_updater/constants.py
new file mode 100644
index 0000000000..d8c4cedb35
--- /dev/null
+++ b/.github/actions/chromedriver-updater/chromedriver_updater/constants.py
@@ -0,0 +1,36 @@
+#  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.
+"""
+Contains and loads constants used by the updater
+"""
+import os
+from typing import Optional
+
+
+def getenv(env_name: str) -> str:
+       """
+       Gets environment variable :param env_name:
+       """
+       env_var: Optional[str] = os.environ.get(env_name)
+       if env_var is None:
+               raise NameError(f"Environment variable {env_name} is not 
defined")
+       return env_var
+
+
+GIT_AUTHOR_NAME = getenv("GIT_AUTHOR_NAME")
+GITHUB_REPO = getenv("GITHUB_REPOSITORY")
+GITHUB_REF_NAME = getenv("GITHUB_REF_NAME")
+PR_GITHUB_TOKEN = getenv("PR_GITHUB_TOKEN")
+GITHUB_TOKEN = getenv("GITHUB_TOKEN")
+BRANCH_NAME = "ATC-Chromedriver-Updater"
+TRAFFIC_PORTAL_V2 = "Traffic Portal v2"
+TRAFFIC_PORTAL = "Traffic Portal"
diff --git 
a/.github/actions/chromedriver-updater/chromedriver_updater/templates/pr.md 
b/.github/actions/chromedriver-updater/chromedriver_updater/templates/pr.md
new file mode 100644
index 0000000000..9a92343051
--- /dev/null
+++ b/.github/actions/chromedriver-updater/chromedriver_updater/templates/pr.md
@@ -0,0 +1,33 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+This PR updates chromedriver in:
+{UPDATES}
+
+## Which Traffic Control components are affected by this PR?
+{PROJECTS}
+
+## What is the best way to verify this PR?
+Verify that the affected e2e tests pass.
+
+## PR submission checklist
+- [ ] This PR has tests
+- [ ] This PR has documentation
+- [ ] This PR has a CHANGELOG.md entry
+- [x] This PR **DOES NOT FIX A SERIOUS SECURITY VULNERABILITY** (see [the 
Apache Software Foundation's security guidelines](https://apache.org/security) 
for details)
diff --git a/.github/actions/chromedriver-updater/entrypoint.sh 
b/.github/actions/chromedriver-updater/entrypoint.sh
new file mode 100755
index 0000000000..4f96e429b9
--- /dev/null
+++ b/.github/actions/chromedriver-updater/entrypoint.sh
@@ -0,0 +1,55 @@
+#
+# 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.
+#
+
+trap 'echo "Error on line ${LINENO} of ${0}"; exit 1' ERR;
+set -o errexit -o nounset
+
+if [[ -z ${PROJECTS} ]]; then
+  echo "PROJECTS environment variable not set!"
+  exit 1
+fi
+
+python3 -m chromedriver_updater
+
+projects=($(echo $PROJECTS | tr ',' ' '))
+touch updates.txt
+
+for proj in "${projects[@]}"
+do
+  package="./$proj"package.json
+  if [[ ! -f "$package" ]]; then
+    echo "Unable to find package.json in project directory $proj"
+    continue
+  fi
+
+  pushd "./$proj" > /dev/null
+
+  outdated=$(npm outdated | grep "chromedriver" || echo "" )
+
+  if [ "$outdated" = "" ]; then
+    echo "$proj is up to date"
+    popd > /dev/null
+    continue
+  fi
+
+  latest=$(echo $outdated | cut -d ' ' -f4)
+  wanted=$(echo $outdated | cut -d ' ' -f3)
+
+  npm i --save-dev "chromedriver@$latest" --ignore-scripts > /dev/null
+
+  popd > /dev/null
+  echo -e "$proj:$wanted,$latest\n" >> updates.txt
+done
+
+python3 -m chromedriver_updater updates.txt
diff --git a/.github/actions/chromedriver-updater/requirements.txt 
b/.github/actions/chromedriver-updater/requirements.txt
new file mode 100644
index 0000000000..c46ccd487e
--- /dev/null
+++ b/.github/actions/chromedriver-updater/requirements.txt
@@ -0,0 +1,13 @@
+# 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.
+#
+PyGithub
diff --git a/.github/actions/chromedriver-updater/setup.cfg 
b/.github/actions/chromedriver-updater/setup.cfg
new file mode 100644
index 0000000000..3c431a7f51
--- /dev/null
+++ b/.github/actions/chromedriver-updater/setup.cfg
@@ -0,0 +1,36 @@
+# 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.
+#
+[metadata]
+name = chromedriver_updater
+version = 1.0.0
+description = Updates chromedriver in package.json to latest version
+long_description = file: README.rst
+long_description_content_type = text/x-rst
+author = Apache Traffic Control
+author_email = [email protected]
+classifiers = OSI Approved :: Apache Software License
+
+[options]
+python_requires = >=3.10
+packages = chromedriver_updater
+install_requires =
+       PyGithub
+
+[options.entry_points]
+console_scripts = chromedriver_updater = chromedriver_updater:main
+
+[options.extras_require]
+test = unittest
+
+[options.package_data]
+chromedriver_updater = templates/pr.md
diff --git a/.github/actions/chromedriver-updater/setup.py 
b/.github/actions/chromedriver-updater/setup.py
new file mode 100644
index 0000000000..027c64df17
--- /dev/null
+++ b/.github/actions/chromedriver-updater/setup.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+# 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.
+#
+
+"""
+The setuptools-based install script for the audit-checker GitHub Action
+"""
+from setuptools import setup
+
+setup()
diff --git a/.github/workflows/chromdriver-update.yml 
b/.github/workflows/chromdriver-update.yml
new file mode 100644
index 0000000000..965e1624a7
--- /dev/null
+++ b/.github/workflows/chromdriver-update.yml
@@ -0,0 +1,51 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+name: Chromedriver Updater
+
+on:
+  workflow_dispatch:
+  schedule:
+    # Every day at 00:00
+    - cron: '0 0 * * *'
+
+env:
+  PROJECTS:
+
+jobs:
+  chromedriver-updater:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@master
+        id: checkout
+      - name: Install Python 3.10
+        uses: actions/setup-python@v2
+        with: { python-version: '3.10' } # Must be quoted to include the 
trailing 0
+      - name: Install Node 16
+        uses: actions/setup-node@v3
+        with:
+          node-version: 16.x
+      - name: Install updater Python module and dependencies
+        run: pip install .github/actions/chromedriver-updater
+      - name: Run chromedriver-updater
+        uses: ./.github/actions/chromedriver-updater
+        env:
+          GIT_AUTHOR_NAME: asf-ci
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          PR_GITHUB_TOKEN: ${{ secrets.ASFCI_TOKEN }}
+          PROJECTS: 
"traffic_portal/test/integration/,experimental/traffic-portal/"

Reply via email to