From 868d84fd1fc1cb7c2c7dd3f97b711fcc18a36436 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Fri, 27 Sep 2024 14:27:39 -0700 Subject: [PATCH 01/32] Importing JosiahSiegel's action-connect-ovpn GitHub Action --- .../.github/dependabot.yml | 6 ++ .../.github/vpn/config.ovpn | 21 ++++ .../action-connect-ovpn/.github/vpn/test.ovpn | 42 ++++++++ .../.github/workflows/check_vpn.yml | 29 ++++++ .../actions/action-connect-ovpn/.gitignore | 4 + .github/actions/action-connect-ovpn/README.md | 96 +++++++++++++++++++ .../actions/action-connect-ovpn/action.yml | 64 +++++++++++++ .../actions/action-connect-ovpn/example.ovpn | 38 ++++++++ 8 files changed, 300 insertions(+) create mode 100644 .github/actions/action-connect-ovpn/.github/dependabot.yml create mode 100644 .github/actions/action-connect-ovpn/.github/vpn/config.ovpn create mode 100644 .github/actions/action-connect-ovpn/.github/vpn/test.ovpn create mode 100644 .github/actions/action-connect-ovpn/.github/workflows/check_vpn.yml create mode 100644 .github/actions/action-connect-ovpn/.gitignore create mode 100644 .github/actions/action-connect-ovpn/README.md create mode 100644 .github/actions/action-connect-ovpn/action.yml create mode 100644 .github/actions/action-connect-ovpn/example.ovpn diff --git a/.github/actions/action-connect-ovpn/.github/dependabot.yml b/.github/actions/action-connect-ovpn/.github/dependabot.yml new file mode 100644 index 00000000000..123014908be --- /dev/null +++ b/.github/actions/action-connect-ovpn/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/actions/action-connect-ovpn/.github/vpn/config.ovpn b/.github/actions/action-connect-ovpn/.github/vpn/config.ovpn new file mode 100644 index 00000000000..01f03eceeea --- /dev/null +++ b/.github/actions/action-connect-ovpn/.github/vpn/config.ovpn @@ -0,0 +1,21 @@ + +client +dev tun +proto tcp +remote 188.94.28.233 443 +verify-x509-name "C=de, L=Hamburg, O=IT works Consulting GmbH, CN=inf-gw-r1-06, emailAddress=technik@itworks-hh.de" +route remote_host 255.255.255.255 net_gateway +resolv-retry infinite +nobind +persist-key +persist-tun +auth-user-pass secret.txt +cipher AES-256-CBC +auth SHA256 +comp-lzo no +route-delay 4 +verb 3 +reneg-sec 0 +ca ca.crt +cert user.crt +key user.key \ No newline at end of file diff --git a/.github/actions/action-connect-ovpn/.github/vpn/test.ovpn b/.github/actions/action-connect-ovpn/.github/vpn/test.ovpn new file mode 100644 index 00000000000..b236608121f --- /dev/null +++ b/.github/actions/action-connect-ovpn/.github/vpn/test.ovpn @@ -0,0 +1,42 @@ +client +remote azuregateway-d1ad0077-4117-45f0-b6fb-3989f1dd1945-a2e7e46fde0f.vpn.azure.com 443 +verify-x509-name 'd1ad0077-4117-45f0-b6fb-3989f1dd1945.vpn.azure.com' name +remote-cert-tls server + +dev tun +proto tcp +resolv-retry infinite +nobind + +auth SHA256 +cipher AES-256-GCM +persist-key +persist-tun + +tls-timeout 30 +tls-version-min 1.2 +key-direction 1 + +dhcp-option DNS 10.0.2.4 +dhcp-option DOMAIN azure.net +dhcp-option DOMAIN azure.com +dhcp-option DOMAIN azurewebsites.net +dhcp-option DOMAIN windows.net + +verb 3 + +# P2S CA root certificate +ca ca.crt + +# Pre Shared Key +tls-auth tls.key + +# P2S client certificate +# Please fill this field with a PEM formatted client certificate +# Alternatively, configure 'cert PATH_TO_CLIENT_CERT' to use input from a PEM certificate file. +cert user.crt + +# P2S client certificate private key +# Please fill this field with a PEM formatted private key of the client certificate. +# Alternatively, configure 'key PATH_TO_CLIENT_KEY' to use input from a PEM key file. +key user.key diff --git a/.github/actions/action-connect-ovpn/.github/workflows/check_vpn.yml b/.github/actions/action-connect-ovpn/.github/workflows/check_vpn.yml new file mode 100644 index 00000000000..3b63e1895bb --- /dev/null +++ b/.github/actions/action-connect-ovpn/.github/workflows/check_vpn.yml @@ -0,0 +1,29 @@ +name: Check connect vpn + +on: pull_request + +jobs: + check-vpn: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Open VPN + run: sudo apt-get install openvpn + - name: Connect VPN + uses: ./ + id: connect_vpn + with: + PING_URL: '${{ secrets.PING_URL }}' + FILE_OVPN: '.github/vpn/test.ovpn' + TLS_KEY: ${{ secrets.TLS_KEY }} + env: + CA_CRT: ${{ secrets.CA_CRT}} + USER_CRT: ${{ secrets.USER_CRT }} + USER_KEY: ${{ secrets.USER_KEY }} + - name: Check Connect VPN + env: + STATUS: ${{ steps.connect_vpn.outputs.STATUS }} + run: echo "$STATUS" + - name: kill vpn + if: always() + run: sudo killall openvpn diff --git a/.github/actions/action-connect-ovpn/.gitignore b/.github/actions/action-connect-ovpn/.gitignore new file mode 100644 index 00000000000..d8a936ebac0 --- /dev/null +++ b/.github/actions/action-connect-ovpn/.gitignore @@ -0,0 +1,4 @@ +.env +*.crt +*.key +*.txt diff --git a/.github/actions/action-connect-ovpn/README.md b/.github/actions/action-connect-ovpn/README.md new file mode 100644 index 00000000000..7f1ab85443f --- /dev/null +++ b/.github/actions/action-connect-ovpn/README.md @@ -0,0 +1,96 @@ +<div align="center"><h1>Actions Connect Open VPN</h1></div> + +>*Replaced deprecated [`set-output`](https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/)* + +> v2 switches to openvpn CLI for stability + +## Example file `.ovpn` to connect vpn + +[Example.ovpn](./example.ovpn) + +## Configuration with With + +The following settings must be passed as environment variables as shown in the +example. + +| Key | Value | Suggested Type | Required | Default | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------- | -------- | --------------- | +| `FILE_OVPN` | Location file open vpn and . | `env` | **Yes** | `./config.ovpn` | +| `PING_URL` | URL for check status vpn connect pass or fail | `env` | **Yes** | `127.0.0.1` | +| `SECRET` | Username password for access vpn`(Encode base 64 before set secret.)`[How to encode base 64 ?](https://www.base64encode.org/). | `secret env` | No | `''` | +| `TLS_KEY` | Tls-crypt for access vpn `(Encode base 64 before set secret.)`[How to encode base 64 ?](https://www.base64encode.org/). | `secret env` | No | `''` | + +## Configuration with Env + +The following settings must be passed as environment variables as shown in the +example. + +| Key | Value | Suggested Type | Required | Default | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------- | -------- | ------- | +| `CA_CRT` | Certificate for access vpn `(Encode base 64 before set secret.)`[How to encode base 64 ?](https://www.base64encode.org/). | `secret env` | **Yes** | N/A | +| `USER_CRT` | User certificate for access vpn. `(Encode base 64 before set secret.)`[How to encode base 64 ?](https://www.base64encode.org/). | `secret env` | **Yes** | N/A | +| `USER_KEY` | User key for access vpn. `(Encode base 64 before set secret.)`[How to encode base 64 ?](https://www.base64encode.org/). | `secret env` | **Yes** | N/A | + +## Outputs + +### `STATUS` + +**Boolean** Can get status after connect `true` or `false`. + +## Example usage + +```yml + connect-open-vpn: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Install Open VPN + run: sudo apt-get install openvpn + - name: Connect VPN + uses: golfzaptw/action-connect-ovpn@master + id: connect_vpn + with: + PING_URL: '127.0.0.1' + FILE_OVPN: '.github/vpn/config.ovpn' + SECRET: ${{ secrets.SECRET_USERNAME_PASSWORD }} + TLS_KEY: ${{ secrets.TLS_KEY }} + env: + CA_CRT: ${{ secrets.CA_CRT}} + USER_CRT: ${{ secrets.USER_CRT }} + USER_KEY: ${{ secrets.USER_KEY }} + - name: Check Connect VPN + run: echo ${{ steps.connect_vpn.outputs.STATUS }} + - name: kill vpn + if: always() + run: sudo killall openvpn +``` + +## How to prepare file .ovpn + +### Step + +1. Copy the data inside the tags +`<ca></ca>` +`<cert></cert>` +`<key></key>` +and encode those values to base64. Then save those values (without a new line!) to the secrets in github actions + +2. In the .ovpn file in your repo, remove the tags +`<ca></ca>` +`<cert></cert>` +`<key></key> ` +and replace the values with +``` +ca ca.crt +cert user.crt +key user.key +``` + +This will allow the values to be filled in from Github secrets. + +3. If your open vpn configuration has a username and password please encode those in base64. After that, save the values in the github actions secrets. +format username password +username-vpn +password-vpn + +4. If open vpn have tag `<tls></tls>` please repeat step 1 and 2 for the TLS records. diff --git a/.github/actions/action-connect-ovpn/action.yml b/.github/actions/action-connect-ovpn/action.yml new file mode 100644 index 00000000000..1aeac9d9876 --- /dev/null +++ b/.github/actions/action-connect-ovpn/action.yml @@ -0,0 +1,64 @@ +name: 'Connect-VPN-action' +description: 'Connect VPN action' +branding: + icon: 'shield' + color: 'orange' +inputs: + SECRET: + description: 'Username and password for access vpn' + required: false + default: '' + TLS_KEY: + description: 'User key for access vpn' + required: false + default: '' + PING_URL: + description: 'For check success or fail' + required: true + default: '127.0.0.1' + FILE_OVPN: + description: 'Location file open vpn' + required: true + default: './config.ovpn' +outputs: + STATUS: + description: 'Status for check connect vpn' + value: ${{ steps.vpn_status.outputs.vpn-status }} +runs: + using: "composite" + steps: + - name: Install OpenVPN + run: | + sudo apt-get update + sudo apt-get install openvpn + sudo apt-get install openvpn-systemd-resolved + shell: bash + + - name: Connect VPN + env: + TLS_KEY: ${{ inputs.TLS_KEY }} + CA_CRT: ${{ env.CA_CRT}} + USER_CRT: ${{ env.USER_CRT }} + USER_KEY: ${{ env.USER_KEY }} + SECRET: ${{ inputs.SECRET }} + shell: bash + run: | + echo "$TLS_KEY" | base64 -d > tls.key + echo "$CA_CRT" | base64 -d > ca.crt + echo "$USER_CRT" | base64 -d > user.crt + echo "$USER_KEY" | base64 -d > user.key + echo "$SECRET" | base64 -d > secret.txt + sudo openvpn --config ${{ inputs.FILE_OVPN }} --daemon + + - name: VPN Status + id: vpn_status + env: + PING_URL: ${{ inputs.PING_URL }} + shell: bash + run: | + sleep 5 + if ping -c 2 $PING_URL > /dev/null 2>&1; then + echo "vpn-status=true" >> $GITHUB_OUTPUT + else + echo "vpn-status=false" >> $GITHUB_OUTPUT + fi diff --git a/.github/actions/action-connect-ovpn/example.ovpn b/.github/actions/action-connect-ovpn/example.ovpn new file mode 100644 index 00000000000..ee61f15a507 --- /dev/null +++ b/.github/actions/action-connect-ovpn/example.ovpn @@ -0,0 +1,38 @@ +// FULL FILE OVPN + +client +dev tun +proto udp +resolv-retry infinite +nobind +persist-key +persist-tun +remote-cert-tls server +auth-nocache +verb 3 +<ca> +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + b1:b0:0b:1a:ad:05:54:0f +-----BEGIN CERTIFICATE----- +MIIBtjCCAVygAwIBAgIUbPYCDoO+XmScoS84AhQsbnKvd84wCgYIKoZIzj0EAwIw +u1MjifHr6jMxwQ== +-----END CERTIFICATE----- +</ca> +<cert> +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + b1:b0:0b:1a:ad:05:54:0f +-----BEGIN CERTIFICATE----- +MIIBtjCCAVygAwIBAgIUbPYCDoO+XmScoS +-----END CERTIFICATE----- +</cert> +<key> +-----BEGIN CERTIFICATE----- +MIIBtjCCAVygAwIBAgIUbPYCDoO+XmScoS84AhQsbn +-----END CERTIFICATE----- +</key> From 2e5d21958e8aa5a8d708c8a030bb6bc8bf2ce042 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Fri, 27 Sep 2024 14:28:04 -0700 Subject: [PATCH 02/32] Importing JosiahSiegel's runleaks GitHub Action --- .../actions/runleaks/.github/dependabot.yml | 16 ++ .../runleaks/.github/runleaks/exclusions.txt | 26 +++ .../runleaks/.github/runleaks/patterns.txt | 20 +++ .../runleaks/patterns_force_failure.txt | 21 +++ .../runleaks/.github/workflows/main.yml | 49 ++++++ .../.github/workflows/scan_public.yml | 61 +++++++ .github/actions/runleaks/Dockerfile | 9 + .github/actions/runleaks/LICENSE | 21 +++ .github/actions/runleaks/README.md | 163 ++++++++++++++++++ .github/actions/runleaks/action.yml | 55 ++++++ .github/actions/runleaks/lib/scan.sh | 126 ++++++++++++++ 11 files changed, 567 insertions(+) create mode 100644 .github/actions/runleaks/.github/dependabot.yml create mode 100644 .github/actions/runleaks/.github/runleaks/exclusions.txt create mode 100644 .github/actions/runleaks/.github/runleaks/patterns.txt create mode 100644 .github/actions/runleaks/.github/runleaks/patterns_force_failure.txt create mode 100644 .github/actions/runleaks/.github/workflows/main.yml create mode 100644 .github/actions/runleaks/.github/workflows/scan_public.yml create mode 100644 .github/actions/runleaks/Dockerfile create mode 100644 .github/actions/runleaks/LICENSE create mode 100644 .github/actions/runleaks/README.md create mode 100644 .github/actions/runleaks/action.yml create mode 100644 .github/actions/runleaks/lib/scan.sh diff --git a/.github/actions/runleaks/.github/dependabot.yml b/.github/actions/runleaks/.github/dependabot.yml new file mode 100644 index 00000000000..92f5139560b --- /dev/null +++ b/.github/actions/runleaks/.github/dependabot.yml @@ -0,0 +1,16 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + + - package-ecosystem: "docker" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/actions/runleaks/.github/runleaks/exclusions.txt b/.github/actions/runleaks/.github/runleaks/exclusions.txt new file mode 100644 index 00000000000..74dddf21418 --- /dev/null +++ b/.github/actions/runleaks/.github/runleaks/exclusions.txt @@ -0,0 +1,26 @@ +#################################################################### + +# Add regular expressions patterns to filter false positives. + +# GUID +("|')[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}("|') +# Azure blob endpoint +("|').*[0-9a-zA-Z]{2,256}[.][b|B][l|L][o|O][b|B][.][c|C][o|O][r|R][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]("|') +# Azure queue endpoint +("|').*[0-9a-zA-Z]{2,256}[.][q|Q][u|U][e|E][u|U][e|E][.][c|C][o|O][r|R][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]("|') +# Azure table endpoint +("|').*[0-9a-zA-Z]{2,256}[.][t|T][a|A][b|B][l|L][e|E][.][c|C][o|O][r|R][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]("|') +# Azure database endpoint +("|').*[0-9a-zA-Z]{2,256}[.][d|D][a|A][t|T][a|A][b|B][a|A][s|S][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]("|') +# Azure servicebus endpoint +("|').*[0-9a-zA-Z]{2,256}[.][s|S][e|E][r|R][v|V][i|I][c|C][e|E][b|B][u|U][s|S][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]("|') +# Azure timeseries endpoint +("|').*[0-9a-zA-Z]{2,256}[.][t|T][i|I][m|M][e|E][s|S][e|E][r|R][i|I][e|E][s|S][.][a|A][z|Z][u|U][r|R][e|E][.][c|C][o|O][m|M]("|') +# 32 length string +("|')[A-Z0-9a-z[:punct:]]{32}("|')$ +# 88 length string +("|')[A-Z0-9a-z[:punct:]]{88}("|')$ +# Common storage credential +.*-Amz-Credential=.* + +#################################################################### diff --git a/.github/actions/runleaks/.github/runleaks/patterns.txt b/.github/actions/runleaks/.github/runleaks/patterns.txt new file mode 100644 index 00000000000..405f0bf7f08 --- /dev/null +++ b/.github/actions/runleaks/.github/runleaks/patterns.txt @@ -0,0 +1,20 @@ +#################################################################### + +# Register a secret provider +--register-azure +--register-aws +--register-gcp + +#################################################################### + +# Add a prohibited pattern +--add [A-Z0-9]{20} +--add Account[k|K]ey +--add Shared[a|A]ccessSignature + +#################################################################### + +# Add a string that is scanned for literally (+ is escaped): +--add --literal foo+bar + +#################################################################### diff --git a/.github/actions/runleaks/.github/runleaks/patterns_force_failure.txt b/.github/actions/runleaks/.github/runleaks/patterns_force_failure.txt new file mode 100644 index 00000000000..fbd849a10fa --- /dev/null +++ b/.github/actions/runleaks/.github/runleaks/patterns_force_failure.txt @@ -0,0 +1,21 @@ +#################################################################### + +# Register a secret provider +--register-azure +--register-aws +--register-gcp + +#################################################################### + +# Add a prohibited pattern +--add [A-Z0-9]{20} +--add Account[k|K]ey +--add Shared[a|A]ccessSignature +--add token + +#################################################################### + +# Add a string that is scanned for literally (+ is escaped): +--add --literal foo+bar + +#################################################################### diff --git a/.github/actions/runleaks/.github/workflows/main.yml b/.github/actions/runleaks/.github/workflows/main.yml new file mode 100644 index 00000000000..186fea02512 --- /dev/null +++ b/.github/actions/runleaks/.github/workflows/main.yml @@ -0,0 +1,49 @@ +name: Scan Action Logs + +on: [push] + +jobs: + scan_run_logs_1: + runs-on: ubuntu-latest + name: Scan repo run logs 1 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Scan run logs + uses: ./ + id: scan + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + run-limit: 5 + fail-on-leak: false + - name: Get scan exceptions + if: steps.scan.outputs.count > 0 + run: | + echo "count=${{ steps.scan.outputs.count }}" + exceptions='${{ steps.scan.outputs.exceptions }}' + echo ${exceptions//"%0A"/} | jq '.' + - name: Confirm no exceptions + if: steps.scan.outputs.count == 0 + run: | + echo "No exceptions found!" + scan_run_logs_2: + runs-on: ubuntu-latest + name: Scan repo run logs 2 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Scan run logs + uses: ./ + id: scan + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + run-limit: 5 + patterns-path: ".github/runleaks/patterns_force_failure.txt" + exclusions-path: ".github/runleaks/exclusions.txt" + fail-on-leak: false + - name: Get scan exceptions + if: steps.scan.outputs.count > 0 + run: | + echo "count=${{ steps.scan.outputs.count }}" + exceptions='${{ steps.scan.outputs.exceptions }}' + echo ${exceptions//"%0A"/} | jq '.' diff --git a/.github/actions/runleaks/.github/workflows/scan_public.yml b/.github/actions/runleaks/.github/workflows/scan_public.yml new file mode 100644 index 00000000000..fff9d4f7a4a --- /dev/null +++ b/.github/actions/runleaks/.github/workflows/scan_public.yml @@ -0,0 +1,61 @@ +name: Scan Public Logs + +on: + workflow_dispatch: + inputs: + repos: + description: 'Repos to scan:' + required: true + default: '[\"josiahsiegel\/runleaks\",\"josiahsiegel\/AzViz-action\"]' + type: string + schedule: + - cron: "0 3 * * *" + push: + +jobs: + pre_job: + name: "Set Build Environment" + runs-on: ubuntu-latest + outputs: + repos: ${{ env.REPOS }} + steps: + - name: Set workflow_dispatch matrix + if: github.event_name == 'workflow_dispatch' + run: echo "REPOS=${{ github.event.inputs.repos }}" >> $GITHUB_ENV + + - name: Fetch random repo name + if: github.event_name == 'schedule' || github.event_name == 'push' + uses: JosiahSiegel/randomrepo@v1.1 + id: random + with: + github-token: ${{ secrets.MY_TOKEN }} + + - name: Set schedule matrix + if: github.event_name == 'schedule' || github.event_name == 'push' + run: | + format_string=$(echo "[\"${{ steps.random.outputs.repo }}\"]" | sed 's/\//\\\//g') + echo "REPOS='$format_string'" + echo "REPOS=$format_string" >> $GITHUB_ENV + + scan_public_logs: + runs-on: ubuntu-latest + name: Scan public logs + needs: + - pre_job + strategy: + matrix: + repo: ${{ fromJson(needs.pre_job.outputs.repos) }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Scan run logs - ${{ matrix.repo }} + uses: ./ + id: scan + with: + github-token: ${{ secrets.MY_TOKEN }} + repo: ${{ matrix.repo }} + run-limit: 100 + min-days-old: 1 + max-days-old: 3 + exclusions-path: ".github/runleaks/exclusions.txt" + fail-on-leak: true diff --git a/.github/actions/runleaks/Dockerfile b/.github/actions/runleaks/Dockerfile new file mode 100644 index 00000000000..872e8c611a0 --- /dev/null +++ b/.github/actions/runleaks/Dockerfile @@ -0,0 +1,9 @@ +FROM debian:stable-slim +RUN apt update +RUN apt install git gh make parallel jq -y + +RUN git clone https://github.com/JosiahSiegel/git-secrets.git +RUN make -C /git-secrets install +COPY lib/* / + +ENTRYPOINT ["bash", "/scan.sh"] diff --git a/.github/actions/runleaks/LICENSE b/.github/actions/runleaks/LICENSE new file mode 100644 index 00000000000..251ac3d0313 --- /dev/null +++ b/.github/actions/runleaks/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Josiah Siegel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.github/actions/runleaks/README.md b/.github/actions/runleaks/README.md new file mode 100644 index 00000000000..f859846858b --- /dev/null +++ b/.github/actions/runleaks/README.md @@ -0,0 +1,163 @@ +# runleaks + +[](https://github.com/JosiahSiegel/runleaks/actions/workflows/main.yml) + +Leverages [git-secrets](https://github.com/awslabs/git-secrets) to identify potential leaks in GitHub action run logs. + + * Common Azure and Google Cloud patterns are available, thanks to fork [msalemcode/git-secrets](https://github.com/msalemcode/git-secrets). + + +## Inputs +```yml + github-token: + description: 'Token used to login to GitHub' + required: true + repo: + description: 'Repo to scan run logs for exceptions' + required: false + default: ${{ github.repository }} + run-limit: + description: 'Limit on how many runs to scan' + required: false + default: '50' + min-days-old: + description: 'Min age of runs in days' + required: false + default: '0' + max-days-old: + description: 'Max age of runs in days' + required: false + default: '3' + patterns-path: + description: 'Patterns file path' + required: false + default: ".runleaks/patterns.txt" + exclusions-path: + description: 'Excluded patterns file path' + required: false + default: ".runleaks/exclusions.txt" + fail-on-leak: + description: 'Fail action if leak is found' + required: false + default: true +``` + +## Outputs +```yml + exceptions: + description: 'Json output of run logs with exceptions' + count: + description: 'Count of exceptions' +``` + +## Usage + * Note: [GitHub rate limits](#rate-limits) +```yml + - name: Checkout + uses: actions/checkout@v3 + - name: Scan run logs + uses: josiahsiegel/runleaks@v1 + id: scan + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + run-limit: 500 + fail-on-leak: false + - name: Get scan exceptions + if: steps.scan.outputs.count > 0 + run: echo "${{ steps.scan.outputs.exceptions }}" +``` +or +```yml + - name: Checkout + uses: actions/checkout@v3 + - name: Scan run logs + uses: josiahsiegel/runleaks@v1 + id: scan + with: + github-token: ${{ secrets.MY_TOKEN }} + patterns-path: ".github/patterns.txt" + exclusions-path: ".github/exclusions.txt" + fail-on-leak: false + - name: Get scan exceptions + if: steps.scan.outputs.count > 0 + run: echo "${{ steps.scan.outputs.exceptions }}" +``` +or +```yml + - name: Checkout + uses: actions/checkout@v3 + with: + repository: 'me/my-repo' + - name: Scan run logs + uses: josiahsiegel/runleaks@v1 + id: scan + with: + github-token: ${{ secrets.MY_TOKEN }} + repo: 'me/my-repo' + run-limit: 200 + min-days-old: 0 + max-days-old: 4 + fail-on-leak: true +``` + +## Local testing + * Registers default patterns +```sh +git clone https://github.com/JosiahSiegel/runleaks.git +cd runleaks/ +docker build -t runleaks . +docker run scan "<PERSONAL_ACCESS_TOKEN>" "<REPO>" <RUN_LIMIT> <MIN_DAYS_OLD> <MAX_DAYS_OLD> +``` + +## Pattern file + * Default location: `.runleaks/patterns.txt` + +``` +#################################################################### + +# Register a secret provider +#--register-azure +#--register-gcp +--register-aws + +#################################################################### + +# Add a prohibited pattern +--add [A-Z0-9]{20} +--add Account[k|K]ey +--add Shared[a|A]ccessSignature + +#################################################################### + +# Add a string that is scanned for literally (+ is escaped): +--add --literal foo+bar + +#################################################################### +``` + +## Exclusion file + * Default location: `.runleaks/exclusions.txt` +``` +#################################################################### + +# Add regular expressions patterns to filter false positives. + +# Allow GUID +("|')[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}("|') + +#################################################################### +``` + +## Performance + + * Scan 50 runs = 1 min + + * Scan 500 runs = 8 mins + +* Scan 3000 runs = 50 mins + +## Rate limits + +Built-in secret `GITHUB_TOKEN` is [limited to 1,000 requests per hour per repository](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#requests-from-github-actions). + +To avoid repo-wide rate limiting, personal access tokens can be added to secrets, which are [limited to 5,000 requests per hour and per authenticated user](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#requests-from-personal-accounts). diff --git a/.github/actions/runleaks/action.yml b/.github/actions/runleaks/action.yml new file mode 100644 index 00000000000..d431caf1cf6 --- /dev/null +++ b/.github/actions/runleaks/action.yml @@ -0,0 +1,55 @@ +# action.yml +name: 'runleaks' +description: 'Identify potential leaks in GitHub action logs' +branding: + icon: 'search' + color: 'red' +inputs: + github-token: + description: 'Token used to login to GitHub' + required: true + repo: + description: 'Repo to scan run logs for exceptions' + required: false + default: ${{ github.repository }} + run-limit: + description: 'Limit on how many runs to scan' + required: false + default: '100' + min-days-old: + description: 'Min age of runs in days' + required: false + default: '0' + max-days-old: + description: 'Max age of runs in days' + required: false + default: '3' + patterns-path: + description: 'Patterns file path' + required: false + default: ".github/runleaks/patterns.txt" + exclusions-path: + description: 'Excluded patterns file path' + required: false + default: ".github/runleaks/exclusions.txt" + fail-on-leak: + description: 'Fail action if leak is found' + required: false + default: true +outputs: + exceptions: + description: 'Json output of run logs with exceptions' + count: + description: 'Count of exceptions' +runs: + using: 'docker' + image: 'Dockerfile' + args: + - ${{ inputs.github-token }} + - ${{ inputs.repo }} + - ${{ inputs.run-limit }} + - ${{ inputs.min-days-old }} + - ${{ inputs.max-days-old }} + - ${{ inputs.patterns-path }} + - ${{ inputs.exclusions-path }} + - ${{ inputs.fail-on-leak }} diff --git a/.github/actions/runleaks/lib/scan.sh b/.github/actions/runleaks/lib/scan.sh new file mode 100644 index 00000000000..4a4b9cadb61 --- /dev/null +++ b/.github/actions/runleaks/lib/scan.sh @@ -0,0 +1,126 @@ +#!/bin/bash + +# Init +repo="$2" +run_count=$3 +min_days_old=$4 +max_days_old=$5 +fail_on_leak=$8 +max_proc_count=32 +log_file=exceptions_search.txt + +# Set defaults +if [[ -z $min_days_old ]]; then + min_days_old="0" + echo "min_days_old: $min_days_old" +fi + +if [[ -z $max_days_old ]]; then + max_days_old="3" + echo "max_days_old: $max_days_old" +fi + +if [[ -z $fail_on_leak ]]; then + fail_on_leak=true + echo "fail_on_leak: $fail_on_leak" +fi + +# If file path error, use default patterns +if cp "$6" /patterns.txt; then + # Register patterns + grep -v -E '(#.*$)|(^$)' /patterns.txt >/clean_patterns.txt + while read line; do + if [[ "$line" != "#*" ]]; then + git secrets $line --global + fi + done </clean_patterns.txt +else + git secrets --register-azure --global + git secrets --register-aws --global + git secrets --register-gcp --global +fi + +cp "$7" /.gitallowed + +# GitHub auth +echo "$1" >auth.txt +gh auth login --with-token <auth.txt +if [[ $? -ne 0 ]]; then + exit 1 +fi + +echo "Repo: $repo" +echo "============================================================================================" +echo "Scan patterns:" +echo "============================================================================================" +git secrets --list --global +echo "============================================================================================" +echo "Excluded patterns:" +echo "============================================================================================" +cat /.gitallowed +echo "============================================================================================" + +# Collect up to 400/day id runs +run_list_limit=$(($max_days_old * 400)) + +echo "Raw run list limited to: $run_list_limit" + +# Collect ids of workflow runs +declare -a run_ids=$(gh run list --repo "$repo" --json databaseId,status,updatedAt --jq '.[] | select(.updatedAt <= ((now - ('"$min_days_old"'*86400))|strftime("%Y-%m-%dT%H:%M:%S %Z"))) | select(.updatedAt > ((now - ('"$max_days_old"'*86400))|strftime("%Y-%m-%dT%H:%M:%S %Z"))) | select(.status =="completed").databaseId' --limit $run_list_limit) +run_ids_limited=$(printf "%s\n" ${run_ids[@]} | head -$run_count) + +echo "Runs to scan: $run_count" +echo $run_ids_limited + +touch "$log_file" + +run_for_each() { + local each=$@ + + # Collect run logs and remove null + log_out=$(gh run view $each --repo "$repo" --log 2>/dev/null | sed 's/\x0//g') + if [[ $? -ne 0 ]]; then + exit 1 + fi + + # Identify potential exceptions + scan_out=$(echo "$log_out" | git secrets --scan - 2>&1) + status=$? + + # If exception, add to array of details + if (($status != 0)); then + raw_log_full=$(echo "$scan_out" | grep '(standard input)') + exception_line=$(echo "$raw_log_full" | awk -F '\t' '{print $2$3}' | sed 's/[^a-zA-Z0-9]/_/g' | sed 's/.*/"&",/') + exception=$(gh run view $each --repo "$repo" --json name,createdAt,databaseId,url,updatedAt,headBranch 2>/dev/null) + if [[ $? -ne 0 ]]; then + exit 1 + fi + exception_with_detail=$(echo $exception | jq '. + {exception_detail: {'"$exception_line"'}}') + echo $exception_with_detail >>"$log_file" + fi +} + +# Make visible to subprocesses +export -f run_for_each +export log_file +export repo + +parallel -0 --jobs $max_proc_count run_for_each ::: $run_ids_limited + +json_out=$(jq -n '.exceptions |= [inputs]' "$log_file") +json_out_length=$(echo $json_out | jq -s '.[].exceptions' | jq length) +echo "$json_out" + +# Make output friendly +json_out="${json_out//'%'/'%25'}" +json_out="${json_out//$'\n'/'%0A'}" +json_out="${json_out//$'\r'/'%0D'}" +echo "exceptions=$json_out" >> $GITHUB_OUTPUT +echo "count=$json_out_length" >> $GITHUB_OUTPUT + +rm "$log_file" + +if [[ $fail_on_leak = true && json_out_length -gt 0 ]]; then + echo "Failing since leak!" + exit 1 +fi From 9fc1cfb6b6f247d18b0cdbc36bdc36d8a180be11 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Fri, 27 Sep 2024 14:28:31 -0700 Subject: [PATCH 03/32] Importing JosiahSiegel's terraform-stats GitHub Action --- .../.github/ISSUE_TEMPLATE/bug_report.md | 38 ++++++ .../.github/ISSUE_TEMPLATE/feature_request.md | 20 +++ .../terraform-stats/.github/dependabot.yml | 6 + .../.github/workflows/test_action.yml | 49 +++++++ .github/actions/terraform-stats/.gitignore | 1 + .../terraform-stats/CODE_OF_CONDUCT.md | 128 ++++++++++++++++++ .github/actions/terraform-stats/LICENSE | 21 +++ .github/actions/terraform-stats/README.md | 110 +++++++++++++++ .github/actions/terraform-stats/action.yml | 108 +++++++++++++++ .../actions/terraform-stats/lib/tf_stats.sh | 79 +++++++++++ 10 files changed, 560 insertions(+) create mode 100644 .github/actions/terraform-stats/.github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/actions/terraform-stats/.github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/actions/terraform-stats/.github/dependabot.yml create mode 100644 .github/actions/terraform-stats/.github/workflows/test_action.yml create mode 100644 .github/actions/terraform-stats/.gitignore create mode 100644 .github/actions/terraform-stats/CODE_OF_CONDUCT.md create mode 100644 .github/actions/terraform-stats/LICENSE create mode 100644 .github/actions/terraform-stats/README.md create mode 100644 .github/actions/terraform-stats/action.yml create mode 100755 .github/actions/terraform-stats/lib/tf_stats.sh diff --git a/.github/actions/terraform-stats/.github/ISSUE_TEMPLATE/bug_report.md b/.github/actions/terraform-stats/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000000..dd84ea7824f --- /dev/null +++ b/.github/actions/terraform-stats/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/actions/terraform-stats/.github/ISSUE_TEMPLATE/feature_request.md b/.github/actions/terraform-stats/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000000..bbcbbe7d615 --- /dev/null +++ b/.github/actions/terraform-stats/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/actions/terraform-stats/.github/dependabot.yml b/.github/actions/terraform-stats/.github/dependabot.yml new file mode 100644 index 00000000000..b552cbd5cc6 --- /dev/null +++ b/.github/actions/terraform-stats/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/actions/terraform-stats/.github/workflows/test_action.yml b/.github/actions/terraform-stats/.github/workflows/test_action.yml new file mode 100644 index 00000000000..8164d226ea1 --- /dev/null +++ b/.github/actions/terraform-stats/.github/workflows/test_action.yml @@ -0,0 +1,49 @@ +name: Test action + +on: + pull_request: + branches: + - main + +jobs: + test-action: + runs-on: ubuntu-latest + env: + tf-dir: "./terraform" + steps: + - name: Check out repository + uses: actions/checkout@v4 + - name: BEFORE APPLY - Use local action + uses: ./ + id: stats1 + with: + terraform-directory: ${{ env.tf-dir }} + add-args: -target=docker_image.nginx -target=docker_container.nginx + upload-plan: true + upload-retention-days: 0 + plan-file: tf_stats_plan.bin + terraform-version: 1.1.9 + - name: BEFORE APPLY - Get outputs + if: steps.stats1.outputs.change-count > 0 + run: | + echo "terraform-version: ${{ steps.stats1.outputs.terraform-version }}" + echo "drift-count: ${{ steps.stats1.outputs.drift-count }}" + echo "resource-drifts: ${{ steps.stats1.outputs.resource-drifts }}" + echo "change-count: ${{ steps.stats1.outputs.change-count }}" + echo "change-percent: ${{ steps.stats1.outputs.change-percent }}" + echo "resource-changes: ${{ steps.stats1.outputs.resource-changes }}" + - name: Terraform apply + run: terraform -chdir=${{ env.tf-dir }} apply -auto-approve + - name: AFTER APPLY - Use local action + uses: ./ + id: stats2 + with: + terraform-directory: ${{ env.tf-dir }} + - name: AFTER APPLY - Get outputs + run: | + echo "terraform-version: ${{ steps.stats2.outputs.terraform-version }}" + echo "drift-count: ${{ steps.stats2.outputs.drift-count }}" + echo "resource-drifts: ${{ steps.stats2.outputs.resource-drifts }}" + echo "change-count: ${{ steps.stats2.outputs.change-count }}" + echo "change-percent: ${{ steps.stats2.outputs.change-percent }}" + echo "resource-changes: ${{ steps.stats2.outputs.resource-changes }}" diff --git a/.github/actions/terraform-stats/.gitignore b/.github/actions/terraform-stats/.gitignore new file mode 100644 index 00000000000..670bd5d7e6c --- /dev/null +++ b/.github/actions/terraform-stats/.gitignore @@ -0,0 +1 @@ +terraform/* \ No newline at end of file diff --git a/.github/actions/terraform-stats/CODE_OF_CONDUCT.md b/.github/actions/terraform-stats/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..100fe08efe8 --- /dev/null +++ b/.github/actions/terraform-stats/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +josiah0601@gmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/.github/actions/terraform-stats/LICENSE b/.github/actions/terraform-stats/LICENSE new file mode 100644 index 00000000000..1c50494ad20 --- /dev/null +++ b/.github/actions/terraform-stats/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Josiah Siegel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.github/actions/terraform-stats/README.md b/.github/actions/terraform-stats/README.md new file mode 100644 index 00000000000..ed273fdcce7 --- /dev/null +++ b/.github/actions/terraform-stats/README.md @@ -0,0 +1,110 @@ +# Terraform Stats + +[](https://github.com/JosiahSiegel/terraform-stats/actions/workflows/test_action.yml) + +## Synopsis + +Output the following statistics for the Terraform environment: +1. Terraform version +2. Drift count + * "Drift" refers to changes made outside of Terraform and does not necessary match any resources listed for changes. +3. Resource drifts +4. Change count + * "Change" refers to change actions that Terraform plans to use to move from the prior state to a new state. +5. Change percent + * Percentage of changes to total resources. +6. Resource changes + +## Usage + +```yml +- name: Terraform stats + uses: josiahsiegel/terraform-stats@<latest-version> + id: stats + with: + terraform-directory: ${{ env.tf-dir }} + terraform-version: 1.1.9 +- name: Get outputs + run: | + echo "terraform-version: ${{ steps.stats.outputs.terraform-version }}" + echo "drift-count: ${{ steps.stats.outputs.drift-count }}" + echo "resource-drifts: ${{ steps.stats.outputs.resource-drifts }}" + echo "change-count: ${{ steps.stats.outputs.change-count }}" + echo "change-percent: ${{ steps.stats.outputs.change-percent }}" + echo "resource-changes: ${{ steps.stats.outputs.resource-changes }}" +``` + +## Workflow summary + +### :construction: Terraform Stats :construction: + +* change-count: 2 +* change-percent: 100 +* resource-changes: +```json +[ + { + "address": "docker_container.nginx", + "changes": [ + "create" + ] + }, + { + "address": "docker_image.nginx", + "changes": [ + "create" + ] + } +] +``` + +## Inputs + +```yml +inputs: + terraform-directory: + description: Terraform commands will run in this location. + required: true + default: "./terraform" + include-no-op: + description: "\"no-op\" refers to the before and after Terraform changes are identical as a value will only be known after apply." + required: true + default: false + add-args: + description: Pass additional arguments to Terraform plan. + required: true + default: "" + upload-plan: + description: Upload plan file. true or false + required: true + default: false + upload-retention-days: + description: Number of days to keep uploaded plan. + required: true + default: 7 + plan-file: + description: Name of plan file. + required: true + default: tf__stats__plan.bin + terraform-version: + description: Specify a specific version of Terraform + required: true + default: latest +``` + +## Outputs +```yml +outputs: + terraform-version: + description: 'Terraform version' + drift-count: + description: 'Count of drifts' + resource-drifts: + description: 'JSON output of resource drifts' + change-count: + description: 'Count of changes' + change-percent: + description: 'Percentage of changes to total resources' + resource-changes: + description: 'JSON output of resource changes' +``` diff --git a/.github/actions/terraform-stats/action.yml b/.github/actions/terraform-stats/action.yml new file mode 100644 index 00000000000..bfe0b53976e --- /dev/null +++ b/.github/actions/terraform-stats/action.yml @@ -0,0 +1,108 @@ +# action.yml +name: 'Generate Terraform statistics' +description: 'Output Terraform stats for drift and pending changes' +branding: + icon: 'bar-chart' + color: 'purple' +inputs: + terraform-directory: + description: Terraform commands will run in this location. + required: true + default: "./terraform" + include-no-op: + description: "\"no-op\" refers to the before and after Terraform changes are identical as a value will only be known after apply." + required: true + default: false + add-args: + description: Pass additional arguments to Terraform plan. + required: true + default: "" + upload-plan: + description: Upload plan file. true or false + required: true + default: false + upload-retention-days: + description: Number of days to keep uploaded plan. + required: true + default: 7 + plan-file: + description: Name of plan file. + required: true + default: tf__stats__plan.bin + terraform-version: + description: Specify a specific version of Terraform + required: true + default: latest + +outputs: + terraform-version: + description: 'Terraform version' + value: ${{ steps.local-action.outputs.terraform-version }} + drift-count: + description: 'Count of drifts' + value: ${{ steps.local-action.outputs.drift-count }} + resource-drifts: + description: 'JSON output of resource drifts' + value: ${{ steps.local-action.outputs.resource-drifts }} + change-count: + description: 'Count of changes' + value: ${{ steps.local-action.outputs.change-count }} + resource-changes: + description: 'JSON output of resource changes' + value: ${{ steps.local-action.outputs.resource-changes }} + change-percent: + description: 'Percentage of changes to total resources' + value: ${{ steps.local-action.outputs.change-percent }} + +runs: + using: "composite" + steps: + - name: Use specific version of Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: ${{ inputs.terraform-version }} + terraform_wrapper: false + - name: Run Terraform stats + id: local-action + run: | + ${{ github.action_path }}/lib/tf_stats.sh \ + "${{ inputs.terraform-directory }}" \ + ${{ inputs.include-no-op }} \ + "${{ inputs.add-args }}" \ + "${{ inputs.plan-file }}" + shell: bash + + - name: Upload Artifact + if: inputs.upload-plan == 'true' + uses: actions/upload-artifact@v4.3.3 + with: + name: ${{ inputs.plan-file }} + path: "${{ inputs.terraform-directory }}/${{ inputs.plan-file }}" + retention-days: ${{ inputs.upload-retention-days }} + + - name: Create summary + if: | + steps.local-action.outputs.change-count > 0 || + steps.local-action.outputs.drift-count > 0 + run: | + echo "### :construction: Terraform Stats :construction:" >> $GITHUB_STEP_SUMMARY + if [[ ${{ steps.local-action.outputs.change-count }} > 0 ]]; then + resource_changes=$(echo "${{ steps.local-action.outputs.resource-changes }}" | jq .) + echo " + * change-count: ${{ steps.local-action.outputs.change-count }} + * change-percent: ${{ steps.local-action.outputs.change-percent }} + * resource-changes: + \`\`\`json + $resource_changes + \`\`\`" >> $GITHUB_STEP_SUMMARY + fi + if [[ ${{ steps.local-action.outputs.drift-count }} > 0 ]]; then + resource_drifts=$(echo "${{ steps.local-action.outputs.resource-drifts }}" | jq .) + echo " + * drift-count: ${{ steps.local-action.outputs.drift-count }} + * resource-drift: + \`\`\`json + $resource_drifts + \`\`\`" >> $GITHUB_STEP_SUMMARY + fi + shell: bash diff --git a/.github/actions/terraform-stats/lib/tf_stats.sh b/.github/actions/terraform-stats/lib/tf_stats.sh new file mode 100755 index 00000000000..74eb7431ed9 --- /dev/null +++ b/.github/actions/terraform-stats/lib/tf_stats.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +tf_dir=$1 +#For ["no-op"], the before and +#after values are identical. The "after" value will be incomplete if there +#are values within it that won't be known until after apply. +include_no_op=$2 +add_args=$3 +plan_file=$4 + +# Define a function to run terraform plan with common arguments +tf_plan() { + terraform -chdir=$tf_dir plan $add_args -input=false -no-color -lock-timeout=120s -out=$plan_file "$@" +} + +# Try to run terraform plan and init if needed +if ! tf_plan &>/dev/null; then + terraform -chdir=$tf_dir init >/dev/null || exit 1 + tf_plan >/dev/null || exit 1 +fi + +# Get the plan output in text and json formats +PLAN_TXT=$( terraform -chdir=$tf_dir show -no-color $plan_file ) +PLAN_JSON=$( terraform -chdir=$tf_dir show -no-color -json $plan_file ) + +# Define a function to parse the plan json with jq +parse_plan_json() { + echo $PLAN_JSON | jq "$@" +} + +# Define a function to make output friendly +make_output_friendly() { + local output=$1 + output="${output//'%'/'%25'}" + output="${output//$'\n'/'%0A'}" + output="${output//$'\r'/'%0D'}" + output="${output//'"'/'\"'}" + output="${output//'\\"'/'\\\"'}" + echo $output +} + +# Define a function to write the output to the github output file +write_output() { + local key=$1 + local value=$2 + echo "$key=$(make_output_friendly $value)" >> $GITHUB_OUTPUT +} + +# Get the terraform version from the plan json +VERSION=$(parse_plan_json .terraform_version) + +# Get the resource drift from the plan json +DRIFT=$(parse_plan_json .resource_drift) +DRIFT_COUNT=$(echo $DRIFT | jq length) +DRIFTED_RESOURCES=$(echo $DRIFT | jq -c '[.[] | {address: .address, changes: .change.actions}]') + +# Get the resource changes from the plan json +CHANGES=$(parse_plan_json .resource_changes) +if [[ $include_no_op = true ]]; then + CHANGES_FILTERED=$CHANGES +else + CHANGES_FILTERED=$(echo $CHANGES | jq -c '[.[] | {address: .address, changes: .change.actions} | select( .changes[] != "no-op")]') +fi +CHANGE_COUNT=$(echo $CHANGES_FILTERED | jq length) + +# Get the total resources and percent changed from the plan json +TOTAL_RESOURCES=$(parse_plan_json .planned_values.root_module) +TOTAL_ROOT=$(echo $TOTAL_RESOURCES | jq -c .resources | jq length) +TOTAL_CHILD=$(echo $TOTAL_RESOURCES | jq -c .child_modules | jq -c '[.[]?.resources | length] | add') +TOTAL_COUNT=$(( TOTAL_ROOT + TOTAL_CHILD )) +CHANGE_PERC=$(echo "scale=0 ; $CHANGE_COUNT / $TOTAL_COUNT * 100" | bc) + +# Write the output to the github output file +write_output "terraform-version" "$VERSION" +write_output "change-percent" "$CHANGE_PERC" +write_output "drift-count" "$DRIFT_COUNT" +write_output "change-count" "$CHANGE_COUNT" +write_output "resource-drifts" "$DRIFTED_RESOURCES" +write_output "resource-changes" "$CHANGES_FILTERED" From 0d97a80c2afc7c43e2fb1ab4b44ca44b0b56e7ff Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 06:44:25 -0700 Subject: [PATCH 04/32] Correcting the terraform-stats GitHub Action with the specific used commit --- .github/actions/terraform-stats/.github/dependabot.yml | 4 ++++ .github/actions/terraform-stats/lib/tf_stats.sh | 0 2 files changed, 4 insertions(+) mode change 100755 => 100644 .github/actions/terraform-stats/lib/tf_stats.sh diff --git a/.github/actions/terraform-stats/.github/dependabot.yml b/.github/actions/terraform-stats/.github/dependabot.yml index b552cbd5cc6..48dfcf9324f 100644 --- a/.github/actions/terraform-stats/.github/dependabot.yml +++ b/.github/actions/terraform-stats/.github/dependabot.yml @@ -4,3 +4,7 @@ updates: directory: "/" # Location of package manifests schedule: interval: "weekly" + - package-ecosystem: "terraform" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/actions/terraform-stats/lib/tf_stats.sh b/.github/actions/terraform-stats/lib/tf_stats.sh old mode 100755 new mode 100644 From 4705450f2025723efb3f6b60cf91f45feda5fa0b Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 06:49:36 -0700 Subject: [PATCH 05/32] Importing JosiahSiegel's AzViz-action GitHub Action --- .../workflows/sample_full_workflow.yml | 70 ++++++++++++++ .../.github/workflows/sample_min_workflow.yml | 35 +++++++ .../.github/workflows/test_linux_runner.yml | 31 +++++++ .../.github/workflows/test_windows_runner.yml | 31 +++++++ .github/actions/azviz-action/README.md | 93 +++++++++++++++++++ .github/actions/azviz-action/action.yml | 83 +++++++++++++++++ .github/actions/azviz-action/viz_run.ps1 | 57 ++++++++++++ 7 files changed, 400 insertions(+) create mode 100644 .github/actions/azviz-action/.github/workflows/sample_full_workflow.yml create mode 100644 .github/actions/azviz-action/.github/workflows/sample_min_workflow.yml create mode 100644 .github/actions/azviz-action/.github/workflows/test_linux_runner.yml create mode 100644 .github/actions/azviz-action/.github/workflows/test_windows_runner.yml create mode 100644 .github/actions/azviz-action/README.md create mode 100644 .github/actions/azviz-action/action.yml create mode 100644 .github/actions/azviz-action/viz_run.ps1 diff --git a/.github/actions/azviz-action/.github/workflows/sample_full_workflow.yml b/.github/actions/azviz-action/.github/workflows/sample_full_workflow.yml new file mode 100644 index 00000000000..13a629d4231 --- /dev/null +++ b/.github/actions/azviz-action/.github/workflows/sample_full_workflow.yml @@ -0,0 +1,70 @@ +name: Manually triggered full workflow +on: + workflow_dispatch: + inputs: + resource-group: + description: Comma-seperated resource group list + required: true + default: test1-rg,test2-rg + out-file: + description: Graph export path + required: true + default: output/viz.svg + sub-name: + description: Azure subscription name + required: true + default: Pay-As-You-Go + theme: + description: Graph theme (dark, light, neon) + required: true + default: neon + depth: + description: Level of Azure Resource Sub-category to be included in vizualization (1 or 2) + required: true + default: '1' + verbosity: + description: Level of information to included in vizualization (1 or 2) + required: true + default: '1' + format: + description: Graph format (png or svg) + required: true + default: svg + direction: + description: Direction in which resource groups are plotted on the visualization (left-to-right or top-to-bottom) + required: true + default: top-to-bottom + exclude-types: + description: Exclude resources via string search + required: true + default: '*excludethisthing1,excludethisthing2*' + splines: + description: Controls how edges appear in visualization. ('spline', 'polyline', 'curved', 'ortho', 'line') + required: true + default: spline + +jobs: + generate-viz: + runs-on: ubuntu-latest + steps: + - name: Login to Azure + uses: azure/login@v1 + with: + creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} + enable-AzPSSession: true + - uses: JosiahSiegel/AzViz-action@v1.0.3 + with: + resource-group: ${{ github.event.inputs.resource-group }} + out-file: ${{ github.event.inputs.out-file }} + sub-name: ${{ github.event.inputs.sub-name }} + theme: ${{ github.event.inputs.theme }} + depth: ${{ github.event.inputs.depth }} + verbosity: ${{ github.event.inputs.verbosity }} + format: ${{ github.event.inputs.format }} + direction: ${{ github.event.inputs.direction }} + exclude-types: ${{ github.event.inputs.exclude-types }} + splines: ${{ github.event.inputs.splines }} + - uses: actions/upload-artifact@v2 + with: + name: viz + path: output/* diff --git a/.github/actions/azviz-action/.github/workflows/sample_min_workflow.yml b/.github/actions/azviz-action/.github/workflows/sample_min_workflow.yml new file mode 100644 index 00000000000..c7818681828 --- /dev/null +++ b/.github/actions/azviz-action/.github/workflows/sample_min_workflow.yml @@ -0,0 +1,35 @@ +name: Manually triggered min workflow +on: + workflow_dispatch: + inputs: + resource-group: + description: Comma-seperated resource group list + required: true + default: test1-rg,test2-rg + out-file: + description: Graph export path + required: true + default: output/viz.svg + sub-name: + description: Azure subscription name + required: true + default: Pay-As-You-Go + +jobs: + generate-viz: + runs-on: ubuntu-latest + steps: + - name: Login to Azure + uses: azure/login@v1 + with: + creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} + enable-AzPSSession: true + - uses: JosiahSiegel/AzViz-action@v1.0.3 + with: + resource-group: ${{ github.event.inputs.resource-group }} + out-file: ${{ github.event.inputs.out-file }} + sub-name: ${{ github.event.inputs.sub-name }} + - uses: actions/upload-artifact@v2 + with: + name: viz + path: output/* diff --git a/.github/actions/azviz-action/.github/workflows/test_linux_runner.yml b/.github/actions/azviz-action/.github/workflows/test_linux_runner.yml new file mode 100644 index 00000000000..3300a863551 --- /dev/null +++ b/.github/actions/azviz-action/.github/workflows/test_linux_runner.yml @@ -0,0 +1,31 @@ +name: Linux runner +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + generate-viz: + runs-on: ubuntu-latest + steps: + - name: Login to Azure + uses: azure/login@v1 + with: + creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} + enable-AzPSSession: true + - name: Check out repository + uses: actions/checkout@v2 + - name: Use local my-action + uses: ./ + with: + resource-group: ${{ secrets.TEST_RG }} + out-file: output/viz.svg + sub-name: Pay-As-You-Go + format: svg + - uses: actions/upload-artifact@v2 + with: + name: viz + path: output/* diff --git a/.github/actions/azviz-action/.github/workflows/test_windows_runner.yml b/.github/actions/azviz-action/.github/workflows/test_windows_runner.yml new file mode 100644 index 00000000000..865ac2fbfd8 --- /dev/null +++ b/.github/actions/azviz-action/.github/workflows/test_windows_runner.yml @@ -0,0 +1,31 @@ +name: Windows runner +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + generate-viz: + runs-on: windows-latest + steps: + - name: Login to Azure + uses: azure/login@v1 + with: + creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} + enable-AzPSSession: true + - name: Check out repository + uses: actions/checkout@v2 + - name: Use local my-action + uses: ./ + with: + resource-group: ${{ secrets.TEST_RG }} + out-file: output/viz.svg + sub-name: Pay-As-You-Go + format: svg + - uses: actions/upload-artifact@v2 + with: + name: viz + path: output/* diff --git a/.github/actions/azviz-action/README.md b/.github/actions/azviz-action/README.md new file mode 100644 index 00000000000..b558fdeaa9c --- /dev/null +++ b/.github/actions/azviz-action/README.md @@ -0,0 +1,93 @@ +# AzViz (Azure Visualizer) action + + +[](https://github.com/JosiahSiegel/AzViz-action/actions/workflows/test_linux_runner.yml) +[](https://github.com/JosiahSiegel/AzViz-action/actions/workflows/test_windows_runner.yml) + +## ☕ Please donate to [AzViz Developer](https://github.com/PrateekKumarSingh/AzViz#readme) + + + +## Synopsis + +[AzViz](https://github.com/PrateekKumarSingh/AzViz) for [GitHub actions](https://github.com/marketplace?type=actions)! + +## Inputs + +### Required + +```yml +inputs: + resource-group: + description: Comma-seperated resource group list + required: true + out-file: + description: Graph export path + required: true + default: output/viz.svg + sub-name: + description: Azure subscription name + required: true + default: Pay-As-You-Go +``` + +### Optional + +```yml + theme: + description: Graph theme (dark, light, neon) + required: false + default: neon + depth: + description: Level of Azure Resource Sub-category to be included in vizualization (1 or 2) + required: false + default: '1' + verbosity: + description: Level of information to included in vizualization (1 or 2) + required: false + default: '1' + format: + description: Graph format (png or svg) + required: false + default: svg + direction: + description: Direction in which resource groups are plotted on the visualization (left-to-right or top-to-bottom) + required: false + default: top-to-bottom + exclude-types: + description: Exclude resources via string search + required: false + default: '*excludethisthing1,excludethisthing2*' + splines: + description: Controls how edges appear in visualization. ('spline', 'polyline', 'curved', 'ortho', 'line') + required: false + default: spline +``` + +## Quick start + +`sample_min_workflow.yml` +```yml +jobs: + generate-viz: + runs-on: ubuntu-latest + steps: + - name: Login to Azure + uses: azure/login@v1 + with: + creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} + enable-AzPSSession: true + - uses: JosiahSiegel/AzViz-action@v1.0.3 + with: + resource-group: ${{ github.event.inputs.resource-group }} + out-file: ${{ github.event.inputs.out-file }} + sub-name: ${{ github.event.inputs.sub-name }} + - uses: actions/upload-artifact@v2 + with: + name: viz + path: output/* +``` + +## Dependencies + + * [azure/login](https://github.com/marketplace/actions/azure-login) with `enable-AzPSSession: true` \ No newline at end of file diff --git a/.github/actions/azviz-action/action.yml b/.github/actions/azviz-action/action.yml new file mode 100644 index 00000000000..0178f26fcaf --- /dev/null +++ b/.github/actions/azviz-action/action.yml @@ -0,0 +1,83 @@ +# action.yml +name: 'Generate Azure resource topology diagrams with AzViz (Azure Visualizer)' +description: 'Run AzViz against one or more Azure Resource Groups' +branding: + icon: 'download-cloud' + color: 'blue' +inputs: + resource-group: + description: Comma-seperated resource group list + required: true + out-file: + description: Graph export path + required: true + default: viz.svg + sub-name: + description: Azure subscription name + required: true + default: Pay-As-You-Go + theme: + description: Graph theme (dark, light, neon) + required: false + default: neon + depth: + description: Level of Azure Resource Sub-category to be included in vizualization (1 or 2) + required: false + default: '1' + verbosity: + description: Level of information to included in vizualization (1 or 2) + required: false + default: '1' + format: + description: Graph format (png or svg) + required: true + default: svg + direction: + description: Direction in which resource groups are plotted on the visualization (left-to-right or top-to-bottom) + required: false + default: top-to-bottom + exclude-types: + description: Exclude resources via string search + required: false + default: '*excludethisthing1,excludethisthing2*' + splines: + description: Controls how edges appear in visualization. ('spline', 'polyline', 'curved', 'ortho', 'line') + required: false + default: spline + +runs: + using: "composite" + steps: + - name: Choco install graphviz + if: runner.os == 'Windows' + uses: crazy-max/ghaction-chocolatey@v1 + with: + args: install graphviz + - name: Apt-get install graphviz + if: runner.os != 'Windows' + run: | + sudo apt-get update; + sudo apt-get install graphviz -y; + shell: bash + - name: 'Install AzViz module' + shell: pwsh + run: | + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; + Install-Module -Name AzViz -AllowClobber -Confirm:$False -Force; + Import-Module AzViz; + - name: Run AzViz + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + ${{ github.action_path }}/viz_run.ps1 ` + -RESOURCE_GROUP '${{ inputs.resource-group }}' ` + -OUT_FILE '${{ inputs.out-file }}' ` + -SUB_NAME '${{ inputs.sub-name }}' ` + -THEME '${{ inputs.theme }}' ` + -DEPTH ${{ inputs.depth }} ` + -VERBOSITY ${{ inputs.verbosity }} ` + -FORMAT '${{ inputs.format }}' ` + -DIRECTION '${{ inputs.direction }}' ` + -EXCLUDE_TYPES '${{ inputs.exclude-types }}' ` + -SPLINES '${{ inputs.splines }}' diff --git a/.github/actions/azviz-action/viz_run.ps1 b/.github/actions/azviz-action/viz_run.ps1 new file mode 100644 index 00000000000..87071186628 --- /dev/null +++ b/.github/actions/azviz-action/viz_run.ps1 @@ -0,0 +1,57 @@ +Param( + [Parameter(Mandatory)] + [String]$RESOURCE_GROUP, + [Parameter(Mandatory)] + [String]$OUT_FILE = 'viz.svg', + [Parameter(Mandatory)] + [String]$SUB_NAME = 'Pay-As-You-Go', + [Parameter(Mandatory)] + [String]$THEME = 'neon', + [Parameter(Mandatory)] + [String]$DEPTH = '1', + [Parameter(Mandatory)] + [String]$VERBOSITY = '1', + [Parameter(Mandatory)] + [String]$FORMAT = 'svg', + [Parameter(Mandatory)] + [String]$DIRECTION = 'top-to-bottom', + [String]$EXCLUDE_TYPES = '*excludethisthing1,excludethisthing2*', + [Parameter(Mandatory)] + [String]$SPLINES = 'spline' +) + +# Create missing directory paths for output +New-Item -ItemType File -Force -Path ${OUT_FILE} + +# Get current Azure context +$currentAzureContext = Get-AzContext; + +# Check If Azure context exists +if ($currentAzureContext.Tenant.TenantId) { + + # Set Azure subscription to match SUB_NAME + Set-AzContext -SubscriptionName ${SUB_NAME}; +}; + +# Run AzViz and export Azure diagram to location OUT_FILE +Export-AzViz ` + -ResourceGroup ${RESOURCE_GROUP}.Split(",") ` + -Theme ${THEME} ` + -OutputFormat ${FORMAT} ` + -CategoryDepth ${DEPTH} ` + -LabelVerbosity ${VERBOSITY} ` + -ExcludeTypes ${EXCLUDE_TYPES}.Split(",") ` + -Splines ${SPLINES} ` + -Direction ${DIRECTION} ` + -OutputFilePath ${OUT_FILE}; + +if (${FORMAT} -eq 'svg') { + + # Move svg embedded png to output directory + ((Get-Content -path ${OUT_FILE} -Raw) -replace '(?<=xlink:href\=").+?(?=icons)','') | Set-Content -Path ${OUT_FILE} + $ICON_PATH=$(Split-Path -Path ${OUT_FILE})+'/icons/' + Write-Host "Moving ${HOME}/*/AzViz/* icons to ${ICON_PATH}" + New-Item -ItemType Directory -Force -Path ${ICON_PATH} + Get-Childitem -Path ${HOME} -Force -recurse -include *.png -ErrorAction SilentlyContinue | Move-Item -dest ${ICON_PATH} -Force + +}; \ No newline at end of file From bf62adfbbedb7f23aab6c01cd18e259b2d7bb62c Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 06:50:41 -0700 Subject: [PATCH 06/32] Importing JosiahSiegel's checksum-validate-action GitHub Action --- .../.github/dependabot.yml | 13 ++ .../.github/workflows/test_action.yml | 119 ++++++++++++++++++ .../checksum-validate-action/README.md | 94 ++++++++++++++ .../checksum-validate-action/action.yml | 111 ++++++++++++++++ 4 files changed, 337 insertions(+) create mode 100644 .github/actions/checksum-validate-action/.github/dependabot.yml create mode 100644 .github/actions/checksum-validate-action/.github/workflows/test_action.yml create mode 100644 .github/actions/checksum-validate-action/README.md create mode 100644 .github/actions/checksum-validate-action/action.yml diff --git a/.github/actions/checksum-validate-action/.github/dependabot.yml b/.github/actions/checksum-validate-action/.github/dependabot.yml new file mode 100644 index 00000000000..b2f16c9faef --- /dev/null +++ b/.github/actions/checksum-validate-action/.github/dependabot.yml @@ -0,0 +1,13 @@ +# Dependabot general documentation: +# https://docs.github.com/en/code-security/dependabot +# Please see the documentation for all configuration options: +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + # GitHub Actions workflows + - package-ecosystem: "github-actions" + # Workflow files stored in `.github/workflows` + directory: "/" + schedule: + interval: "daily" diff --git a/.github/actions/checksum-validate-action/.github/workflows/test_action.yml b/.github/actions/checksum-validate-action/.github/workflows/test_action.yml new file mode 100644 index 00000000000..078b0964c09 --- /dev/null +++ b/.github/actions/checksum-validate-action/.github/workflows/test_action.yml @@ -0,0 +1,119 @@ +name: Test Action + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + generate-checksums: + name: Test generate checksum on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v4.1.1 + + - name: Generate checksum of string + uses: ./ + with: + key: test string + input: hello world + + - name: Generate checksum of command output + uses: ./ + with: + key: test command + input: $(cat action.yml) + + validate-checksums: + name: Test validate checksum on ${{ matrix.os }} + needs: + - generate-checksums + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v4.1.1 + + - name: Validate checksum of valid string + id: valid-string + uses: ./ + with: + key: test string + validate: true + fail-invalid: true + input: hello world + - name: Fail if output of valid string is wrong + if: steps.valid-string.outputs.valid != 'true' + run: exit 1 + + - name: Validate checksum of INVALID string + id: invalid-string + uses: ./ + with: + key: test string + validate: true + fail-invalid: false + input: hello world! + - name: Fail if output of INVALID string is wrong + if: steps.invalid-string.outputs.valid != 'false' + run: exit 1 + + - name: Validate checksum of valid command output + id: valid-command + uses: ./ + with: + key: test command + validate: true + fail-invalid: true + input: $(cat action.yml) + - name: Fail if output of valid command is wrong + if: steps.valid-command.outputs.valid != 'true' + run: exit 1 + + - name: Validate checksum of INVALID command output + id: invalid-command + uses: ./ + with: + key: test command + validate: true + fail-invalid: false + input: $(cat README.md) + - name: Fail if output of INVALID command is wrong + if: steps.invalid-command.outputs.valid != 'false' + run: exit 1 + + validate-checksum-failures: + name: Test checksum failures on ${{ matrix.os }} + needs: + - generate-checksums + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v4.1.1 + + - name: Validate checksum of INVALID string + continue-on-error: true + uses: ./ + with: + key: test string + validate: true + fail-invalid: true + input: hello world! + + - name: Validate checksum of INVALID command output + continue-on-error: true + uses: ./ + with: + key: test command + validate: true + fail-invalid: true + input: $(cat README.md) diff --git a/.github/actions/checksum-validate-action/README.md b/.github/actions/checksum-validate-action/README.md new file mode 100644 index 00000000000..e6e62a4caaa --- /dev/null +++ b/.github/actions/checksum-validate-action/README.md @@ -0,0 +1,94 @@ +# Checksum Validate Action + +[](https://github.com/JosiahSiegel/checksum-validate-action/actions/workflows/test_action.yml) + +## Synopsis + +1. Generate a checksum from either a string or shell command (use command substitution: `$()`). +2. Validate if checksum is identical to input (even across multiple jobs), using a `key` to link the validation attempt with the correct generated checksum. + * Validation is possible across jobs since the checksum is uploaded as a workflow artifact + +## Usage + +```yml +jobs: + generate-checksums: + name: Generate checksum + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.1 + + - name: Generate checksum of string + uses: JosiahSiegel/checksum-validate-action@v1 + with: + key: test string + input: hello world + + - name: Generate checksum of command output + uses: JosiahSiegel/checksum-validate-action@v1 + with: + key: test command + input: $(cat action.yml) + + validate-checksums: + name: Validate checksum + needs: + - generate-checksums + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.1 + + - name: Validate checksum of valid string + id: valid-string + uses: JosiahSiegel/checksum-validate-action@v1 + with: + key: test string + validate: true + fail-invalid: true + input: hello world + + - name: Validate checksum of valid command output + id: valid-command + uses: JosiahSiegel/checksum-validate-action@v1 + with: + key: test command + validate: true + fail-invalid: true + input: $(cat action.yml) + + - name: Get outputs + run: | + echo ${{ steps.valid-string.outputs.valid }} + echo ${{ steps.valid-command.outputs.valid }} +``` + +## Workflow summary + +### ✅ test string checksum valid ✅ + +### ❌ test string checksum INVALID ❌ + +## Inputs + +```yml +inputs: + validate: + description: Check if checksums match + default: false + key: + description: String to keep unique checksums separate + required: true + fail-invalid: + description: Fail step if invalid checksum + default: false + input: + description: String or command for checksum + required: true +``` + +## Outputs +```yml +outputs: + valid: + description: True if checksums match +``` \ No newline at end of file diff --git a/.github/actions/checksum-validate-action/action.yml b/.github/actions/checksum-validate-action/action.yml new file mode 100644 index 00000000000..1ad3023476e --- /dev/null +++ b/.github/actions/checksum-validate-action/action.yml @@ -0,0 +1,111 @@ +# action.yml +name: Checksum Validate Action +description: Generate and validate checksums +branding: + icon: 'lock' + color: 'orange' +inputs: + validate: + description: Check if checksums match + default: false + key: + description: String to keep unique checksums separate + required: true + fail-invalid: + description: Fail step if invalid checksum + default: false + input: + description: String or command for checksum + required: true +outputs: + valid: + description: True if checksums match + value: ${{ steps.validate_checksum.outputs.valid }} + +runs: + using: "composite" + steps: + + # CHECKSUM START + - name: Generate SHA + uses: nick-fields/retry@v3.0.0 + with: + max_attempts: 5 + retry_on: any + timeout_seconds: 10 + retry_wait_seconds: 15 + command: | + function fail { + printf '%s\n' "$1" >&2 + exit "${2-1}" + } + input_cmd="${{ inputs.input }}" || fail + sha="$(echo "$input_cmd" | sha256sum)" + echo "sha=$sha" >> $GITHUB_ENV + echo "success=true" >> $GITHUB_ENV + + - name: Get input SHA + if: env.success + id: input_sha + shell: bash + run: echo "sha=${{ env.sha }}" >> $GITHUB_OUTPUT + + - name: Get input SHA + if: env.success != 'true' + shell: bash + run: | + echo "failed to generate sha" + exit 1 + # CHECKSUM END + + # UPLOAD FILE START + - name: Create checksum file + if: inputs.validate != 'true' + shell: bash + run: | + echo "${{ steps.input_sha.outputs.sha }}" > "${{ github.sha }}-${{ inputs.key }}.txt" + + - name: Upload checksum file + if: inputs.validate != 'true' + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 + with: + name: "${{ github.sha }}-${{ inputs.key }}.txt" + path: "${{ github.sha }}-${{ inputs.key }}.txt" + retention-days: 5 + # UPLOAD FILE END + + # VALIDATE FILE START + - name: Download checksum file + if: inputs.validate == 'true' + uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe + with: + name: "${{ github.sha }}-${{ inputs.key }}.txt" + + - name: Validate pre and post checksums + if: inputs.validate == 'true' + id: validate_checksum + shell: bash + run: | + echo "${{ steps.input_sha.outputs.sha }}" > "${{ github.sha }}-${{ inputs.key }}-2.txt" + DIFF=$(diff -q "${{ github.sha }}-${{ inputs.key }}-2.txt" "${{ github.sha }}-${{ inputs.key }}.txt") || true + codevalid=true + if [ "$DIFF" != "" ] + then + codevalid=false + fi + echo "valid=$codevalid" >> $GITHUB_OUTPUT + + - name: Create summary + if: inputs.validate == 'true' + run: | + # Use ternary operator to assign emoji based on validity + emoji=${{ steps.validate_checksum.outputs.valid == 'true' && '✅' || '❌' }} + valid=${{ steps.validate_checksum.outputs.valid == 'true' && 'valid' || 'INVALID' }} + echo "### $emoji ${{ inputs.key }} checksum $valid $emoji" >> $GITHUB_STEP_SUMMARY + shell: bash + # VALIDATE FILE END + + - name: Fail if invalid checksum + if: inputs.validate == 'true' && steps.validate_checksum.outputs.valid == 'false' && inputs.fail-invalid == 'true' + run: exit 1 + shell: bash From 723e791ef5733f51041a4576c4d62f3d2a8edfa6 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 06:53:05 -0700 Subject: [PATCH 07/32] Importing JosiahSiegel's randomrepo GitHub Action --- .../randomrepo/.github/workflows/main.yml | 19 +++++++++++++++ .github/actions/randomrepo/Dockerfile | 13 ++++++++++ .github/actions/randomrepo/README.md | 15 ++++++++++++ .github/actions/randomrepo/action.yml | 18 ++++++++++++++ .github/actions/randomrepo/entrypoint.sh | 24 +++++++++++++++++++ .github/actions/randomrepo/lib/install.sh | 8 +++++++ 6 files changed, 97 insertions(+) create mode 100644 .github/actions/randomrepo/.github/workflows/main.yml create mode 100644 .github/actions/randomrepo/Dockerfile create mode 100644 .github/actions/randomrepo/README.md create mode 100644 .github/actions/randomrepo/action.yml create mode 100644 .github/actions/randomrepo/entrypoint.sh create mode 100644 .github/actions/randomrepo/lib/install.sh diff --git a/.github/actions/randomrepo/.github/workflows/main.yml b/.github/actions/randomrepo/.github/workflows/main.yml new file mode 100644 index 00000000000..1888987d707 --- /dev/null +++ b/.github/actions/randomrepo/.github/workflows/main.yml @@ -0,0 +1,19 @@ +name: Fetch Random Repo Name + +on: [push] + +jobs: + test_run: + runs-on: ubuntu-latest + name: A job to fetch a random repo name + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Fetch random repo name + uses: ./ + id: random + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Get the repo name + run: | + echo "Random repo name: ${{ steps.random.outputs.repo }}" diff --git a/.github/actions/randomrepo/Dockerfile b/.github/actions/randomrepo/Dockerfile new file mode 100644 index 00000000000..f2cefc6b1a2 --- /dev/null +++ b/.github/actions/randomrepo/Dockerfile @@ -0,0 +1,13 @@ +# Container image that runs your code +FROM curlimages/curl + +# Install GitHub CLI +USER root +COPY /lib/install.sh /lib/install.sh +RUN ["sh", "/lib/install.sh"] + +# Copies your code file from your action repository to the filesystem path `/` of the container +COPY entrypoint.sh /entrypoint.sh + +# Code file to execute when the docker container starts up (`entrypoint.sh`) +ENTRYPOINT ["sh", "/entrypoint.sh"] \ No newline at end of file diff --git a/.github/actions/randomrepo/README.md b/.github/actions/randomrepo/README.md new file mode 100644 index 00000000000..e4813fe7fca --- /dev/null +++ b/.github/actions/randomrepo/README.md @@ -0,0 +1,15 @@ +# Fetch random repo name + +This action is useful for running actions against random repositories (e.g. leak scanner, etc.) + +## Inputs + +`github-token` + +**Required** Token used to authenticate with GitHub + +## Outputs + +`repo` + +The name of the random repo. diff --git a/.github/actions/randomrepo/action.yml b/.github/actions/randomrepo/action.yml new file mode 100644 index 00000000000..537a98e6542 --- /dev/null +++ b/.github/actions/randomrepo/action.yml @@ -0,0 +1,18 @@ +# action.yml +name: 'randomrepo-action' +description: 'Fetch name of a random GitHub repository' +branding: + icon: 'search' + color: 'yellow' +inputs: + github-token: + description: 'Token used to login to GitHub' + required: true +outputs: + repo: + description: 'Name of random repo' +runs: + using: 'docker' + image: 'Dockerfile' + args: + - ${{ inputs.github-token }} diff --git a/.github/actions/randomrepo/entrypoint.sh b/.github/actions/randomrepo/entrypoint.sh new file mode 100644 index 00000000000..79d8fc7b128 --- /dev/null +++ b/.github/actions/randomrepo/entrypoint.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +echo "$1" >auth.txt +gh auth login --with-token <auth.txt +if [[ $? -ne 0 ]]; then + echo "GitHub auth failed" + exit 1 +fi + +random=$(< /dev/urandom tr -dc A-Za-z0-9_ | head -c1) + +repo=$(gh search repos $random \ +--sort updated \ +--order desc \ +--visibility public \ +--limit 1 \ +--include-forks false \ +--stars=">=10" \ +--size=">=500" \ +--json fullName,updatedAt,url,pushedAt \ +--jq '.[].fullName') + +echo "repo=$repo" >> $GITHUB_OUTPUT +echo $repo diff --git a/.github/actions/randomrepo/lib/install.sh b/.github/actions/randomrepo/lib/install.sh new file mode 100644 index 00000000000..fb5b051cba1 --- /dev/null +++ b/.github/actions/randomrepo/lib/install.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Install latest GitHub CLI +VERSION=`curl "https://api.github.com/repos/cli/cli/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/' | cut -c2-` +echo $VERSION +curl -sSL https://github.com/cli/cli/releases/download/v${VERSION}/gh_${VERSION}_linux_amd64.tar.gz -o gh_${VERSION}_linux_amd64.tar.gz +tar xvf gh_${VERSION}_linux_amd64.tar.gz +cp gh_${VERSION}_linux_amd64/bin/gh /usr/local/bin/ From 6bc4b2962179c9e42cbdd7517bff2fde42e0cd9c Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 06:53:58 -0700 Subject: [PATCH 08/32] Importing JosiahSiegel's reliable-pull-request-action GitHub Action --- .../.github/workflows/test-action.yml | 35 ++++++++ .../reliable-pull-request-action/LICENSE | 21 +++++ .../reliable-pull-request-action/README.md | 83 +++++++++++++++++++ .../reliable-pull-request-action/action.yml | 42 ++++++++++ .../reliable-pull-request-action/create-pr.sh | 16 ++++ 5 files changed, 197 insertions(+) create mode 100644 .github/actions/reliable-pull-request-action/.github/workflows/test-action.yml create mode 100644 .github/actions/reliable-pull-request-action/LICENSE create mode 100644 .github/actions/reliable-pull-request-action/README.md create mode 100644 .github/actions/reliable-pull-request-action/action.yml create mode 100644 .github/actions/reliable-pull-request-action/create-pr.sh diff --git a/.github/actions/reliable-pull-request-action/.github/workflows/test-action.yml b/.github/actions/reliable-pull-request-action/.github/workflows/test-action.yml new file mode 100644 index 00000000000..9d09461dbd3 --- /dev/null +++ b/.github/actions/reliable-pull-request-action/.github/workflows/test-action.yml @@ -0,0 +1,35 @@ +name: Test Action + +on: + push: + branches-ignore: + - 'main' + +jobs: + test-action: + name: Test create PR on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + steps: + - name: Checkout the repo + uses: actions/checkout@v4.1.1 + + - name: Create Pull Request + id: create_pr + uses: ./ + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + title: 'Automated Pull Request' + sourceBranch: ${{ github.ref_name }} + targetBranch: 'main' + body: | + ## Merge ${{ github.ref_name }} + This PR contains the changes from ${{ github.ref_name }}. + labels: 'invalid,wontfix' + assignees: ${{ github.actor }} + + - name: Output PR URL + run: echo "The PR URL is ${{ steps.create_pr.outputs.PRURL }}" diff --git a/.github/actions/reliable-pull-request-action/LICENSE b/.github/actions/reliable-pull-request-action/LICENSE new file mode 100644 index 00000000000..ade79f7960a --- /dev/null +++ b/.github/actions/reliable-pull-request-action/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Josiah Siegel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.github/actions/reliable-pull-request-action/README.md b/.github/actions/reliable-pull-request-action/README.md new file mode 100644 index 00000000000..e9af804ee4a --- /dev/null +++ b/.github/actions/reliable-pull-request-action/README.md @@ -0,0 +1,83 @@ +# Reliable* Pull Request Action + +> *Only uses built-in GitHub runner commands + +[](https://github.com/JosiahSiegel/reliable-pull-request-action/actions/workflows/test-action.yml) + +## Synopsis + +1. Create a pull request on a GitHub repository using existing branches. +2. [actions/checkout](https://github.com/actions/checkout) determins the active repo. + +## Usage + +```yml +jobs: + create-pr: + name: Test create PR on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + steps: + - name: Checkout the repo + uses: actions/checkout@v4.1.1 + + - name: Create Pull Request + id: create_pr + uses: JosiahSiegel/reliable-pull-request-action@v1.0.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + title: 'Automated Pull Request' + sourceBranch: ${{ github.ref_name }} + targetBranch: 'main' + body: 'This is an automated pull request.' + labels: 'automated,pr' + assignees: 'octocat' + + - name: Output PR URL + run: echo "The PR URL is ${{ steps.create_pr.outputs.PRURL }}" +``` + +## Inputs + +```yml +inputs: + title: + description: 'Pull Request Title' + required: true + sourceBranch: + description: 'Source Branch Name' + required: true + targetBranch: + description: 'Target Branch Name' + required: true + body: + description: 'Pull Request Body' + required: false + labels: + description: 'Labels (comma-separated)' + required: false + assignees: + description: 'Assignees (comma-separated)' + required: false +``` + +## Outputs +```yml +outputs: + PRURL: + description: 'The URL of the created pull request' +``` + +## Requirements + +The following permissions must be set for the repository: + * `Settings > Actions > General` + * Workflow permissions + 1. Read and write permissions + 2. Allow GitHub Actions to create and approve pull requests + 3. Save + +>*Alternative is to set [jobs.<job_id>.permissions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idpermissions)* diff --git a/.github/actions/reliable-pull-request-action/action.yml b/.github/actions/reliable-pull-request-action/action.yml new file mode 100644 index 00000000000..4fcda57b318 --- /dev/null +++ b/.github/actions/reliable-pull-request-action/action.yml @@ -0,0 +1,42 @@ +name: Reliable Pull Request Action +description: Creates a pull request on a GitHub repository using existing branches +branding: + icon: 'git-pull-request' + color: 'blue' +inputs: + title: + description: 'Pull Request Title' + required: true + sourceBranch: + description: 'Source Branch Name' + required: true + targetBranch: + description: 'Target Branch Name' + required: true + body: + description: 'Pull Request Body' + required: false + labels: + description: 'Labels (comma-separated)' + required: false + assignees: + description: 'Assignees (comma-separated)' + required: false +outputs: + PRURL: + description: 'The URL of the created pull request' + value: ${{ steps.create_pr.outputs.PR_URL }} +runs: + using: 'composite' + steps: + - name: Create Pull Request + id: create_pr + shell: bash + run: bash ${{github.action_path}}/create-pr.sh + env: + INPUT_TITLE: ${{ inputs.title }} + INPUT_SOURCEBRANCH: ${{ inputs.sourceBranch }} + INPUT_TARGETBRANCH: ${{ inputs.targetBranch }} + INPUT_BODY: ${{ inputs.body }} + INPUT_LABELS: ${{ inputs.labels }} + INPUT_ASSIGNEES: ${{ inputs.assignees }} diff --git a/.github/actions/reliable-pull-request-action/create-pr.sh b/.github/actions/reliable-pull-request-action/create-pr.sh new file mode 100644 index 00000000000..c6046c0582d --- /dev/null +++ b/.github/actions/reliable-pull-request-action/create-pr.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Create Pull Request and capture the output +PR_OUTPUT=$(gh pr create \ + --title "$INPUT_TITLE" \ + --body "$INPUT_BODY" \ + --base "$INPUT_TARGETBRANCH" \ + --head "$INPUT_SOURCEBRANCH" \ + --label "$INPUT_LABELS" \ + --assignee "$INPUT_ASSIGNEES" 2>&1) + +# Extract PR URL from the output +PR_URL=$(echo "$PR_OUTPUT" | grep -o 'https://github.com/[^ ]*') + +# Set the PR URL as the output +echo "PR_URL=$PR_URL" >> $GITHUB_OUTPUT From c6865d3b8e307ec44e7ee9b97d2bdd9ae348af38 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 06:54:48 -0700 Subject: [PATCH 09/32] Importing JosiahSiegel's remote-branch-action GitHub Action --- .../.github/workflows/test-action.yml | 49 ++++++++ .github/actions/remote-branch-action/LICENSE | 21 ++++ .../actions/remote-branch-action/README.md | 108 ++++++++++++++++++ .../actions/remote-branch-action/action.yml | 61 ++++++++++ 4 files changed, 239 insertions(+) create mode 100644 .github/actions/remote-branch-action/.github/workflows/test-action.yml create mode 100644 .github/actions/remote-branch-action/LICENSE create mode 100644 .github/actions/remote-branch-action/README.md create mode 100644 .github/actions/remote-branch-action/action.yml diff --git a/.github/actions/remote-branch-action/.github/workflows/test-action.yml b/.github/actions/remote-branch-action/.github/workflows/test-action.yml new file mode 100644 index 00000000000..1a0e4c25e1e --- /dev/null +++ b/.github/actions/remote-branch-action/.github/workflows/test-action.yml @@ -0,0 +1,49 @@ +name: Test Action + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test-action: + name: Test create branch on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Checkout second repo + uses: actions/checkout@v4 + with: + sparse-checkout: . + repository: josiahsiegel/rapid-wsl + token: ${{ secrets.SECONDARY_REPO_TOKEN }} + path: second-repo + + - name: Test action + id: test-action + uses: ./ + with: + branch: test-action + + - name: Test action on second repo + id: test-action-second-repo + uses: ./ + with: + branch: test-action-second-repo + path: second-repo + + - name: Get create branch status + if: steps.test-action.outputs.create-status != 'false' + run: echo ${{ steps.test-action.outputs.create-status }} + + - name: Get create branch status on second repo + if: steps.test-action-second-repo.outputs.create-status != 'false' + run: echo ${{ steps.test-action-second-repo.outputs.create-status }} diff --git a/.github/actions/remote-branch-action/LICENSE b/.github/actions/remote-branch-action/LICENSE new file mode 100644 index 00000000000..ade79f7960a --- /dev/null +++ b/.github/actions/remote-branch-action/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Josiah Siegel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.github/actions/remote-branch-action/README.md b/.github/actions/remote-branch-action/README.md new file mode 100644 index 00000000000..8592ac8b5b4 --- /dev/null +++ b/.github/actions/remote-branch-action/README.md @@ -0,0 +1,108 @@ +# Remote Branch Action + +[](https://github.com/JosiahSiegel/remote-branch-action/actions/workflows/test-action.yml) + +## Synopsis + +1. Create a branch on a remote repository. +2. [actions/checkout](https://github.com/actions/checkout) determins the active repo. + +## Usage + +### Single repo +```yml +jobs: + create-branch-action: + name: Create branch + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Create branch + uses: JosiahSiegel/remote-branch-action@v1.1.0 + with: + branch: new-branch +``` +### Single alternative repo +```yml +jobs: + create-branch-action: + name: Create branch + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Checkout alt repo + uses: actions/checkout@v4 + with: + sparse-checkout: . + repository: me/alt-repo + token: ${{ secrets.ALT_REPO_TOKEN }} + path: alt-repo + + - name: Create branch on alt repo + uses: JosiahSiegel/remote-branch-action@v1.1.0 + with: + branch: new-branch-alt-repo + path: alt-repo +``` +### Multiple repos +```yml +jobs: + create-branch-action: + name: Create branch + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Checkout second repo + uses: actions/checkout@v4 + with: + sparse-checkout: . + repository: me/second-repo + token: ${{ secrets.SECONDARY_REPO_TOKEN }} + path: second-repo + + - name: Create branch + id: create-branch-action + uses: JosiahSiegel/remote-branch-action@v1.1.0 + with: + branch: new-branch + + - name: Create branch on second repo + id: create-branch-action-second-repo + uses: JosiahSiegel/remote-branch-action@v1.1.0 + with: + branch: new-branch-second-repo + path: second-repo + + - name: Get create branch status + run: echo ${{ steps.create-branch-action.outputs.create-status }} + + - name: Get create branch status on second repo + run: echo ${{ steps.create-branch-action-second-repo.outputs.create-status }} +``` + +## Inputs + +```yml +inputs: + branch: + description: Branch name + required: true + path: + description: Relative path under $GITHUB_WORKSPACE to place the repository + required: false + default: '.' +``` + +## Outputs +```yml +outputs: + create-status: + description: Branch creation status + value: ${{ steps.create-branch.outputs.create_status }} +``` \ No newline at end of file diff --git a/.github/actions/remote-branch-action/action.yml b/.github/actions/remote-branch-action/action.yml new file mode 100644 index 00000000000..215d2203407 --- /dev/null +++ b/.github/actions/remote-branch-action/action.yml @@ -0,0 +1,61 @@ +# action.yml +name: Remote Branch Action +description: Create and manage a remote branch +branding: + icon: 'git-branch' + color: 'blue' +inputs: + branch: + description: Branch name + required: true + path: + description: Relative path under $GITHUB_WORKSPACE to place the repository + required: false + default: '.' +outputs: + create-status: + description: Branch creation status + value: ${{ steps.create-branch.outputs.create_status }} + +runs: + using: "composite" + steps: + + - name: Create branch + id: create-branch + working-directory: ${{ inputs.path }} + shell: bash + run: | + # Assign the arguments to variables + branch_name=${{ inputs.branch }} + + # Create a new branch locally + git checkout -b $branch_name + + # Check if the branch exists on the remote + check_status=$(git ls-remote --heads origin $branch_name | wc -l) + + # Check if the branch does not exist on the remote + if [ $check_status -eq 0 ]; then + # Push the new branch to the remote repository using the token + git push -u origin $branch_name + + # Store the status of the push command + status=$? + + # Check if the push was successful + if [ $status -eq 0 ]; then + # Print a success message + echo "Branch $branch_name created and pushed" + else + # Print an error message + echo "Branch creation failed with status $status" + status="Branch creation failed with status $status" + fi + else + # Print a message that the branch already exists on the remote + echo "Branch $branch_name already exists" + status="Branch $branch_name already exists" + fi + + echo "create_status=$status" >> $GITHUB_OUTPUT From a933908b10e091e44117f78059e38fc376c5196b Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 06:56:04 -0700 Subject: [PATCH 10/32] Importing JosiahSiegel's slack-boltjs-app GitHub Action --- .../actions/slack-boltjs-app/.dockerignore | 1 + .github/actions/slack-boltjs-app/.env.example | 10 + .../actions/slack-boltjs-app/.eslintignore | 1 + .../actions/slack-boltjs-app/.eslintrc.cjs | 15 + .../slack-boltjs-app/.github/dependabot.yml | 17 + .../.github/workflows/codacy.yml | 61 + .../.github/workflows/codeql.yml | 74 + .../.github/workflows/confirm_push.yml | 15 + .../.github/workflows/confirm_run.yml | 19 + .../.github/workflows/dependency-review.yml | 20 + .../.github/workflows/njsscan.yml | 40 + .../.github/workflows/validate.yml | 64 + .github/actions/slack-boltjs-app/.gitignore | 3 + .../actions/slack-boltjs-app/.glitch-assets | 6 + .../actions/slack-boltjs-app/.help.example | 32 + .../slack-boltjs-app/Dockerfile.example | 11 + .github/actions/slack-boltjs-app/Makefile | 10 + .github/actions/slack-boltjs-app/README.md | 83 + .../slack-boltjs-app/npm-shrinkwrap.json | 4263 +++++++++++++++++ .github/actions/slack-boltjs-app/package.json | 27 + .github/actions/slack-boltjs-app/src/index.js | 47 + .../src/utils/github/default_branch.js | 11 + .../src/utils/github/list_targets.js | 13 + .../src/utils/github/lock_target.js | 61 + .../slack-boltjs-app/src/utils/github/push.js | 182 + .../src/utils/github/request.js | 44 + .../src/utils/github/router.js | 65 + .../src/utils/github/run_workflow.js | 43 + .../slack-boltjs-app/src/views/app_home.js | 33 + 29 files changed, 5271 insertions(+) create mode 100644 .github/actions/slack-boltjs-app/.dockerignore create mode 100644 .github/actions/slack-boltjs-app/.env.example create mode 100644 .github/actions/slack-boltjs-app/.eslintignore create mode 100644 .github/actions/slack-boltjs-app/.eslintrc.cjs create mode 100644 .github/actions/slack-boltjs-app/.github/dependabot.yml create mode 100644 .github/actions/slack-boltjs-app/.github/workflows/codacy.yml create mode 100644 .github/actions/slack-boltjs-app/.github/workflows/codeql.yml create mode 100644 .github/actions/slack-boltjs-app/.github/workflows/confirm_push.yml create mode 100644 .github/actions/slack-boltjs-app/.github/workflows/confirm_run.yml create mode 100644 .github/actions/slack-boltjs-app/.github/workflows/dependency-review.yml create mode 100644 .github/actions/slack-boltjs-app/.github/workflows/njsscan.yml create mode 100644 .github/actions/slack-boltjs-app/.github/workflows/validate.yml create mode 100644 .github/actions/slack-boltjs-app/.gitignore create mode 100644 .github/actions/slack-boltjs-app/.glitch-assets create mode 100644 .github/actions/slack-boltjs-app/.help.example create mode 100644 .github/actions/slack-boltjs-app/Dockerfile.example create mode 100644 .github/actions/slack-boltjs-app/Makefile create mode 100644 .github/actions/slack-boltjs-app/README.md create mode 100644 .github/actions/slack-boltjs-app/npm-shrinkwrap.json create mode 100644 .github/actions/slack-boltjs-app/package.json create mode 100644 .github/actions/slack-boltjs-app/src/index.js create mode 100644 .github/actions/slack-boltjs-app/src/utils/github/default_branch.js create mode 100644 .github/actions/slack-boltjs-app/src/utils/github/list_targets.js create mode 100644 .github/actions/slack-boltjs-app/src/utils/github/lock_target.js create mode 100644 .github/actions/slack-boltjs-app/src/utils/github/push.js create mode 100644 .github/actions/slack-boltjs-app/src/utils/github/request.js create mode 100644 .github/actions/slack-boltjs-app/src/utils/github/router.js create mode 100644 .github/actions/slack-boltjs-app/src/utils/github/run_workflow.js create mode 100644 .github/actions/slack-boltjs-app/src/views/app_home.js diff --git a/.github/actions/slack-boltjs-app/.dockerignore b/.github/actions/slack-boltjs-app/.dockerignore new file mode 100644 index 00000000000..2eea525d885 --- /dev/null +++ b/.github/actions/slack-boltjs-app/.dockerignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/.github/actions/slack-boltjs-app/.env.example b/.github/actions/slack-boltjs-app/.env.example new file mode 100644 index 00000000000..6f435a07baf --- /dev/null +++ b/.github/actions/slack-boltjs-app/.env.example @@ -0,0 +1,10 @@ +# App-Level Token w/connections:write +SLACK_APP_TOKEN= +# Bot User OAuth Token under the OAuth & Permissions sidebar +SLACK_BOT_TOKEN= +# https://github.com/settings/tokens +GITHUB_TOKEN= +# Only pushes to this repo if specified +GITHUB_REPO= +# Only pushes are permitted to these branches +GITHUB_TARGET_BRANCHES= \ No newline at end of file diff --git a/.github/actions/slack-boltjs-app/.eslintignore b/.github/actions/slack-boltjs-app/.eslintignore new file mode 100644 index 00000000000..3c3629e647f --- /dev/null +++ b/.github/actions/slack-boltjs-app/.eslintignore @@ -0,0 +1 @@ +node_modules diff --git a/.github/actions/slack-boltjs-app/.eslintrc.cjs b/.github/actions/slack-boltjs-app/.eslintrc.cjs new file mode 100644 index 00000000000..7a28658ef87 --- /dev/null +++ b/.github/actions/slack-boltjs-app/.eslintrc.cjs @@ -0,0 +1,15 @@ +module.exports = { + env: { + browser: false, + es2021: true, + }, + extends: ["eslint:recommended", "plugin:node/recommended"], + rules: { + "node/no-missing-import": [ + "error", + { + allowModules: ["@slack/bolt"], + }, + ], + }, +}; diff --git a/.github/actions/slack-boltjs-app/.github/dependabot.yml b/.github/actions/slack-boltjs-app/.github/dependabot.yml new file mode 100644 index 00000000000..82f4a9f460e --- /dev/null +++ b/.github/actions/slack-boltjs-app/.github/dependabot.yml @@ -0,0 +1,17 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "npm" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + - package-ecosystem: "github-actions" + # Workflow files stored in the + # default location of `.github/workflows` + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/actions/slack-boltjs-app/.github/workflows/codacy.yml b/.github/actions/slack-boltjs-app/.github/workflows/codacy.yml new file mode 100644 index 00000000000..43f560f1615 --- /dev/null +++ b/.github/actions/slack-boltjs-app/.github/workflows/codacy.yml @@ -0,0 +1,61 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# This workflow checks out code, performs a Codacy security scan +# and integrates the results with the +# GitHub Advanced Security code scanning feature. For more information on +# the Codacy security scan action usage and parameters, see +# https://github.com/codacy/codacy-analysis-cli-action. +# For more information on Codacy Analysis CLI in general, see +# https://github.com/codacy/codacy-analysis-cli. + +name: Codacy Security Scan + +on: + push: + branches: [ "main" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main" ] + schedule: + - cron: '24 4 * * 1' + +permissions: + contents: read + +jobs: + codacy-security-scan: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + name: Codacy Security Scan + runs-on: ubuntu-latest + steps: + # Checkout the repository to the GitHub Actions runner + - name: Checkout code + uses: actions/checkout@v4 + + # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis + - name: Run Codacy Analysis CLI + uses: codacy/codacy-analysis-cli-action@3ff8e64eb4b714c4bee91b7b4eea31c6fc2c4f93 + with: + # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository + # You can also omit the token and run the tools that support default configurations + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + verbose: true + output: results.sarif + format: sarif + # Adjust severity of non-security issues + gh-code-scanning-compat: true + # Force 0 exit code to allow SARIF file generation + # This will handover control about PR rejection to the GitHub side + max-allowed-issues: 2147483647 + + # Upload the SARIF file generated in the previous step + - name: Upload SARIF results file + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif diff --git a/.github/actions/slack-boltjs-app/.github/workflows/codeql.yml b/.github/actions/slack-boltjs-app/.github/workflows/codeql.yml new file mode 100644 index 00000000000..0626d41b044 --- /dev/null +++ b/.github/actions/slack-boltjs-app/.github/workflows/codeql.yml @@ -0,0 +1,74 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "main" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main" ] + +jobs: + analyze: + name: Analyze + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/actions/slack-boltjs-app/.github/workflows/confirm_push.yml b/.github/actions/slack-boltjs-app/.github/workflows/confirm_push.yml new file mode 100644 index 00000000000..6e15bf969ce --- /dev/null +++ b/.github/actions/slack-boltjs-app/.github/workflows/confirm_push.yml @@ -0,0 +1,15 @@ +name: Confirm Push + +on: + push: + branches: + - dummy + +permissions: read-all + +jobs: + confirm_push: + runs-on: ubuntu-latest + name: Validate - Push + steps: + - run: echo "Success!" diff --git a/.github/actions/slack-boltjs-app/.github/workflows/confirm_run.yml b/.github/actions/slack-boltjs-app/.github/workflows/confirm_run.yml new file mode 100644 index 00000000000..c8f5046da1f --- /dev/null +++ b/.github/actions/slack-boltjs-app/.github/workflows/confirm_run.yml @@ -0,0 +1,19 @@ +name: Confirm Run + +on: + workflow_dispatch: + inputs: + choice_name: + options: + - choice1 + - choice2 + - choice3 + type: choice + +jobs: + confirm_run: + name: "Confirm run: ${{ github.event.inputs.choice_name }}" + runs-on: ubuntu-latest + steps: + - name: Echo choice + run: echo "${{ github.event.inputs.choice_name }}"; \ No newline at end of file diff --git a/.github/actions/slack-boltjs-app/.github/workflows/dependency-review.yml b/.github/actions/slack-boltjs-app/.github/workflows/dependency-review.yml new file mode 100644 index 00000000000..0d4a01360d7 --- /dev/null +++ b/.github/actions/slack-boltjs-app/.github/workflows/dependency-review.yml @@ -0,0 +1,20 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v4 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v4 diff --git a/.github/actions/slack-boltjs-app/.github/workflows/njsscan.yml b/.github/actions/slack-boltjs-app/.github/workflows/njsscan.yml new file mode 100644 index 00000000000..f68e77f8528 --- /dev/null +++ b/.github/actions/slack-boltjs-app/.github/workflows/njsscan.yml @@ -0,0 +1,40 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# This workflow integrates njsscan with GitHub's Code Scanning feature +# nodejsscan is a static security code scanner that finds insecure code patterns in your Node.js applications + +name: njsscan sarif + +on: + push: + branches: [ "main" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main" ] + +permissions: + contents: read + +jobs: + njsscan: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + name: njsscan code scanning + steps: + - name: Checkout the code + uses: actions/checkout@v4 + - name: nodejsscan scan + id: njsscan + uses: ajinabraham/njsscan-action@d58d8b2f26322cd35a9efb8003baac517f226d81 + with: + args: '. --sarif --output results.sarif || true' + - name: Upload njsscan report + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif diff --git a/.github/actions/slack-boltjs-app/.github/workflows/validate.yml b/.github/actions/slack-boltjs-app/.github/workflows/validate.yml new file mode 100644 index 00000000000..610c317c2c3 --- /dev/null +++ b/.github/actions/slack-boltjs-app/.github/workflows/validate.yml @@ -0,0 +1,64 @@ +name: Validate + +on: + pull_request: + branches: + - main + +permissions: read-all + +jobs: + run_docker: + runs-on: ubuntu-latest + name: Run Docker + outputs: + result: ${{ steps.validate_bot.outputs.result }} + steps: + - uses: actions/checkout@v4 + - name: Build and run Docker + run: | + echo "${{ secrets.ENV }}" > .env + cp .help.example .help + make + shell: bash + - name: Validate bot + id: validate_bot + run: | + # Define a function to send a message to Slack and store the result + out=true; + + send_message() { + text=$1 + ok=$(curl -d "text=<@${{ vars.VALIDATE_BOTID }}> $text" -d "channel=${{ secrets.VALIDATE_CHANNEL }}" \ + -H "Authorization: Bearer ${{ secrets.VALIDATE_TOKEN }}" \ + -X POST https://slack.com/api/chat.postMessage | jq .ok) + + if [ "$ok" = false ] ; then + out=false; + fi + } + + # Loop through the messages and call the function with a counter + messages=(":wave:" "gh-targets" "help" "gh-run confirm_run.yml --inputs choice_name:choice2" \ + "gh-run confirm_run.yml" "gh-lock add dummy" "gh-deploy main to dummy" "gh-lock show dummy" \ + "gh-lock remove dummy" "gh-deploy main to dummy") + counter=1 + for message in "${messages[@]}"; do + send_message "$message" "$counter" + sleep 5 + ((counter++)) + done + + echo "result=$out" >> $GITHUB_OUTPUT + shell: bash + + no_fail_check: + needs: + - run_docker + runs-on: ubuntu-latest + name: Validate - No failure + steps: + - if: needs.run_docker.outputs.result == 'true' + run: echo "Success!" + - if: needs.run_docker.outputs.result != 'true' + run: exit 1 diff --git a/.github/actions/slack-boltjs-app/.gitignore b/.github/actions/slack-boltjs-app/.gitignore new file mode 100644 index 00000000000..1e268ce3f2d --- /dev/null +++ b/.github/actions/slack-boltjs-app/.gitignore @@ -0,0 +1,3 @@ +.env +.help +node_modules diff --git a/.github/actions/slack-boltjs-app/.glitch-assets b/.github/actions/slack-boltjs-app/.glitch-assets new file mode 100644 index 00000000000..954626f61ff --- /dev/null +++ b/.github/actions/slack-boltjs-app/.glitch-assets @@ -0,0 +1,6 @@ +{"name":"drag-in-files.svg","date":"2016-10-22T16:17:49.954Z","url":"https://cdn.hyperdev.com/drag-in-files.svg","type":"image/svg","size":7646,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/drag-in-files.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(102, 153, 205)","uuid":"adSBq97hhhpFNUna"} +{"name":"click-me.svg","date":"2016-10-23T16:17:49.954Z","url":"https://cdn.hyperdev.com/click-me.svg","type":"image/svg","size":7116,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/click-me.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(243, 185, 186)","uuid":"adSBq97hhhpFNUnb"} +{"name":"paste-me.svg","date":"2016-10-24T16:17:49.954Z","url":"https://cdn.hyperdev.com/paste-me.svg","type":"image/svg","size":7242,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/paste-me.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(42, 179, 185)","uuid":"adSBq97hhhpFNUnc"} +{"uuid":"adSBq97hhhpFNUna","deleted":true} +{"uuid":"adSBq97hhhpFNUnb","deleted":true} +{"uuid":"adSBq97hhhpFNUnc","deleted":true} diff --git a/.github/actions/slack-boltjs-app/.help.example b/.github/actions/slack-boltjs-app/.help.example new file mode 100644 index 00000000000..b24e6e72302 --- /dev/null +++ b/.github/actions/slack-boltjs-app/.help.example @@ -0,0 +1,32 @@ +USAGE + [<@bot>] gh-deploy [<branch>] to [<branch>] [OPTIONAL: for <owner/repo>] + +EXAMPLES + @MyBot gh-deploy master to feature3 + @MyBot gh-deploy feature1 to feature3 for my/awesome_repo + +========================================================================== + +USAGE + [<@bot>] gh-lock [add|remove|show] [<branch>] + +EXAMPLES + @MyBot gh-lock add feature3 + @MyBot gh-lock remove feature1 + +========================================================================== + +USAGE + [<@bot>] gh-run [<workflow file>] [OPTIONAL: <owner/repo> <branch>] [OPTIONAL: --inputs <a:b,c:d>] + +EXAMPLES + @MyBot gh-run log_management.yml --inputs name:alpha,desc:uat env + @MyBot gh-run log_management.yml my/awesome_repo main --inputs name:alpha,desc:uat env + +========================================================================== + +USAGE + [<@bot>] gh-targets + +EXAMPLES + @bot gh-targets diff --git a/.github/actions/slack-boltjs-app/Dockerfile.example b/.github/actions/slack-boltjs-app/Dockerfile.example new file mode 100644 index 00000000000..0bc2f1257f9 --- /dev/null +++ b/.github/actions/slack-boltjs-app/Dockerfile.example @@ -0,0 +1,11 @@ +FROM node:20-bookworm-slim + +WORKDIR /usr/app +COPY --chown=node:node ./ ./ +RUN mkdir -p ./src/.locks && chown node ./src/.locks +VOLUME /usr/app/src/.locks + +RUN npm install +USER node + +CMD ["npm", "start"] diff --git a/.github/actions/slack-boltjs-app/Makefile b/.github/actions/slack-boltjs-app/Makefile new file mode 100644 index 00000000000..032201f6d6d --- /dev/null +++ b/.github/actions/slack-boltjs-app/Makefile @@ -0,0 +1,10 @@ +default: start + +start: build + docker stop slack-boltjs-app || true + docker rm slack-boltjs-app || true + docker run --name=slack-boltjs-app --env-file .env \ + --volume gh_locks:/usr/app/src/.locks -d slack_boltjs_app + +build: + docker build -t slack_boltjs_app -f Dockerfile.example . \ No newline at end of file diff --git a/.github/actions/slack-boltjs-app/README.md b/.github/actions/slack-boltjs-app/README.md new file mode 100644 index 00000000000..1a095225b65 --- /dev/null +++ b/.github/actions/slack-boltjs-app/README.md @@ -0,0 +1,83 @@ +# Slack-Boltjs-App + +[](https://github.com/JosiahSiegel/slack-bolt/actions/workflows/codeql.yml) +[](https://github.com/JosiahSiegel/slack-bolt/actions/workflows/njsscan.yml) +[](https://app.codacy.com/gh/JosiahSiegel/slack-boltjs-app/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) + + + +<a href="https://glitch.com/edit/#!/remix/slack-boltjs-app"><img alt="Remix on Glitch" src="https://cdn.gomix.com/f3620a78-0ad3-4f81-a271-c8a4faa20f86%2Fremix-button.svg"></a> + +## A secure and simple Boltjs app for Slack ChatOps + +> App includes basic GitHub push functionality to get you started + +### Quickstart +* `cp .env.example .env` + * Update `.env` ⚠️(required)⚠️ +* `cp .help.example .help` + * Update `.help` (optional) +* `npm install` +* `npm start` + +### Helpful links + +* [Bolt getting started guide](https://api.slack.com/start/building/bolt) +* [Bolt documentation](https://slack.dev/bolt) +* [Slack app home](https://api.slack.com/apps) + +### Example Slack app manifest + +```yml +display_information: + name: Bolt DevBot +features: + app_home: + home_tab_enabled: true + messages_tab_enabled: false + messages_tab_read_only_enabled: false + bot_user: + display_name: DevBot + always_online: true +oauth_config: + scopes: + bot: + - app_mentions:read + - calls:write + - channels:read + - chat:write + - commands + - channels:history + - reactions:read +settings: + event_subscriptions: + bot_events: + - app_home_opened + - app_mention + - message.channels + - reaction_added + interactivity: + is_enabled: true + org_deploy_enabled: false + socket_mode_enabled: true + token_rotation_enabled: false +``` + +Tips: + +* For [external workspace users][1], add an `app.message` per `app.command`. +* Check vulnerabilities: `npm audit` +* Fix Glitch out of sync with repo: + * `git pull` + * `refresh` +* Hard refresh Glitch from repo: + * `git fetch --all` + * `git reset --hard origin/main` + * `refresh` + +--- + +[Glitch](https://glitch.com/~slack-boltjs-app) +[GitHub](https://github.com/JosiahSiegel/slack-boltjs-app) + +[1]: https://slack.com/help/articles/115004151203-Slack-Connect-guide--work-with-external-organizations diff --git a/.github/actions/slack-boltjs-app/npm-shrinkwrap.json b/.github/actions/slack-boltjs-app/npm-shrinkwrap.json new file mode 100644 index 00000000000..aa05bfa7ddf --- /dev/null +++ b/.github/actions/slack-boltjs-app/npm-shrinkwrap.json @@ -0,0 +1,4263 @@ +{ + "name": "slack-bolt", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "slack-bolt", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@slack/bolt": "^3.18.0", + "dotenv": "^16", + "node-localstorage": "^3.0.5" + }, + "devDependencies": { + "eslint": "9.3.0", + "eslint-config-eslint": "^10.0.0" + }, + "engines": { + "node": "^20", + "npm": "^10" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.42.0.tgz", + "integrity": "sha512-R1w57YlVA6+YE01wch3GPYn6bCsrOV3YW/5oGGE2tmX6JcL9Nr+b5IikrjMPF+v9CV3ay+obImEdsDhovhJrzw==", + "dev": true, + "dependencies": { + "comment-parser": "1.4.1", + "esquery": "^1.5.0", + "jsdoc-type-pratt-parser": "~4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@eslint-community/eslint-plugin-eslint-comments": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.3.0.tgz", + "integrity": "sha512-6e93KtgsndNkvwCCa07LOQJSwzzLLxwrFll3+huyFoiiQXWG0KBcmo0Q1bVgYQQDLfWOOZl2VPBsXqZL6vHIBQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^4.0.0", + "ignore": "^5.2.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.3.0.tgz", + "integrity": "sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@slack/bolt": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@slack/bolt/-/bolt-3.18.0.tgz", + "integrity": "sha512-A7bDi5kY50fS6/nsmURkQdO3iMxD8aX/rA+m1UXEM2ue2z4KijeQtx2sOZ4YkJQ/h7BsgTQM0CYh3qqmo+m5sQ==", + "dependencies": { + "@slack/logger": "^4.0.0", + "@slack/oauth": "^2.6.2", + "@slack/socket-mode": "^1.3.3", + "@slack/types": "^2.11.0", + "@slack/web-api": "^6.11.2", + "@types/express": "^4.16.1", + "@types/promise.allsettled": "^1.0.3", + "@types/tsscmp": "^1.0.0", + "axios": "^1.6.0", + "express": "^4.16.4", + "path-to-regexp": "^6.2.1", + "please-upgrade-node": "^3.2.0", + "promise.allsettled": "^1.0.2", + "raw-body": "^2.3.3", + "tsscmp": "^1.0.6" + }, + "engines": { + "node": ">=12.13.0", + "npm": ">=6.12.0" + } + }, + "node_modules/@slack/logger": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-4.0.0.tgz", + "integrity": "sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA==", + "dependencies": { + "@types/node": ">=18.0.0" + }, + "engines": { + "node": ">= 18", + "npm": ">= 8.6.0" + } + }, + "node_modules/@slack/oauth": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@slack/oauth/-/oauth-2.6.2.tgz", + "integrity": "sha512-2R3MyB/R63hTRXzk5J6wcui59TBxXzhk+Uh2/Xu3Wp3O4pXg/BNucQhP/DQbL/ScVhLvFtMXirLrKi0Yo5gIVw==", + "dependencies": { + "@slack/logger": "^3.0.0", + "@slack/web-api": "^6.11.2", + "@types/jsonwebtoken": "^8.3.7", + "@types/node": ">=12", + "jsonwebtoken": "^9.0.0", + "lodash.isstring": "^4.0.1" + }, + "engines": { + "node": ">=12.13.0", + "npm": ">=6.12.0" + } + }, + "node_modules/@slack/oauth/node_modules/@slack/logger": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-3.0.0.tgz", + "integrity": "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==", + "dependencies": { + "@types/node": ">=12.0.0" + }, + "engines": { + "node": ">= 12.13.0", + "npm": ">= 6.12.0" + } + }, + "node_modules/@slack/socket-mode": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@slack/socket-mode/-/socket-mode-1.3.3.tgz", + "integrity": "sha512-vN3zG4woRtf2Ut6rZgRW6G/Oe56uLMlnz39I08Q7DOvVfB+1MmDbNv0PNOiFgujdKXJR+bXF41/F/VvryXcqlw==", + "dependencies": { + "@slack/logger": "^3.0.0", + "@slack/web-api": "^6.11.2", + "@types/node": ">=12.0.0", + "@types/p-queue": "^2.3.2", + "@types/ws": "^7.4.7", + "eventemitter3": "^3.1.0", + "finity": "^0.5.4", + "p-cancelable": "^1.1.0", + "p-queue": "^2.4.2", + "ws": "^7.5.3" + }, + "engines": { + "node": ">=12.13.0", + "npm": ">=6.12.0" + } + }, + "node_modules/@slack/socket-mode/node_modules/@slack/logger": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-3.0.0.tgz", + "integrity": "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==", + "dependencies": { + "@types/node": ">=12.0.0" + }, + "engines": { + "node": ">= 12.13.0", + "npm": ">= 6.12.0" + } + }, + "node_modules/@slack/types": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@slack/types/-/types-2.11.0.tgz", + "integrity": "sha512-UlIrDWvuLaDly3QZhCPnwUSI/KYmV1N9LyhuH6EDKCRS1HWZhyTG3Ja46T3D0rYfqdltKYFXbJSSRPwZpwO0cQ==", + "engines": { + "node": ">= 12.13.0", + "npm": ">= 6.12.0" + } + }, + "node_modules/@slack/web-api": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-6.12.0.tgz", + "integrity": "sha512-RPw6F8rWfGveGkZEJ4+4jUin5iazxRK2q3FpQDz/FvdgzC3nZmPyLx8WRzc6nh0w3MBjEbphNnp2VZksfhpBIQ==", + "dependencies": { + "@slack/logger": "^3.0.0", + "@slack/types": "^2.11.0", + "@types/is-stream": "^1.1.0", + "@types/node": ">=12.0.0", + "axios": "^1.6.5", + "eventemitter3": "^3.1.0", + "form-data": "^2.5.0", + "is-electron": "2.2.2", + "is-stream": "^1.1.0", + "p-queue": "^6.6.1", + "p-retry": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0", + "npm": ">= 6.12.0" + } + }, + "node_modules/@slack/web-api/node_modules/@slack/logger": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-3.0.0.tgz", + "integrity": "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==", + "dependencies": { + "@types/node": ">=12.0.0" + }, + "engines": { + "node": ">= 12.13.0", + "npm": ">= 6.12.0" + } + }, + "node_modules/@slack/web-api/node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@slack/web-api/node_modules/p-queue/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.43", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", + "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, + "node_modules/@types/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jsonwebtoken": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", + "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "node_modules/@types/node": { + "version": "20.11.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.23.tgz", + "integrity": "sha512-ZUarKKfQuRILSNYt32FuPL20HS7XwNT7/uRwSV8tiHWfyyVwDLYZNF6DZKc2bove++pgfsXn9sUwII/OsQ82cQ==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/p-queue": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/p-queue/-/p-queue-2.3.2.tgz", + "integrity": "sha512-eKAv5Ql6k78dh3ULCsSBxX6bFNuGjTmof5Q/T6PiECDq0Yf8IIn46jCyp3RJvCi8owaEmm3DZH1PEImjBMd/vQ==" + }, + "node_modules/@types/promise.allsettled": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/promise.allsettled/-/promise.allsettled-1.0.6.tgz", + "integrity": "sha512-wA0UT0HeT2fGHzIFV9kWpYz5mdoyLxKrTgMdZQM++5h6pYAFH73HXcQhefg24nD1yivUFEn5KU+EF4b+CXJ4Wg==" + }, + "node_modules/@types/qs": { + "version": "6.9.12", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.12.tgz", + "integrity": "sha512-bZcOkJ6uWrL0Qb2NAWKa7TBU+mJHPzhx9jjLL1KHF+XpzEcR7EXHvjbHlGtR/IsP1vyPrehuS6XqkmaePy//mg==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/tsscmp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/tsscmp/-/tsscmp-1.0.2.tgz", + "integrity": "sha512-cy7BRSU8GYYgxjcx0Py+8lo5MthuDhlyu076KUcYzVNXL23luYgRHkMG2fIFEc6neckeh/ntP82mw+U4QjZq+g==" + }, + "node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array.prototype.map": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.6.tgz", + "integrity": "sha512-nK1psgF2cXqP3wSyCSq0Hc7zwNq3sfljQqaG27r/7a7ooNUnn5nGq6yYWyks9jMO5EoFQ0ax80hSg6oXSRNXaw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001614", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz", + "integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ci-info": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", + "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clean-regexp/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/core-js-compat": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.0.tgz", + "integrity": "sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.751", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.751.tgz", + "integrity": "sha512-2DEPi++qa89SMGRhufWTiLmzqyuGmNF3SK4+PQetW1JKiZdEpF4XQonJXJCzyuYSA6mauiMhbyVhqYAP45Hvfw==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", + "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.22.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz", + "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.1", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.0", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.5", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.3.0.tgz", + "integrity": "sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.3.0", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.0.1", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.0.tgz", + "integrity": "sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==", + "dev": true, + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-config-eslint": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-eslint/-/eslint-config-eslint-10.0.0.tgz", + "integrity": "sha512-ejGeXkQLyAEqUBr6UE246sZBRqscQVuJOTuYLYIt+GaCrfRodLsoJ6/oXRHZumcLDbZYnZoXRpgYy8+NJ/UOOw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-plugin-eslint-comments": "^4.3.0", + "@eslint/js": "^9.0.0", + "eslint-plugin-jsdoc": "^48.2.3", + "eslint-plugin-n": "^17.2.0", + "eslint-plugin-unicorn": "^52.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.6.0.tgz", + "integrity": "sha512-I0AmeNgevgaTR7y2lrVCJmGYF0rjoznpDvqV/kIkZSZbZ8Rw3eu4cGlvBBULScfkSOCzqKbff5LR4CNrV7mZHA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.6.0", + "eslint-compat-utils": "^0.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-jsdoc": { + "version": "48.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.3.tgz", + "integrity": "sha512-r9DMAmFs66VNvNqRLLjHejdnJtILrt3xGi+Qx0op0oRfFGVpOR1Hb3BC++MacseHx93d8SKYPhyrC9BS7Os2QA==", + "dev": true, + "dependencies": { + "@es-joy/jsdoccomment": "~0.42.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.5.0", + "is-builtin-module": "^3.2.1", + "semver": "^7.6.0", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-n": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.3.1.tgz", + "integrity": "sha512-25+HTtKe1F8U/M4ERmdzbz/xkm/gaY0OYC8Fcv1z/WvpLJ8Xfh9LzJ13JV5uj4QyCUD8kOPJrNjn/3y+tc57Vw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "enhanced-resolve": "^5.15.0", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^15.0.0", + "ignore": "^5.2.4", + "minimatch": "^9.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": ">=8.23.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/eslint-plugin-unicorn": { + "version": "52.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz", + "integrity": "sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "@eslint-community/eslint-utils": "^4.4.0", + "@eslint/eslintrc": "^2.1.4", + "ci-info": "^4.0.0", + "clean-regexp": "^1.0.0", + "core-js-compat": "^3.34.0", + "esquery": "^1.5.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.1", + "jsesc": "^3.0.2", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.10.0", + "semver": "^7.5.4", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=8.56.0" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", + "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/finity": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/finity/-/finity-0.5.4.tgz", + "integrity": "sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA==" + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", + "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.1.0.tgz", + "integrity": "sha512-926gJqg+4mkxwYKiFvoomM4J0kWESfk3qfTvRL2/oc/tK/eTDBbrfcKnSa2KtfdxB5onoL7D3A3qIHQFpd4+UA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-electron": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", + "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/iterate-iterator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.2.tgz", + "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "dependencies": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", + "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-localstorage": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-3.0.5.tgz", + "integrity": "sha512-GCwtK33iwVXboZWYcqQHu3aRvXEBwmPkAMRBLeaX86ufhqslyUkLGsi4aW3INEfdQYpUB5M9qtYf3eHvAk2VBg==", + "dependencies": { + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-2.4.2.tgz", + "integrity": "sha512-n8/y+yDJwBjoLQe1GSJbbaYQLTI7QHNZI2+rpmCDbe++WLf9HC3gf6iqj5yfPAV71W4UF3ql5W1+UBPXoXTxng==", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/promise.allsettled": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.7.tgz", + "integrity": "sha512-hezvKvQQmsFkOdrZfYxUxkyxl8mgFQeT259Ajj9PXdbg9VzBCWrItOev72JyWxkCD5VSSqAeHmlN3tWx4DlmsA==", + "dependencies": { + "array.prototype.map": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "iterate-value": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true, + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regjsparser": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz", + "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "dependencies": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "dev": true + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "engines": { + "node": ">=0.6.x" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", + "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", + "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", + "dependencies": { + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.5", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/.github/actions/slack-boltjs-app/package.json b/.github/actions/slack-boltjs-app/package.json new file mode 100644 index 00000000000..877b244e3f0 --- /dev/null +++ b/.github/actions/slack-boltjs-app/package.json @@ -0,0 +1,27 @@ +{ + "name": "slack-bolt", + "version": "1.0.0", + "description": "A secure and simple Bolt app for Slack ChatOps", + "main": "src/index.js", + "type": "module", + "scripts": { + "start": "node -r dotenv/config src/index.js" + }, + "dependencies": { + "dotenv": "^16", + "@slack/bolt": "^3.18.0", + "node-localstorage": "^3.0.5" + }, + "engines": { + "node": "^20", + "npm": "^10" + }, + "repository": { + "url": "https://github.com/JosiahSiegel/slack-boltjs-app" + }, + "license": "MIT", + "devDependencies": { + "eslint": "9.3.0", + "eslint-config-eslint": "^10.0.0" + } +} diff --git a/.github/actions/slack-boltjs-app/src/index.js b/.github/actions/slack-boltjs-app/src/index.js new file mode 100644 index 00000000000..a40aabce36a --- /dev/null +++ b/.github/actions/slack-boltjs-app/src/index.js @@ -0,0 +1,47 @@ +import boltjs from "@slack/bolt"; +const { App, directMention } = boltjs; +import fs from "fs"; +import ghRouter from "./utils/github/router.js"; +import appHome from "./views/app_home.js"; + +const app = new App({ + token: process.env.SLACK_BOT_TOKEN, + appToken: process.env.SLACK_APP_TOKEN, + socketMode: true, +}); + +// Use a single message listener with a switch statement to handle different commands +app.message(directMention(), async ({ message, say }) => { + const command = message.text.split(" ")[1].split("-")[0]; + switch (command) { + case ":wave:": + await say(`Hello, <@${message.user}>`); + break; + case "gh": + await ghRouter({ message, say }); + break; + case "help": + const filename = ".help"; + const data = fs.readFileSync(filename, "utf8"); + say(data); + break; + default: + // Handle unknown commands + say( + `Sorry, I don't recognize that command. Try typing \`@bot help\` for more information.` + ); + } +}); + +// Listen for users opening App Home +app.event("app_home_opened", async ({ event, client }) => { + appHome({ event, client }); +}); + +(async () => { + const port = process.env.PORT || 3000; + + // Start your app + await app.start(port); + console.log(`⚡️ Slack Bolt app is running on port ${port}!`); +})(); diff --git a/.github/actions/slack-boltjs-app/src/utils/github/default_branch.js b/.github/actions/slack-boltjs-app/src/utils/github/default_branch.js new file mode 100644 index 00000000000..7ba49a0a476 --- /dev/null +++ b/.github/actions/slack-boltjs-app/src/utils/github/default_branch.js @@ -0,0 +1,11 @@ +import request from "./request.js"; + +const default_branch = async function ({ api, app, token, say, msg }) { + const path = `/repos/${app}`; + const method = "GET"; + const data = null; + const out = await request({ api, path, method, token, data, say, msg }); + return JSON.parse(out)?.default_branch; +}; + +export default default_branch; diff --git a/.github/actions/slack-boltjs-app/src/utils/github/list_targets.js b/.github/actions/slack-boltjs-app/src/utils/github/list_targets.js new file mode 100644 index 00000000000..1cf459027c9 --- /dev/null +++ b/.github/actions/slack-boltjs-app/src/utils/github/list_targets.js @@ -0,0 +1,13 @@ +const list_targets = async ({ say }) => { + const deployTargets = process.env.GITHUB_TARGET_BRANCHES.split(","); + if (deployTargets.length === 0) { + return say( + "No targets branches defined. Set GITHUB_TARGET_BRANCHES first." + ); + } else { + var targets = "- " + deployTargets.join("\n- "); + return say(targets); + } +}; + +export default list_targets; diff --git a/.github/actions/slack-boltjs-app/src/utils/github/lock_target.js b/.github/actions/slack-boltjs-app/src/utils/github/lock_target.js new file mode 100644 index 00000000000..c7332647dc6 --- /dev/null +++ b/.github/actions/slack-boltjs-app/src/utils/github/lock_target.js @@ -0,0 +1,61 @@ +const lock_target = async ({ localStorage, args, say, user }) => { + const action = args[2]; + const branch = args[3]; + + const deployTargets = process.env.GITHUB_TARGET_BRANCHES.split(","); + if (!deployTargets.includes(branch)) { + say( + `\"${branch}\" is not in available target branches. Use: <@bot name> gh-targets` + ); + return false; + } + + switch (action) { + case "add": + // Check if the branch already has a lock + if (localStorage.getItem(branch)) { + // If yes, say that the branch is already locked + await say( + `Branch ${branch} is already locked by <@${localStorage.getItem( + branch + )}>` + ); + } else { + // If no, add the lock and say that the branch is locked + localStorage.setItem(branch, user); + await say(`Locked branch ${branch}`); + } + break; + case "remove": + // Check if the branch has a lock + if (localStorage.getItem(branch)) { + // If yes, remove the lock and say that the lock is removed + await say( + `Removed <@${localStorage.getItem( + branch + )}>'s lock on branch ${branch}` + ); + localStorage.removeItem(branch); + } else { + // If no, say that the branch is not locked + await say(`Branch ${branch} is not locked`); + } + break; + case "show": + // Check if the branch has a lock + if (localStorage.getItem(branch)) { + // If yes, say who locked the branch + await say( + `Branch ${branch} locked by <@${localStorage.getItem(branch)}>` + ); + } else { + // If no, say that the branch is not locked + await say(`Branch ${branch} is not locked`); + } + break; + default: + await say("Invalid lock command"); + } +}; + +export default lock_target; diff --git a/.github/actions/slack-boltjs-app/src/utils/github/push.js b/.github/actions/slack-boltjs-app/src/utils/github/push.js new file mode 100644 index 00000000000..3124d2aed65 --- /dev/null +++ b/.github/actions/slack-boltjs-app/src/utils/github/push.js @@ -0,0 +1,182 @@ +import request from "./request.js"; +import https from "https"; + +const push = async ({ localStorage, args, api, respond, say, force, isCommand }) => { + branchPush(localStorage, args, api, force, respond, say, isCommand); +}; + +export default push; + +// Define a function to check the configuration for branch push +const branchPushCheckConfiguration = function ( + localStorage, + sourceBranch, + targetBranch, + app, + token, + respond +) { + // Get the deploy targets from the environment variable + const deployTargets = process.env.GITHUB_TARGET_BRANCHES.split(","); + + // Define an array of error messages and conditions + const errors = [ + { + message: "Missing configuration: GITHUB_TOKEN", + condition: !token, + }, + { + message: + "Missing configuration: [for :owner/:repo] or GITHUB_OWNER/GITHUB_REPO", + condition: !app, + }, + { + message: + "Missing <sourceBranch>: gh-deploy <sourceBranch> to <targetBranch>", + condition: !sourceBranch, + }, + { + message: + "Missing <targetBranch>: gh-deploy <sourceBranch> to <targetBranch>", + condition: !targetBranch, + }, + { + message: "Missing configuration: GITHUB_TARGET_BRANCHES", + condition: !process.env.GITHUB_TARGET_BRANCHES, + }, + { + message: `\"${targetBranch}\" is not in available target branches. Use: <@bot name> gh-targets`, + condition: !deployTargets.includes(targetBranch), + }, + ]; + + // Loop through the errors and respond if any condition is true + for (const error of errors) { + if (error.condition) { + respond(error.message); + return false; + } + } + + if (localStorage.getItem(targetBranch)) { + respond(`Branch ${targetBranch} is locked by <@${localStorage.getItem(targetBranch)}>`); + return false; + } + + // Return true if no errors are found + return true; +}; + +// Define constants +const token = process.env.GITHUB_TOKEN; +const app = process.env.GITHUB_REPO; +const port = 443; +const method = "PATCH"; +const headers = { + "User-Agent": "request", + "X-GitHub-Api-Version": "2022-11-28", +}; + +// Define a function to get the SHA of a branch +const getSHA = (api, branch) => { + return new Promise((resolve, reject) => { + // Set the options for the HTTPS request + const options = { + hostname: api, + port, + path: `/repos/${app}/git/refs/heads/${branch}`, + method: "GET", + headers, + }; + + // Make the HTTPS request + const req = https.request(options, (res) => { + let data = ""; + + // Concatenate the data chunks + res.on("data", (chunk) => { + data += chunk; + }); + + // Parse the JSON response and resolve the promise with the SHA + res.on("end", () => { + try { + const sha = JSON.parse(data).object.sha; + resolve(sha); + } catch (error) { + reject(error); + } + }); + }); + + // Handle errors and end the request + req.on("error", (error) => { + reject(error); + }); + req.end(); + }); +}; + +// Define a function to push a branch to another branch +const branchPush = async (localStorage, args, api, force, respond, say, isCommand) => { + // Get the source and target branches from the arguments + const sourceBranch = isCommand ? args[0] : args[2]; + const targetBranch = isCommand ? args[2] : args[4]; + + if ( + !branchPushCheckConfiguration( + localStorage, + sourceBranch, + targetBranch, + app, + token, + respond + ) + ) { + return; + } + + // Get the SHA of the source branch + try { + const sha = await getSHA(api, sourceBranch); + + // Prepare the data for the push request + const data = JSON.stringify({ + sha, + force, + }); + + // Set the path for the push request + const path = `/repos/${app}/git/refs/heads/${targetBranch}`; + + // Make the push request using the request module + const out = await request({ + api, + path, + method, + token, + data, + say, + msg: "", + }); + + // If the push request is successful, respond and say accordingly + if (out) { + const json = JSON.parse(out); + console.log(json); + respond( + `${ + force ? "Force pushed" : "Pushed" + } commit \"${sha}\" to branch \"${targetBranch}\"` + ); + say( + `\`deploy ${sourceBranch} to ${targetBranch} for ${app}\` triggered! :rocket:` + ); + } + } catch (error) { + // If there is an error, say that the branch was not found + say(`I failed to find branch \"${sourceBranch}\"!`); + } + + return true; +}; diff --git a/.github/actions/slack-boltjs-app/src/utils/github/request.js b/.github/actions/slack-boltjs-app/src/utils/github/request.js new file mode 100644 index 00000000000..faf1e373bb8 --- /dev/null +++ b/.github/actions/slack-boltjs-app/src/utils/github/request.js @@ -0,0 +1,44 @@ +import https from "https"; + +const request = async ({ api, path, method, token, data, say, msg }) => { + const json = await httpsRequest(api, path, method, token, data, say, msg); + return json; +}; + +export default request; + +const httpsRequest = (api, path, method, token, data, say, msg) => { + return new Promise((resolve, reject) => { + const options = { + hostname: api, + port: 443, + path, + method, + headers: { + "User-Agent": "request", + Authorization: `token ${token}`, + "Content-Type": "application/json", + "X-GitHub-Api-Version": "2022-11-28", + }, + }; + const req = https.request(options, async (res) => { + let body = ""; + res.on("data", (chunk) => { + body += chunk; + }); + res.on("end", async () => { + resolve(body); + if (msg) { + await say(msg); + } + }); + }); + req.on("error", (err) => { + reject(err); + }); + if (data) { + req.write(data); + } + req.end(); + }); +}; diff --git a/.github/actions/slack-boltjs-app/src/utils/github/router.js b/.github/actions/slack-boltjs-app/src/utils/github/router.js new file mode 100644 index 00000000000..da71834e9ef --- /dev/null +++ b/.github/actions/slack-boltjs-app/src/utils/github/router.js @@ -0,0 +1,65 @@ +import push from "../../utils/github/push.js"; +import listTargets from "./list_targets.js"; +import lockTarget from "./lock_target.js"; +import runWorkflow from "./run_workflow.js"; +import nodeLs from "node-localstorage"; + +const { LocalStorage } = nodeLs; +const { GITHUB_API } = process.env; +const localStorage = new LocalStorage("src/.locks"); +const api = GITHUB_API || "api.github.com"; +const force = true; + +// Define a function to route the commands +const router = async ({ message, say }) => { + const { text, user } = message; + // Split the message text by spaces + const args = text.split(" "); + + // Check if there are inputs after --inputs flag + const inputsIndex = args.indexOf("--inputs"); + let inputs; + if (inputsIndex > -1) { + // Get the inputs from the message text + inputs = text.split("--inputs")[1].trim(); + // Remove the inputs from the args array + args.splice(inputsIndex); + } + + // Get the command from the second argument + const ghCommand = args[1].split("-")[1]; + + try { + // Execute the command based on a switch statement + switch (ghCommand) { + case "deploy": + await push({ + localStorage, + args, + api, + respond: say, + say, + force, + isCommand: false, + }); + break; + case "targets": + await listTargets({ say }); + break; + case "run": + await runWorkflow({ args, api, say, inputs }); + break; + case "lock": + await lockTarget({ localStorage, args, say, user }); + break; + default: + await say(`Invalid command :(: ${ghCommand}`); + } + } catch (error) { + // Handle errors and log them + await say(`gh ${ghCommand} failed with error: ${error}`); + console.error(error); + } +}; + +export default router; diff --git a/.github/actions/slack-boltjs-app/src/utils/github/run_workflow.js b/.github/actions/slack-boltjs-app/src/utils/github/run_workflow.js new file mode 100644 index 00000000000..09881a97c92 --- /dev/null +++ b/.github/actions/slack-boltjs-app/src/utils/github/run_workflow.js @@ -0,0 +1,43 @@ +import defaultBranch from "../../utils/github/default_branch.js"; +import request from "../../utils/github/request.js"; + +const run_workflow = async ({ args, api, say, inputs }) => { + const [workflowFile, app = process.env.GITHUB_REPO, branch] = args.slice(2); + const token = process.env.GITHUB_TOKEN; + // Use an object to store the request data + let data = { ref: branch || (await defaultBranch({ api, app, token, say })) }; + + if (inputs) { + // Use JSON.parse and JSON.stringify to convert the inputs to a valid JSON object + data.inputs = JSON.parse( + `{${inputs}}` + .replace(/([,\{] *)(\w+):/g, '$1"$2":') + .replace( + /([,\{] *"\w+":)(?! *-?[0-9\.]+[,\}])(?! *[\{\[])( *)([^,\}]*)/g, + '$1$2"$3"' + ) + ); + } + + const stringData = JSON.stringify(data); + const path = `/repos/${app}/actions/workflows/${workflowFile}/dispatches`; + const method = "POST"; + + const out = await request({ + api, + path, + method, + token, + data: stringData, + say, + }); + if (out) { + say(JSON.parse(out).message); + } else { + say( + `Triggered workflow \`${workflowFile}\` with \`${stringData}\` for \`${app}\`! :rocket:` + ); + } +}; + +export default run_workflow; diff --git a/.github/actions/slack-boltjs-app/src/views/app_home.js b/.github/actions/slack-boltjs-app/src/views/app_home.js new file mode 100644 index 00000000000..11202a820d9 --- /dev/null +++ b/.github/actions/slack-boltjs-app/src/views/app_home.js @@ -0,0 +1,33 @@ +const appHome = async ({ event, client }) => { + try { + /* view.publish is the method that your app uses to push a view to the Home tab */ + await client.views.publish({ + /* the user that opened your app's app home */ + user_id: event.user, + + /* the view object that appears in the app home*/ + view: { + type: "home", + callback_id: "home_view", + + /* body of the view */ + blocks: [ + { + type: "section", + text: { + type: "mrkdwn", + text: "Welcome :tada:", + }, + }, + { + type: "divider", + }, + ], + }, + }); + } catch (error) { + console.error(error); + } +}; + +export default appHome; From 1acb1eb977d9636a3e82eeeb3967eaff67febcdf Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 06:57:05 -0700 Subject: [PATCH 11/32] Importing JosiahSiegel's workflow-housekeeper GitHub Action --- .../.github/dependabot.yml | 11 +++ .../.github/workflows/test_action.yml | 27 +++++++ .../actions/workflow-housekeeper/README.md | 55 ++++++++++++++ .../actions/workflow-housekeeper/action.yml | 41 +++++++++++ .../workflow-housekeeper/lib/housekeeper.sh | 72 +++++++++++++++++++ 5 files changed, 206 insertions(+) create mode 100644 .github/actions/workflow-housekeeper/.github/dependabot.yml create mode 100644 .github/actions/workflow-housekeeper/.github/workflows/test_action.yml create mode 100644 .github/actions/workflow-housekeeper/README.md create mode 100644 .github/actions/workflow-housekeeper/action.yml create mode 100644 .github/actions/workflow-housekeeper/lib/housekeeper.sh diff --git a/.github/actions/workflow-housekeeper/.github/dependabot.yml b/.github/actions/workflow-housekeeper/.github/dependabot.yml new file mode 100644 index 00000000000..0d08e261a2a --- /dev/null +++ b/.github/actions/workflow-housekeeper/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/actions/workflow-housekeeper/.github/workflows/test_action.yml b/.github/actions/workflow-housekeeper/.github/workflows/test_action.yml new file mode 100644 index 00000000000..b63b8b8944c --- /dev/null +++ b/.github/actions/workflow-housekeeper/.github/workflows/test_action.yml @@ -0,0 +1,27 @@ +name: Test action + +on: + pull_request: + branches: + - main + push: + branches: + - main + + +jobs: + test-action: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + - name: Use local action + uses: ./ + id: local-action + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + ignore-branch-workflows: false + retention-time: '0 minutes' + retain-run-count: 2 + dry-run: false diff --git a/.github/actions/workflow-housekeeper/README.md b/.github/actions/workflow-housekeeper/README.md new file mode 100644 index 00000000000..2907e9131ef --- /dev/null +++ b/.github/actions/workflow-housekeeper/README.md @@ -0,0 +1,55 @@ +# Workflow Housekeeper + +Retain a time period or quantity of workflow runs. + +[](https://github.com/JosiahSiegel/workflow-housekeeper/actions/workflows/test_action.yml) + +### Dependencies: + +>Change in repo: `Settings -> Actions -> General -> Workflow Permissions to allow read and write` + +## Inputs +```yml + ignore-branch-workflows: + description: 'Ignore runs from workflows currently in ./github/workflow' + required: false + retention-time: + description: 'Period of time to maintain history. E.g. "2 weeks", "3 days", etc.' + required: false + retain-run-count: + description: 'Number of latest runs to keep' + required: false + dry-run: + description: 'Only list runs pending deletion' + required: false +``` + +## Usage +```yml + - name: Checkout + uses: actions/checkout@v3 + - name: Run workflow housekeeper + uses: josiahsiegel/workflow-housekeeper@<CURRENT_VERSION> + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` +or +```yml + - name: Checkout + uses: actions/checkout@v3 + - name: Run workflow housekeeper + uses: josiahsiegel/workflow-housekeeper@<CURRENT_VERSION> + id: scan + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + ignore-branch-workflows: true + retention-time: '1 days' + retain-run-count: 1 + dry-run: false +``` + +## Generated summary +### ✨ Workflow Housekeeper ✨ + * .github/workflows/test_action.yml 4618840926 + * .github/workflows/test_action.yml 4618827035 \ No newline at end of file diff --git a/.github/actions/workflow-housekeeper/action.yml b/.github/actions/workflow-housekeeper/action.yml new file mode 100644 index 00000000000..796910b771c --- /dev/null +++ b/.github/actions/workflow-housekeeper/action.yml @@ -0,0 +1,41 @@ +# action.yml +name: 'Workflow Housekeeper' +description: 'Retain a time period or quantity of workflow runs.' +branding: + icon: 'trash-2' + color: 'red' +inputs: + ignore-branch-workflows: + description: 'Ignore runs from workflows currently in ./github/workflow' + required: false + retention-time: + description: 'Period of time to maintain history. E.g. "2 weeks", "3 days", etc.' + required: false + retain-run-count: + description: 'Number of latest runs to keep' + required: false + dry-run: + description: 'Only list runs pending deletion' + required: false +outputs: + housekeeping_output: + description: 'Output of housekeeping steps' + value: ${{ steps.local-action.outputs.housekeeping_output }} +runs: + using: "composite" + steps: + - name: Run local action + id: local-action + run: | + ${{ github.action_path }}/lib/housekeeper.sh \ + "${{ github.repository }}" \ + "${{ inputs.ignore-branch-workflows }}" \ + "${{ inputs.retention-time }}" \ + "${{ inputs.retain-run-count }}" \ + "${{ inputs.dry-run }}" + shell: bash + - name: Create summary + run: | + echo "### :sparkles: Workflow Housekeeper :sparkles:" >> $GITHUB_STEP_SUMMARY + echo -e "${{ steps.local-action.outputs.housekeeping_output }}" >> $GITHUB_STEP_SUMMARY + shell: bash diff --git a/.github/actions/workflow-housekeeper/lib/housekeeper.sh b/.github/actions/workflow-housekeeper/lib/housekeeper.sh new file mode 100644 index 00000000000..ff4bd6e84cc --- /dev/null +++ b/.github/actions/workflow-housekeeper/lib/housekeeper.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# Init +repo="$1" +ignore_branch_workflows=$2 +retention_time=$3 +retain_run_count=$4 +dry_run=$5 + +# ignore_branch_workflows +if [[ -z $ignore_branch_workflows ]]; then + ignore_branch_workflows=false +fi +echo "ignore_branch_workflows: $ignore_branch_workflows" + +if [[ $ignore_branch_workflows = true ]]; then + files=$(ls -1 .github/workflows/ | sed 's/^/and .path != \".github\/workflows\//;s/$/\"/') +else + files="" +fi + +# retention_time +if [[ -z $retention_time ]]; then + retention_time="" +else + keep_date=$(date -d "$date -$retention_time" +%s) + keep_stmt="| select (.run_started_at | . == null or fromdateiso8601 < $keep_date)" +fi +echo "time_threshold: $retention_time" + +# retain_run_count +if [[ -z $retain_run_count ]]; then + retain_run_count=0 +fi +let retain_run_count2=retain_run_count*2 +echo "retain_run_count: $retain_run_count2" + +# dry_run +if [[ -z $dry_run ]]; then + dry_run=false +fi +echo "dry_run: $dry_run" + +# Build jq query +runs="repos/$repo/actions/runs" +query=".workflow_runs[] \ +| select( \ +.path != \".github/workflows/placeholder.yaml\" \ +"$files" +) +$keep_stmt +| (.path,.id)" + +# Get run ids +output=$(gh api --paginate $runs --jq "$query") +output=($(echo $output | tr " " "\n")) +output=${output[@]:$retain_run_count2} + +# Delete or echo run ids +for id in $output; do + if [[ $dry_run = false ]]; then + [[ $id != ".git"* ]] && gh api --silent $runs/$id -X DELETE + else + [[ $id != ".git"* ]] && echo "gh api --silent $runs/$id -X DELETE" || echo "$id" + fi + [[ $id = ".git"* ]] && summary+=" * $id" || summary+=" $id\n" + + # Prevent rate limit + sleep 1; +done + +echo "housekeeping_output=$(echo "${summary}")" >>$GITHUB_OUTPUT From fe62ba3ba2ad556f5e812f60df42895268b60da2 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 15:41:18 -0700 Subject: [PATCH 12/32] Modifying links to JosiahSiegel's GHA (remote repositories) --- .github/actions/deploy-backend/action.yml | 9 ++++++--- .github/actions/vpn-azure/action.yml | 6 ++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/actions/deploy-backend/action.yml b/.github/actions/deploy-backend/action.yml index a7d23f2bf88..cfe6a329142 100644 --- a/.github/actions/deploy-backend/action.yml +++ b/.github/actions/deploy-backend/action.yml @@ -138,7 +138,7 @@ runs: echo "::add-mask::$value" echo "$secret_get=$value" >> $GITHUB_OUTPUT done - + - name: Create ssl key file if: env.USE_DCT == 'true' @@ -152,7 +152,7 @@ runs: shell: bash env: SSL_KEY: ${{ steps.key-vault.outputs[env.KEY_NAME] }} - + - name: Confirm if runner is a signer if: env.USE_DCT == 'true' working-directory: prime-router @@ -333,7 +333,10 @@ runs: - name: Validate function app checksum if: inputs.checksum-validation == 'true' - uses: JosiahSiegel/checksum-validate-action@ebdf8c12c00912d18de93c483b935d51582f9236 + + ## uses: JosiahSiegel/checksum-validate-action@ebdf8c12c00912d18de93c483b935d51582f9236 + uses: ./.github/actions/checksum-validate-action + with: key: backend validate: true diff --git a/.github/actions/vpn-azure/action.yml b/.github/actions/vpn-azure/action.yml index 803ff5fe6a4..ad2f09be2d7 100644 --- a/.github/actions/vpn-azure/action.yml +++ b/.github/actions/vpn-azure/action.yml @@ -40,7 +40,9 @@ runs: sed -i "s/\(dhcp-option DNS \).*/\1${{ inputs.dns-ip }}/" .github/vpn/${{ inputs.env-name }}.ovpn shell: bash - - uses: josiahsiegel/action-connect-ovpn@794339aff94452216c97f609476c367a43a31295 + ## - uses: josiahsiegel/action-connect-ovpn@794339aff94452216c97f609476c367a43a31295 + - uses: ./.github/actions/action-connect-ovpn + if: inputs.env-name && inputs.ca-cert != 'false' id: connect_vpn with: @@ -79,7 +81,7 @@ runs: $env:ARM_CLIENT_SECRET = $servicePrincipal.clientSecret $env:ARM_SUBSCRIPTION_ID = $servicePrincipal.subscriptionId $env:ARM_TENANT_ID = $servicePrincipal.tenantId - + # Save environment variable setup for subsequent steps Get-ChildItem -Path Env: -Recurse -Include ARM_* | ForEach-Object {Write-Output "$($_.Name)=$($_.Value)"} >> $env:GITHUB_ENV shell: pwsh From a538a8a6acb7ecb815dc97460f2ce74874ed8434 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 15:45:42 -0700 Subject: [PATCH 13/32] Modifying links to JosiahSiegel's GHA (remote repositories) --- .github/actions/deploy-backend/action.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/actions/deploy-backend/action.yml b/.github/actions/deploy-backend/action.yml index cfe6a329142..930a08db5f2 100644 --- a/.github/actions/deploy-backend/action.yml +++ b/.github/actions/deploy-backend/action.yml @@ -139,7 +139,6 @@ runs: echo "$secret_get=$value" >> $GITHUB_OUTPUT done - - name: Create ssl key file if: env.USE_DCT == 'true' working-directory: prime-router From bcdd7f14dc68b8ecd95f071d5f71fe8c3f4d682d Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 16:16:04 -0700 Subject: [PATCH 14/32] Reverting changes to references for JosiahSiegel's GHA (remote repositories) --- .github/actions/deploy-backend/action.yml | 4 ++-- .github/actions/vpn-azure/action.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/deploy-backend/action.yml b/.github/actions/deploy-backend/action.yml index 930a08db5f2..de808a918d8 100644 --- a/.github/actions/deploy-backend/action.yml +++ b/.github/actions/deploy-backend/action.yml @@ -333,8 +333,8 @@ runs: - name: Validate function app checksum if: inputs.checksum-validation == 'true' - ## uses: JosiahSiegel/checksum-validate-action@ebdf8c12c00912d18de93c483b935d51582f9236 - uses: ./.github/actions/checksum-validate-action + uses: JosiahSiegel/checksum-validate-action@ebdf8c12c00912d18de93c483b935d51582f9236 + ## emvaldes@hotmail.com (Replace) uses: ./.github/actions/checksum-validate-action with: key: backend diff --git a/.github/actions/vpn-azure/action.yml b/.github/actions/vpn-azure/action.yml index ad2f09be2d7..5c9aaf5f784 100644 --- a/.github/actions/vpn-azure/action.yml +++ b/.github/actions/vpn-azure/action.yml @@ -40,8 +40,8 @@ runs: sed -i "s/\(dhcp-option DNS \).*/\1${{ inputs.dns-ip }}/" .github/vpn/${{ inputs.env-name }}.ovpn shell: bash - ## - uses: josiahsiegel/action-connect-ovpn@794339aff94452216c97f609476c367a43a31295 - - uses: ./.github/actions/action-connect-ovpn + - uses: josiahsiegel/action-connect-ovpn@794339aff94452216c97f609476c367a43a31295 + ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/action-connect-ovpn if: inputs.env-name && inputs.ca-cert != 'false' id: connect_vpn From 05620bd2a5724cca5b03129642b356948a7e1ad9 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 16:32:26 -0700 Subject: [PATCH 15/32] Pre-setting workflows configuration changes for referencing JosiahSiegel's GHA (remote repositories) --- .github/workflows/alert_terraform_changes.yml | 10 ++++++---- .github/workflows/deploy_terraform.yml | 11 +++++++---- .github/workflows/log_management.yml | 6 ++++++ .github/workflows/prepare_deployment_branch.yaml | 6 ++++++ .github/workflows/release_to_azure.yml | 3 +++ .github/workflows/scan_action_logs.yml | 5 ++++- 6 files changed, 32 insertions(+), 9 deletions(-) diff --git a/.github/workflows/alert_terraform_changes.yml b/.github/workflows/alert_terraform_changes.yml index a9033ec99ba..c551ec265fc 100644 --- a/.github/workflows/alert_terraform_changes.yml +++ b/.github/workflows/alert_terraform_changes.yml @@ -26,9 +26,12 @@ jobs: user-key: ${{ secrets.USER_KEY }} sp-creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} tf-auth: true - + - name: Collect Terraform stats + uses: josiahsiegel/terraform-stats@68b8cbe42c494333fbf6f8d90ac86da1fb69dcc2 + ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/terraform-stats + id: stats1 with: terraform-directory: operations/app/terraform/vars/${{ matrix.env }} @@ -46,7 +49,7 @@ jobs: echo "resource-drifts=$(echo '${{ steps.stats1.outputs.resource-drifts }}' \ | sed 's/\"/\\\"/g' | sed 's/\\\\\"/\\\\\\"/g')" >> $GITHUB_OUTPUT echo "$EOF" >> $GITHUB_OUTPUT - + - name: Slack Notification if: ${{ steps.format_out.outputs.CHANGES != '' }} uses: ./.github/actions/notifications @@ -57,9 +60,8 @@ jobs: "change-count": "${{ steps.stats1.outputs.change-count }}" "drift-count": "${{ steps.stats1.outputs.drift-count }}" "resource-drifts": "${{ env.resource-drifts }}" - + icon-emoji: ':bell:' channel: pagerduty-alert-dump webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }} color: warning - diff --git a/.github/workflows/deploy_terraform.yml b/.github/workflows/deploy_terraform.yml index 3112e70fc10..cc7f37cd99d 100644 --- a/.github/workflows/deploy_terraform.yml +++ b/.github/workflows/deploy_terraform.yml @@ -11,7 +11,7 @@ on: jobs: pre_job: name: Set Build Environment - concurrency: + concurrency: group: ${{ github.workflow }}-${{ needs.pre_job.outputs.env_name }} cancel-in-progress: true runs-on: ubuntu-latest @@ -28,7 +28,7 @@ jobs: confirm_changes: name: Check Terraform Stats - ${{ needs.pre_job.outputs.env_name }} if: ${{ needs.pre_job.outputs.tf_change == 'true' }} - concurrency: + concurrency: group: ${{ github.workflow }}-${{ needs.pre_job.outputs.env_name }} cancel-in-progress: true needs: @@ -51,7 +51,10 @@ jobs: sp-creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} tf-auth: true - name: Collect Terraform stats + uses: josiahsiegel/terraform-stats@68b8cbe42c494333fbf6f8d90ac86da1fb69dcc2 + ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/terraform-stats + id: stats1 with: terraform-directory: operations/app/terraform/vars/${{ needs.pre_job.outputs.env_name }} @@ -60,7 +63,7 @@ jobs: approve_deploy: name: Approve Deploy - ${{ needs.pre_job.outputs.env_name }} - concurrency: + concurrency: group: ${{ github.workflow }}-${{ needs.pre_job.outputs.env_name }} cancel-in-progress: true needs: @@ -75,7 +78,7 @@ jobs: run_deploy: name: Run Deploy - ${{ needs.pre_job.outputs.env_name }} - concurrency: + concurrency: group: ${{ github.workflow }}-${{ needs.pre_job.outputs.env_name }} cancel-in-progress: true needs: diff --git a/.github/workflows/log_management.yml b/.github/workflows/log_management.yml index 4da1340e62c..88685e96f40 100644 --- a/.github/workflows/log_management.yml +++ b/.github/workflows/log_management.yml @@ -12,7 +12,10 @@ jobs: steps: - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - name: Workflow Housekeeper - workflows NOT in default branch + uses: JosiahSiegel/workflow-housekeeper@731cc20bb613208b34efb6ac74aab4ba147abb50 + ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/workflow-housekeeper + env: GITHUB_TOKEN: ${{ secrets.LOG_MANAGEMENT_TOKEN }} with: @@ -21,7 +24,10 @@ jobs: retain-run-count: 0 dry-run: false - name: Workflow Housekeeper - workflows in default branch + uses: JosiahSiegel/workflow-housekeeper@731cc20bb613208b34efb6ac74aab4ba147abb50 + ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/workflow-housekeeper + env: GITHUB_TOKEN: ${{ secrets.LOG_MANAGEMENT_TOKEN }} with: diff --git a/.github/workflows/prepare_deployment_branch.yaml b/.github/workflows/prepare_deployment_branch.yaml index 25cf8aff75e..df0f8ed61db 100644 --- a/.github/workflows/prepare_deployment_branch.yaml +++ b/.github/workflows/prepare_deployment_branch.yaml @@ -27,13 +27,19 @@ jobs: echo "Branch name: \"${BRANCH_NAME}\"" - name: "Create branch '${{ env.BRANCH_NAME }}' to contain the changes for the deployment on ${{ env.DEPLOYMENT_DATE }}" + uses: JosiahSiegel/remote-branch-action@dbe7a2138eb064fbfdb980abee918091a7501fbe + ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/remote-branch-action + with: branch: "${{ env.BRANCH_NAME }}" - name: "Prepare a Pull Request from ${{ env.BRANCH_NAME }} into production branch" id: pr + uses: JosiahSiegel/reliable-pull-request-action@ae8d0c88126329ee363a35392793d0bc94cb82e7 + ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/reliable-pull-request-action + env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/release_to_azure.yml b/.github/workflows/release_to_azure.yml index 619bc91645a..f8438d4ee0a 100644 --- a/.github/workflows/release_to_azure.yml +++ b/.github/workflows/release_to_azure.yml @@ -144,7 +144,10 @@ jobs: env: checksum_validation: ${{ vars.CHECKSUM_VALIDATION }} if: needs.pre_job.outputs.has_router_change == 'true' && env.checksum_validation == 'true' + uses: JosiahSiegel/checksum-validate-action@ebdf8c12c00912d18de93c483b935d51582f9236 + ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/checksum-validate-action + with: key: backend input: $(az functionapp config appsettings list -g prime-data-hub-${{ needs.pre_job.outputs.env_name }} -n pdh${{ needs.pre_job.outputs.env_name }}-functionapp -o tsv | sort) diff --git a/.github/workflows/scan_action_logs.yml b/.github/workflows/scan_action_logs.yml index 2c209176fc4..687f4ea94dd 100644 --- a/.github/workflows/scan_action_logs.yml +++ b/.github/workflows/scan_action_logs.yml @@ -12,7 +12,10 @@ jobs: steps: - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - name: Scan run logs + uses: josiahsiegel/runleaks@4dd30d107c03b6ade87978e10c94a77015e488f9 + ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/runleaks + id: scan with: github-token: ${{ secrets.RUNLEAKS_TOKEN }} @@ -24,7 +27,7 @@ jobs: fail-on-leak: false - name: Get scan exceptions if: steps.scan.outputs.count > 0 - run: | + run: | echo "count=${{ steps.scan.outputs.count }}" exceptions='${{ steps.scan.outputs.exceptions }}' exceptions_out=$(echo ${exceptions//"%0A"/} | jq '.') From be2a6b5af6cbf4d796ca2876fe79e0d67f1539cf Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 8 Oct 2024 19:51:18 -0700 Subject: [PATCH 16/32] Updated the imported GitHub Actions documentation (README.md) --- .github/actions/azviz-action/README.md | 10 +++++----- .github/actions/checksum-validate-action/README.md | 12 ++++++------ .../actions/reliable-pull-request-action/README.md | 4 ++-- .github/actions/remote-branch-action/README.md | 14 +++++++------- .github/actions/runleaks/README.md | 10 +++++----- .github/actions/slack-boltjs-app/README.md | 8 ++++---- .github/actions/terraform-stats/README.md | 4 ++-- .github/actions/workflow-housekeeper/README.md | 8 ++++---- 8 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.github/actions/azviz-action/README.md b/.github/actions/azviz-action/README.md index b558fdeaa9c..c55796133a6 100644 --- a/.github/actions/azviz-action/README.md +++ b/.github/actions/azviz-action/README.md @@ -1,8 +1,8 @@ # AzViz (Azure Visualizer) action  -[](https://github.com/JosiahSiegel/AzViz-action/actions/workflows/test_linux_runner.yml) -[](https://github.com/JosiahSiegel/AzViz-action/actions/workflows/test_windows_runner.yml) +[](https://github.com/CDCgov/azviz-action/actions/workflows/test_linux_runner.yml) +[](https://github.com/CDCgov/azviz-action/actions/workflows/test_windows_runner.yml) ## ☕ Please donate to [AzViz Developer](https://github.com/PrateekKumarSingh/AzViz#readme) @@ -75,9 +75,9 @@ jobs: - name: Login to Azure uses: azure/login@v1 with: - creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} + creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} enable-AzPSSession: true - - uses: JosiahSiegel/AzViz-action@v1.0.3 + - uses: .github/actions/azviz-action with: resource-group: ${{ github.event.inputs.resource-group }} out-file: ${{ github.event.inputs.out-file }} @@ -90,4 +90,4 @@ jobs: ## Dependencies - * [azure/login](https://github.com/marketplace/actions/azure-login) with `enable-AzPSSession: true` \ No newline at end of file + * [azure/login](https://github.com/marketplace/actions/azure-login) with `enable-AzPSSession: true` diff --git a/.github/actions/checksum-validate-action/README.md b/.github/actions/checksum-validate-action/README.md index e6e62a4caaa..6d1a2d9a2d8 100644 --- a/.github/actions/checksum-validate-action/README.md +++ b/.github/actions/checksum-validate-action/README.md @@ -1,6 +1,6 @@ # Checksum Validate Action -[](https://github.com/JosiahSiegel/checksum-validate-action/actions/workflows/test_action.yml) +[](https://github.com/CDCgov/checksum-validate-action/actions/workflows/test_action.yml) ## Synopsis @@ -19,13 +19,13 @@ jobs: - uses: actions/checkout@v4.1.1 - name: Generate checksum of string - uses: JosiahSiegel/checksum-validate-action@v1 + uses: .github/actions/checksum-validate-action with: key: test string input: hello world - name: Generate checksum of command output - uses: JosiahSiegel/checksum-validate-action@v1 + uses: .github/actions/checksum-validate-action with: key: test command input: $(cat action.yml) @@ -40,7 +40,7 @@ jobs: - name: Validate checksum of valid string id: valid-string - uses: JosiahSiegel/checksum-validate-action@v1 + uses: .github/actions/checksum-validate-action with: key: test string validate: true @@ -49,7 +49,7 @@ jobs: - name: Validate checksum of valid command output id: valid-command - uses: JosiahSiegel/checksum-validate-action@v1 + uses: .github/actions/checksum-validate-action with: key: test command validate: true @@ -91,4 +91,4 @@ inputs: outputs: valid: description: True if checksums match -``` \ No newline at end of file +``` diff --git a/.github/actions/reliable-pull-request-action/README.md b/.github/actions/reliable-pull-request-action/README.md index e9af804ee4a..f2a5da85084 100644 --- a/.github/actions/reliable-pull-request-action/README.md +++ b/.github/actions/reliable-pull-request-action/README.md @@ -2,7 +2,7 @@ > *Only uses built-in GitHub runner commands -[](https://github.com/JosiahSiegel/reliable-pull-request-action/actions/workflows/test-action.yml) +[](https://github.com/CDCgov/reliable-pull-request-action/actions/workflows/test-action.yml) ## Synopsis @@ -25,7 +25,7 @@ jobs: - name: Create Pull Request id: create_pr - uses: JosiahSiegel/reliable-pull-request-action@v1.0.0 + uses: .github/actions/reliable-pull-request-action env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/actions/remote-branch-action/README.md b/.github/actions/remote-branch-action/README.md index 8592ac8b5b4..c7543163db3 100644 --- a/.github/actions/remote-branch-action/README.md +++ b/.github/actions/remote-branch-action/README.md @@ -1,6 +1,6 @@ # Remote Branch Action -[](https://github.com/JosiahSiegel/remote-branch-action/actions/workflows/test-action.yml) +[](https://github.com/CDCgov/remote-branch-action/actions/workflows/test-action.yml) ## Synopsis @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@v4 - name: Create branch - uses: JosiahSiegel/remote-branch-action@v1.1.0 + uses: .github/actions/remote-branch-action with: branch: new-branch ``` @@ -43,7 +43,7 @@ jobs: path: alt-repo - name: Create branch on alt repo - uses: JosiahSiegel/remote-branch-action@v1.1.0 + uses: .github/actions/remote-branch-action with: branch: new-branch-alt-repo path: alt-repo @@ -68,20 +68,20 @@ jobs: - name: Create branch id: create-branch-action - uses: JosiahSiegel/remote-branch-action@v1.1.0 + uses: .github/actions/remote-branch-action with: branch: new-branch - name: Create branch on second repo id: create-branch-action-second-repo - uses: JosiahSiegel/remote-branch-action@v1.1.0 + uses: .github/actions/remote-branch-action with: branch: new-branch-second-repo path: second-repo - name: Get create branch status run: echo ${{ steps.create-branch-action.outputs.create-status }} - + - name: Get create branch status on second repo run: echo ${{ steps.create-branch-action-second-repo.outputs.create-status }} ``` @@ -105,4 +105,4 @@ outputs: create-status: description: Branch creation status value: ${{ steps.create-branch.outputs.create_status }} -``` \ No newline at end of file +``` diff --git a/.github/actions/runleaks/README.md b/.github/actions/runleaks/README.md index f859846858b..8df5d96f09a 100644 --- a/.github/actions/runleaks/README.md +++ b/.github/actions/runleaks/README.md @@ -1,6 +1,6 @@ # runleaks -[](https://github.com/JosiahSiegel/runleaks/actions/workflows/main.yml) +[](https://github.com/CDCgov/runleaks/actions/workflows/main.yml) Leverages [git-secrets](https://github.com/awslabs/git-secrets) to identify potential leaks in GitHub action run logs. @@ -56,7 +56,7 @@ Leverages [git-secrets](https://github.com/awslabs/git-secrets) to identify pote - name: Checkout uses: actions/checkout@v3 - name: Scan run logs - uses: josiahsiegel/runleaks@v1 + uses: .github/actions/runleaks id: scan with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -71,7 +71,7 @@ or - name: Checkout uses: actions/checkout@v3 - name: Scan run logs - uses: josiahsiegel/runleaks@v1 + uses: .github/actions/runleaks id: scan with: github-token: ${{ secrets.MY_TOKEN }} @@ -89,7 +89,7 @@ or with: repository: 'me/my-repo' - name: Scan run logs - uses: josiahsiegel/runleaks@v1 + uses: .github/actions/runleaks id: scan with: github-token: ${{ secrets.MY_TOKEN }} @@ -103,7 +103,7 @@ or ## Local testing * Registers default patterns ```sh -git clone https://github.com/JosiahSiegel/runleaks.git +git clone https://github.com/CDCgov/runleaks.git cd runleaks/ docker build -t runleaks . docker run scan "<PERSONAL_ACCESS_TOKEN>" "<REPO>" <RUN_LIMIT> <MIN_DAYS_OLD> <MAX_DAYS_OLD> diff --git a/.github/actions/slack-boltjs-app/README.md b/.github/actions/slack-boltjs-app/README.md index 1a095225b65..8294b09ca78 100644 --- a/.github/actions/slack-boltjs-app/README.md +++ b/.github/actions/slack-boltjs-app/README.md @@ -1,8 +1,8 @@ # Slack-Boltjs-App -[](https://github.com/JosiahSiegel/slack-bolt/actions/workflows/codeql.yml) -[](https://github.com/JosiahSiegel/slack-bolt/actions/workflows/njsscan.yml) -[](https://app.codacy.com/gh/JosiahSiegel/slack-boltjs-app/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) +[](https://github.com/CDCgov/slack-bolt/actions/workflows/codeql.yml) +[](https://github.com/CDCgov/slack-bolt/actions/workflows/njsscan.yml) +[](https://app.codacy.com/gh/CDCgov/slack-boltjs-app/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)  @@ -78,6 +78,6 @@ Tips: --- [Glitch](https://glitch.com/~slack-boltjs-app) -[GitHub](https://github.com/JosiahSiegel/slack-boltjs-app) +[GitHub](https://github.com/CDCgov/slack-boltjs-app) [1]: https://slack.com/help/articles/115004151203-Slack-Connect-guide--work-with-external-organizations diff --git a/.github/actions/terraform-stats/README.md b/.github/actions/terraform-stats/README.md index ed273fdcce7..b96eb108ad8 100644 --- a/.github/actions/terraform-stats/README.md +++ b/.github/actions/terraform-stats/README.md @@ -1,6 +1,6 @@ # Terraform Stats -[](https://github.com/JosiahSiegel/terraform-stats/actions/workflows/test_action.yml) +[](https://github.com/CDCgov/terraform-stats/actions/workflows/test_action.yml) ## Synopsis @@ -19,7 +19,7 @@ Output the following statistics for the Terraform environment: ```yml - name: Terraform stats - uses: josiahsiegel/terraform-stats@<latest-version> + uses: .github/actions/terraform-stats id: stats with: terraform-directory: ${{ env.tf-dir }} diff --git a/.github/actions/workflow-housekeeper/README.md b/.github/actions/workflow-housekeeper/README.md index 2907e9131ef..2bce1b74abe 100644 --- a/.github/actions/workflow-housekeeper/README.md +++ b/.github/actions/workflow-housekeeper/README.md @@ -2,7 +2,7 @@ Retain a time period or quantity of workflow runs. -[](https://github.com/JosiahSiegel/workflow-housekeeper/actions/workflows/test_action.yml) +[](https://github.com/CDCgov/workflow-housekeeper/actions/workflows/test_action.yml) ### Dependencies: @@ -29,7 +29,7 @@ Retain a time period or quantity of workflow runs. - name: Checkout uses: actions/checkout@v3 - name: Run workflow housekeeper - uses: josiahsiegel/workflow-housekeeper@<CURRENT_VERSION> + uses: .github/actions/workflow-housekeeper env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` @@ -38,7 +38,7 @@ or - name: Checkout uses: actions/checkout@v3 - name: Run workflow housekeeper - uses: josiahsiegel/workflow-housekeeper@<CURRENT_VERSION> + uses: .github/actions/workflow-housekeeper id: scan env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -52,4 +52,4 @@ or ## Generated summary ### ✨ Workflow Housekeeper ✨ * .github/workflows/test_action.yml 4618840926 - * .github/workflows/test_action.yml 4618827035 \ No newline at end of file + * .github/workflows/test_action.yml 4618827035 From 7ee6a6eedbec144c24fadd40767e2d7e0c77d19e Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Wed, 9 Oct 2024 15:46:55 -0700 Subject: [PATCH 17/32] Correcting self/cross referenced repos within these GitHub Actions --- .../azviz-action/.github/workflows/sample_full_workflow.yml | 4 ++-- .../azviz-action/.github/workflows/sample_min_workflow.yml | 4 ++-- .github/actions/runleaks/.github/workflows/scan_public.yml | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/actions/azviz-action/.github/workflows/sample_full_workflow.yml b/.github/actions/azviz-action/.github/workflows/sample_full_workflow.yml index 13a629d4231..ef1d8d8d1a9 100644 --- a/.github/actions/azviz-action/.github/workflows/sample_full_workflow.yml +++ b/.github/actions/azviz-action/.github/workflows/sample_full_workflow.yml @@ -50,9 +50,9 @@ jobs: - name: Login to Azure uses: azure/login@v1 with: - creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} + creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} enable-AzPSSession: true - - uses: JosiahSiegel/AzViz-action@v1.0.3 + - uses: .github/actions/azviz-action with: resource-group: ${{ github.event.inputs.resource-group }} out-file: ${{ github.event.inputs.out-file }} diff --git a/.github/actions/azviz-action/.github/workflows/sample_min_workflow.yml b/.github/actions/azviz-action/.github/workflows/sample_min_workflow.yml index c7818681828..676388ff265 100644 --- a/.github/actions/azviz-action/.github/workflows/sample_min_workflow.yml +++ b/.github/actions/azviz-action/.github/workflows/sample_min_workflow.yml @@ -22,9 +22,9 @@ jobs: - name: Login to Azure uses: azure/login@v1 with: - creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} + creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} enable-AzPSSession: true - - uses: JosiahSiegel/AzViz-action@v1.0.3 + - uses: .github/actions/azviz-action with: resource-group: ${{ github.event.inputs.resource-group }} out-file: ${{ github.event.inputs.out-file }} diff --git a/.github/actions/runleaks/.github/workflows/scan_public.yml b/.github/actions/runleaks/.github/workflows/scan_public.yml index fff9d4f7a4a..9365f5e2557 100644 --- a/.github/actions/runleaks/.github/workflows/scan_public.yml +++ b/.github/actions/runleaks/.github/workflows/scan_public.yml @@ -1,12 +1,12 @@ name: Scan Public Logs -on: +on: workflow_dispatch: inputs: repos: description: 'Repos to scan:' required: true - default: '[\"josiahsiegel\/runleaks\",\"josiahsiegel\/AzViz-action\"]' + default: '[\"prime-reportstream\/runleaks\",\"prime-reportstream\/azviz-action\"]' type: string schedule: - cron: "0 3 * * *" @@ -25,7 +25,7 @@ jobs: - name: Fetch random repo name if: github.event_name == 'schedule' || github.event_name == 'push' - uses: JosiahSiegel/randomrepo@v1.1 + uses: .github/actions/randomrepo id: random with: github-token: ${{ secrets.MY_TOKEN }} From 9b56f4c3d8e14ac07f84d2fa1466512698540c49 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Wed, 9 Oct 2024 16:54:36 -0700 Subject: [PATCH 18/32] Importing JosiahSiegel's rapid-wsl GitHub Action --- .github/actions/rapid-wsl/.gitignore | 8 + .github/actions/rapid-wsl/README.md | 105 +++++++++++ .github/actions/rapid-wsl/defaults/demo.sh | 1 + .github/actions/rapid-wsl/defaults/generic.sh | 1 + .github/actions/rapid-wsl/defaults/macos.sh | 1 + .../defaults/prime-data-ingestion.sh | 1 + .../rapid-wsl/defaults/prime-reportstream.sh | 1 + .../actions/rapid-wsl/etc/sample.gitconfig | 9 + .github/actions/rapid-wsl/lib/args.sh | 163 ++++++++++++++++++ .../rapid-wsl/lib/commands/backup_distro.sh | 5 + .../rapid-wsl/lib/commands/destroy_distro.sh | 9 + .../rapid-wsl/lib/commands/docker_distro.sh | 30 ++++ .../rapid-wsl/lib/commands/down_distro.sh | 8 + .../rapid-wsl/lib/commands/enter_distro.sh | 17 ++ .../rapid-wsl/lib/commands/fix_distro.sh | 8 + .../rapid-wsl/lib/commands/git_setup.sh | 28 +++ .../rapid-wsl/lib/commands/gpg_import.sh | 42 +++++ .../rapid-wsl/lib/commands/init_distro.sh | 50 ++++++ .../rapid-wsl/lib/commands/install_distro.sh | 61 +++++++ .../rapid-wsl/lib/commands/restore_distro.sh | 9 + .../rapid-wsl/lib/commands/setup_distro.sh | 27 +++ .../rapid-wsl/lib/commands/status_distro.sh | 39 +++++ .../lib/commands/test-pipe_distro.sh | 8 + .../rapid-wsl/lib/commands/test_distro.sh | 8 + .../rapid-wsl/lib/commands/tips_distro.sh | 37 ++++ .../rapid-wsl/lib/commands/up_distro.sh | 12 ++ .github/actions/rapid-wsl/lib/controller.sh | 66 +++++++ .github/actions/rapid-wsl/lib/waiting.sh | 17 ++ .github/actions/rapid-wsl/modules/README.md | 4 + .../rapid-wsl/modules/demo/install_distro.sh | 28 +++ .../rapid-wsl/modules/generic/README.md | 17 ++ .../rapid-wsl/modules/macos/install_distro.sh | 28 +++ .../rapid-wsl/modules/macos/setup_distro.sh | 24 +++ .../modules/prime-data-ingestion/README.md | 18 ++ .../prime-data-ingestion/install_distro.sh | 44 +++++ .../prime-data-ingestion/setup_distro.sh | 16 ++ .../modules/prime-reportstream/README.md | 18 ++ .../modules/prime-reportstream/down_distro.sh | 11 ++ .../modules/prime-reportstream/fix_distro.sh | 7 + .../prime-reportstream/install_distro.sh | 45 +++++ .../prime-reportstream/setup_distro.sh | 44 +++++ .../prime-reportstream/status_distro.sh | 7 + .../prime-reportstream/test-pipe_distro.sh | 8 + .../modules/prime-reportstream/test_distro.sh | 23 +++ .../modules/prime-reportstream/tips_distro.sh | 34 ++++ .../modules/prime-reportstream/up_distro.sh | 9 + .github/actions/rapid-wsl/rwsl | 4 + 47 files changed, 1160 insertions(+) create mode 100644 .github/actions/rapid-wsl/.gitignore create mode 100644 .github/actions/rapid-wsl/README.md create mode 100644 .github/actions/rapid-wsl/defaults/demo.sh create mode 100644 .github/actions/rapid-wsl/defaults/generic.sh create mode 100644 .github/actions/rapid-wsl/defaults/macos.sh create mode 100644 .github/actions/rapid-wsl/defaults/prime-data-ingestion.sh create mode 100644 .github/actions/rapid-wsl/defaults/prime-reportstream.sh create mode 100644 .github/actions/rapid-wsl/etc/sample.gitconfig create mode 100644 .github/actions/rapid-wsl/lib/args.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/backup_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/destroy_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/docker_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/down_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/enter_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/fix_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/git_setup.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/gpg_import.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/init_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/install_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/restore_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/setup_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/status_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/test-pipe_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/test_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/tips_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/commands/up_distro.sh create mode 100644 .github/actions/rapid-wsl/lib/controller.sh create mode 100644 .github/actions/rapid-wsl/lib/waiting.sh create mode 100644 .github/actions/rapid-wsl/modules/README.md create mode 100644 .github/actions/rapid-wsl/modules/demo/install_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/generic/README.md create mode 100644 .github/actions/rapid-wsl/modules/macos/install_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/macos/setup_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/prime-data-ingestion/README.md create mode 100644 .github/actions/rapid-wsl/modules/prime-data-ingestion/install_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/prime-data-ingestion/setup_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/README.md create mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/down_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/fix_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/install_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/setup_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/status_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/test-pipe_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/test_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/tips_distro.sh create mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/up_distro.sh create mode 100644 .github/actions/rapid-wsl/rwsl diff --git a/.github/actions/rapid-wsl/.gitignore b/.github/actions/rapid-wsl/.gitignore new file mode 100644 index 00000000000..699ff4165b5 --- /dev/null +++ b/.github/actions/rapid-wsl/.gitignore @@ -0,0 +1,8 @@ +backups/* +etc/* +!etc/sample.gitconfig +courgette.log +bin/* +.env +*.deb +nohup.out diff --git a/.github/actions/rapid-wsl/README.md b/.github/actions/rapid-wsl/README.md new file mode 100644 index 00000000000..34abe5fb27d --- /dev/null +++ b/.github/actions/rapid-wsl/README.md @@ -0,0 +1,105 @@ +# Rapid WSL + + + +[](https://open.vscode.dev/JosiahSiegel/rapid-wsl) + +>⚠️Disable Docker Desktop resource saver due to instability + +## Synopsis + +Easily manage your WSL environment with a handful of generic commands. +Applications are seperated into [modules](modules/). +Remote repository is cloned into distro if specified (`-r`). + +## Quick start + +```sh +./rwsl init -d "Ubuntu-18.04" -m "generic" # -d:Distro, -m:Module, -u:User, -r:Repo +``` +or +```sh +./rwsl init $(cat defaults/generic.sh) +``` + +## Security + +Place `.pgp` & `.gitconfig` files into the `etc/` directory (*ignored by git*). + +Example `.gitconfig`: + +```sh +[core] + editor = "vim" +[user] + name = First Last + email = my@email.com + signingKey = xxx + +[commit] + gpgSign = True +``` + +How to import pgp files from keybase.io: + +```sh +keybase pgp export -q xxx > etc/public.pgp +keybase pgp export -q xxx --secret > etc/private.pgp +``` + +For GitHub authorization, add GitHub personal access token file `git.token` to `etc/`. + +## Commands + +1. `init` (Re)initialize env. +2. `status` Status of env. +3. `backup` Backup env to `.tar` file. +4. `down` Shutdown env. +5. `up` Bring env up after shutdown. +6. `destroy` Destroy env. +7. `restore` Restore env from `.tar` file. +8. `test` Run module specific env tests. +9. `fix` Run module specific env fixes. + +The following scripts can be run in various order, but some run orders are not compatible (e.g. *destroy then test*): + +```sh +d="Ubuntu"; # Distro name +m="generic"; # Module name + +./rwsl init -d $d -m $m; +./rwsl status -d $d -m $m; +./rwsl backup -d $d -m $m; +./rwsl down -d $d -m $m; +./rwsl up -d $d -m $m; +./rwsl destroy -d $d -m $m; +./rwsl restore -d $d -m $m; +./rwsl test -d $d -m $m; + +``` +--- + +**Mandatory:** + * `-d`, `--distro` VAL Distro name (*reference: wsl --list --online*) + * `-m`, `--module` VAL Application name + +**Optional:** + * `-u`, `--user` VAL username + * `-r`, `--repo` VAL Repository url + * `-h`, `--help` Prints this help + +**Examples:** + * `./rwsl [COMMAND] -d VAL -m VAL -u VAL` + * `./rwsl init -d 'Ubuntu-18.04' -m 'generic' -u 'user3' -r 'https://github.com/JosiahSiegel/rapid-wsl.git'` + +## Tips + +Quick re-use arguments: + 1. Create `defaults/my_args.sh` file + * `-d Ubuntu-20.04 -m generic -u user3` + 2. Run script using default file + * `./rwsl init $(cat defaults/my_args.sh)` + +## Notes + +Currently, only tested using Git Bash via Windows Terminal. diff --git a/.github/actions/rapid-wsl/defaults/demo.sh b/.github/actions/rapid-wsl/defaults/demo.sh new file mode 100644 index 00000000000..dd1eac9b43f --- /dev/null +++ b/.github/actions/rapid-wsl/defaults/demo.sh @@ -0,0 +1 @@ +-d Ubuntu-22.04 -m demo -u demouser -r 'https://github.com/JosiahSiegel/terraform-templates.git' \ No newline at end of file diff --git a/.github/actions/rapid-wsl/defaults/generic.sh b/.github/actions/rapid-wsl/defaults/generic.sh new file mode 100644 index 00000000000..abd8896fc1b --- /dev/null +++ b/.github/actions/rapid-wsl/defaults/generic.sh @@ -0,0 +1 @@ +-d Ubuntu -m generic -u user \ No newline at end of file diff --git a/.github/actions/rapid-wsl/defaults/macos.sh b/.github/actions/rapid-wsl/defaults/macos.sh new file mode 100644 index 00000000000..2065b58fddc --- /dev/null +++ b/.github/actions/rapid-wsl/defaults/macos.sh @@ -0,0 +1 @@ +-d Ubuntu-22.04 -m macos -u macuser \ No newline at end of file diff --git a/.github/actions/rapid-wsl/defaults/prime-data-ingestion.sh b/.github/actions/rapid-wsl/defaults/prime-data-ingestion.sh new file mode 100644 index 00000000000..b9d5039f1c1 --- /dev/null +++ b/.github/actions/rapid-wsl/defaults/prime-data-ingestion.sh @@ -0,0 +1 @@ +-d Ubuntu-18.04 -m prime-data-ingestion -u primeuser \ No newline at end of file diff --git a/.github/actions/rapid-wsl/defaults/prime-reportstream.sh b/.github/actions/rapid-wsl/defaults/prime-reportstream.sh new file mode 100644 index 00000000000..5907cf8956b --- /dev/null +++ b/.github/actions/rapid-wsl/defaults/prime-reportstream.sh @@ -0,0 +1 @@ +-d Ubuntu-20.04 -m prime-reportstream -u primeuser \ No newline at end of file diff --git a/.github/actions/rapid-wsl/etc/sample.gitconfig b/.github/actions/rapid-wsl/etc/sample.gitconfig new file mode 100644 index 00000000000..c274e17793d --- /dev/null +++ b/.github/actions/rapid-wsl/etc/sample.gitconfig @@ -0,0 +1,9 @@ +[core] + editor = "vim" +[user] + name = First Last + email = my@email.com + signingKey = xxx + +[commit] + gpgSign = True diff --git a/.github/actions/rapid-wsl/lib/args.sh b/.github/actions/rapid-wsl/lib/args.sh new file mode 100644 index 00000000000..37e6d9c4b52 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/args.sh @@ -0,0 +1,163 @@ +#!/bin/bash + +source ./lib/controller.sh + +#Declare the number of mandatory args +margs=2 + +# Common functions - BEGIN +function example { + echo -e "example: ./rwsl [COMMAND] -d VAL -m VAL -u VAL -r VAL" + echo -e "example: ./rwsl init -d 'Ubuntu-18.04' -m 'generic' -u 'user3' -r 'https://github.com/JosiahSiegel/rapid-wsl.git'" +} + +function usage { + echo -e "usage: ./rwsl [COMMAND] MANDATORY [OPTION]\n" +} + +function help { + usage + echo -e "MANDATORY:" + echo -e " -d, --distro VAL Distro name (*reference: wsl --list --online*)" + echo -e " -m, --module VAL Application name\n" + echo -e "OPTION:" + echo -e " -u, --user VAL Username" + echo -e " -r, --repo VAL Repository url" + echo -e " -h, --help Prints this help\n" + example +} + +# Ensures that the number of passed args are at least equals +# to the declared number of mandatory args. +# It also handles the special case of the -h or --help arg. +function margs_precheck { + if [ $2 ] && [ $1 -lt $margs ]; then + if [ $2 == "--help" ] || [ $2 == "-h" ]; then + help + exit + else + usage + example + exit 1 # error + fi + fi +} + +# Ensures that all the mandatory args are not empty +function margs_check { + if [ $# -lt $margs ]; then + usage + example + exit 1 # error + fi +} +# Common functions - END + +# Main +margs_precheck $# $1 + +distro= +module= +user="user" +repo= + +i=0 +command="" +# Args while-loop +while [ "$1" != "" ]; do + ((i++)) + case $1 in + -d | --distro) + shift + distro=$1 + ;; + -m | --module) + shift + module=$1 + ;; + -u | --user) + shift + user=$1 + ;; + -r | --repo) + shift + repo=$1 + ;; + -h | --help) + help + exit + ;; + *) + if [ $i -eq 1 ]; then + case $1 in + -h | --help) + help + exit + ;; + init) + command="init" + ;; + destroy) + command="destroy" + ;; + install) + command="install" + ;; + docker) + command="docker" + ;; + setup) + command="setup" + ;; + test) + command="test" + ;; + test-pipe) + command="test-pipe" + ;; + enter) + command="enter" + ;; + backup) + command="backup" + ;; + down) + command="down" + ;; + fix) + command="fix" + ;; + restore) + command="restore" + ;; + status) + command="status" + ;; + up) + command="up" + ;; + tips) + command="tips" + ;; + *) + echo "[COMMAND]: illegal option $1" + usage + example + exit 1 # error + ;; + esac + else + echo "[COMMAND]: illegal option $1" + usage + example + exit 1 # error + fi + ;; + esac + shift +done + +# Pass here your mandatory args for check +margs_check $distro $module +echo "$distro $module $user $repo" +eval $command "$distro" "$module" "$user" "$repo" diff --git a/.github/actions/rapid-wsl/lib/commands/backup_distro.sh b/.github/actions/rapid-wsl/lib/commands/backup_distro.sh new file mode 100644 index 00000000000..6a6c5739881 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/backup_distro.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "Creating backup: backups\\$1-$2-backup.tar" +wsl --export $1 'backups\'"$1"'-'"$2"'-backup.tar' +echo "Backup complete" diff --git a/.github/actions/rapid-wsl/lib/commands/destroy_distro.sh b/.github/actions/rapid-wsl/lib/commands/destroy_distro.sh new file mode 100644 index 00000000000..ae0c24fe29b --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/destroy_distro.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +./lib/commands/down_distro.sh $1 $2 $3 $4 + +wsl --unregister $1 + +if [[ ${?} == 0 ]]; then + sleep 5 +fi diff --git a/.github/actions/rapid-wsl/lib/commands/docker_distro.sh b/.github/actions/rapid-wsl/lib/commands/docker_distro.sh new file mode 100644 index 00000000000..bbdd829c9c7 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/docker_distro.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +wsl --set-default $1 + +echo "Restarting docker" +powershell -c 'Stop-Process -Name "Docker Desktop"' +sleep 1 +dpath=$(which docker | sed 's/\/resources\/bin\/docker/\/Docker Desktop.exe/g') +"$dpath" +sleep 1 + +echo "Waiting for docker..." +wsl -d $1 -e bash -c \ +' \ +until usermod -aG docker '"$3"'; do sleep 5; done \ +' &>/dev/null + +wsl -d $1 -u $3 -e bash -c \ +' \ +until docker info; do sleep 5; done \ +' &>/dev/null + +echo "Docker restart complete" + +FILE=./modules/$2/docker_distro.sh +if [[ -f "$FILE" ]]; then + echo "Updating module specific docker" + $FILE $1 $2 $3 $4 + echo "Module specific docker update complete" +fi diff --git a/.github/actions/rapid-wsl/lib/commands/down_distro.sh b/.github/actions/rapid-wsl/lib/commands/down_distro.sh new file mode 100644 index 00000000000..108415f1f1c --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/down_distro.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +FILE=./modules/$2/down_distro.sh +if [[ -f "$FILE" ]]; then + echo "Running module specific shutdown" + $FILE $1 $2 $3 $4 &>/dev/null + echo "Completed module specific shutdown" +fi diff --git a/.github/actions/rapid-wsl/lib/commands/enter_distro.sh b/.github/actions/rapid-wsl/lib/commands/enter_distro.sh new file mode 100644 index 00000000000..8d84c48a561 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/enter_distro.sh @@ -0,0 +1,17 @@ +#!/bin/bash + + +if [ ! -z "$4" ] +then + wsl -d $1 -u $3 -e bash -c \ + ' \ + mkdir -p ~/repos/; \ + cd ~/repos/; \ + repo_url='"$4"'; \ + repo_name='"$(basename $4 .git)"'; \ + cd $repo_name; \ + code .; \ + ' +fi + +wsl -d $1 -u $3 -e bash diff --git a/.github/actions/rapid-wsl/lib/commands/fix_distro.sh b/.github/actions/rapid-wsl/lib/commands/fix_distro.sh new file mode 100644 index 00000000000..e63a5a1eb05 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/fix_distro.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +FILE=./modules/$2/fix_distro.sh +if [[ -f "$FILE" ]]; then + echo "Running module specific fixes" + $FILE $1 $2 $3 $4 + echo "Completed module specific fixes" +fi diff --git a/.github/actions/rapid-wsl/lib/commands/git_setup.sh b/.github/actions/rapid-wsl/lib/commands/git_setup.sh new file mode 100644 index 00000000000..5814e2939e7 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/git_setup.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +echo "" +echo "***" +echo "Setting up GitHub personal access token" + +TKN_FILES=(./etc/git.token) +if [[ -e "${TKN_FILES[0]}" ]]; then + echo "Importing ./etc/git.token file:" + echo "***" + echo "" + wsl -d $1 -u $3 -e bash -c \ + ' \ + path=$(wslpath -u '"$PWD"')/etc/; \ + path=$(echo $path | sed "s3/c/c/3/c/3g"); \ + gh auth login --with-token < $path/git.token; \ + ' + echo "" + echo "***" + echo "Import complete" + echo "***" + echo "" +else + echo "No etc/git.token file found" + echo "GitHub personal access token > etc/git.token" + echo "***" + echo "" +fi diff --git a/.github/actions/rapid-wsl/lib/commands/gpg_import.sh b/.github/actions/rapid-wsl/lib/commands/gpg_import.sh new file mode 100644 index 00000000000..3dcbb85215f --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/gpg_import.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +echo "" +echo "***" +echo "Setting up gpg" + +PGP_FILES=(./etc/*.pgp) +GIT_CONFIG=(./etc/.gitconfig) +if [[ -e "${PGP_FILES[0]}" ]]; then + if [[ -f "${GIT_CONFIG}" ]]; then + echo "Importing etc/*.pgp files:" + echo "***" + echo "" + wsl -d $1 -u $3 -e bash -c \ + ' \ + path=$(wslpath -u '"$PWD"')/etc/; \ + path=$(echo $path | sed "s3/c/c/3/c/3g"); \ + gpg --pinentry-mode loopback --import $path/*.pgp; \ + cp $path/.gitconfig ~/.gitconfig; \ + cp $path/.gitconfig /home/'"$3"'/.gitconfig; \ + sudo chown -R '"$3"':'"$3"' /home/'"$3"'/.gitconfig; \ + echo "export GPG_TTY=\$(tty)" >>~/.bashrc; \ + ' + echo "" + echo "***" + echo "Import complete" + echo "***" + echo "" + else + echo "No etc/.gitconfig file found" + echo "***" + cat "etc/sample.gitconfig" + echo "***" + echo "" + fi +else + echo "No etc/*.pgp files found" + echo "keybase pgp export -q xxx > etc/public.pgp" + echo "keybase pgp export -q xxx --secret > etc/private.pgp" + echo "***" + echo "" +fi diff --git a/.github/actions/rapid-wsl/lib/commands/init_distro.sh b/.github/actions/rapid-wsl/lib/commands/init_distro.sh new file mode 100644 index 00000000000..d11614c44b9 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/init_distro.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +source ./lib/waiting.sh + +echo ' +################################ +# env init # +################################ +' + +echo "(Re)initializing raw distro" +./rwsl destroy -d $1 -m $2 -u $3 -r $4 + +./rwsl install -d $1 -m $2 -u $3 -r $4 + +echo ' +################################ +# docker integration # +################################ +' + +echo "Preparing docker desktop...(if installed)" +./rwsl docker -d $1 -m $2 -u $3 -r $4 + +echo ' +################################ +# env setup # +################################ +' + +echo "Preparing env..." +./rwsl setup -d $1 -m $2 -u $3 -r $4 + +echo ' +################################ +# run tests # +################################ +' + +echo "Running test...(if exists)" +./rwsl test -d $1 -m $2 -u $3 -r $4 + +echo ' +################################ +# enter distro # +################################ +' + +echo "Entering distro..." +./rwsl enter -d $1 -m $2 -u $3 -r $4 diff --git a/.github/actions/rapid-wsl/lib/commands/install_distro.sh b/.github/actions/rapid-wsl/lib/commands/install_distro.sh new file mode 100644 index 00000000000..4521aaacb7e --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/install_distro.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +source ./lib/waiting.sh + +nohup wsl --install -d $1 & +sleep 30 +wsl -d $1 -e bash -c \ +' \ +poweroff -f \ +' +wsl.exe --shutdown +sleep 10 +wsl --manage $1 --set-sparse true + +echo "Creating dev env" +echo "" +echo "***" +echo "CHANGE ROOT PASSWORD FROM 'temp'!" +echo "User '"$3"'" +echo "***" +echo "" + +wsl -d $1 -e bash -c \ +' \ +echo "root:temp" | chpasswd; \ +useradd -m '"$3"'; \ +echo "'"$3"' ALL=(ALL:ALL) NOPASSWD: ALL" | tee /etc/sudoers.d/'"$3"'; \ +chown -R '"$3"':'"$3"' /home/'"$3"'/; \ +curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg; \ +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null; \ +apt-get update; \ +apt-get install gh -y; \ +echo "nameserver 1.1.1.1" >> /etc/resolv.conf; \ +' + +./lib/commands/gpg_import.sh $1 $2 $3 $4 +./lib/commands/git_setup.sh $1 $2 $3 $4 + +wsl -d $1 -u $3 -e bash -c \ +' \ +echo "cd ~" >>~/.bashrc; \ +' + +# https://github.com/microsoft/WSL/issues/8022 +wsl -d $1 -e bash -c \ +' \ +echo -e "[network] \ngenerateResolvConf = false" > /etc/wsl.conf; \ +' +wsl --terminate $1 +wsl -d $1 -e bash -c \ +' \ +rm -f /etc/resolv.conf; \ +echo "nameserver 8.8.8.8" > /etc/resolv.conf; \ +' + +FILE=./modules/$2/install_distro.sh +if [[ -f "$FILE" ]]; then + echo "Running module specific install" + $FILE $1 $2 $3 $4 &>/dev/null & waiting + echo "Completed module specific install" +fi diff --git a/.github/actions/rapid-wsl/lib/commands/restore_distro.sh b/.github/actions/rapid-wsl/lib/commands/restore_distro.sh new file mode 100644 index 00000000000..f2b752518e2 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/restore_distro.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +echo "Restoring backup: backups\\$1-$2-backup.tar" +wsl --import $1 'bin\'"$1"''-''"$2"'\' 'backups\'"$1"'-'"$2"'-backup.tar' + +if [[ ${?} == 0 ]]; then + ./lib/commands/up_distro.sh $1 $2 $3 $4 + echo "Restore complete" +fi diff --git a/.github/actions/rapid-wsl/lib/commands/setup_distro.sh b/.github/actions/rapid-wsl/lib/commands/setup_distro.sh new file mode 100644 index 00000000000..c4493079e3f --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/setup_distro.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ -z "$4" ] +then + echo "Repository url is empty" +else + echo "Repository url is $4" + + wsl -d $1 -u $3 -e bash -c \ + ' \ + mkdir -p ~/repos/; \ + cd ~/repos/; \ + repo_url='"$4"'; \ + repo_name='"$(basename $4 .git)"'; \ + git clone $repo_url; \ + git config --global --add safe.directory $repo_name; \ + sudo chown -R '"$3"':'"$3"' .; \ + echo "cd ~/repos/$repo_name/" >>~/.bashrc; \ + ' +fi + +FILE=./modules/$2/setup_distro.sh +if [[ -f "$FILE" ]]; then + echo "Running module specific setup" + $FILE $1 $2 $3 $4 #>/dev/null + echo "Completed module specific setup" +fi diff --git a/.github/actions/rapid-wsl/lib/commands/status_distro.sh b/.github/actions/rapid-wsl/lib/commands/status_distro.sh new file mode 100644 index 00000000000..02376554f47 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/status_distro.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +echo ' +################################ +# installed distros # +################################ +' +wsl -l --all + +echo ' +################################ +# images # +################################ +' +docker images + +echo ' +################################ +# containers # +################################ +' +docker ps -q | xargs -i sh -c '\ +echo "========================================================="; \ +echo "========================================================="; \ +docker ps --filter "id={}" --format "{{.Names}}"; \ +docker logs --tail 8 {};' + +echo ' +################################ +# application # +################################ +' + +FILE=./modules/$2/status_distro.sh +if [[ -f "$FILE" ]]; then + echo "Fetching module specific application status" + $FILE $1 $2 $3 $4 + echo "Completed module specific application status" +fi diff --git a/.github/actions/rapid-wsl/lib/commands/test-pipe_distro.sh b/.github/actions/rapid-wsl/lib/commands/test-pipe_distro.sh new file mode 100644 index 00000000000..a690c137cf9 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/test-pipe_distro.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +FILE=./modules/$2/test-pipe_distro.sh +if [[ -f "$FILE" ]]; then + echo "Running module specific pipeline test" + $FILE $1 $2 $3 $4 + echo "Completed module specific pipeline test" +fi diff --git a/.github/actions/rapid-wsl/lib/commands/test_distro.sh b/.github/actions/rapid-wsl/lib/commands/test_distro.sh new file mode 100644 index 00000000000..31834a8d819 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/test_distro.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +FILE=./modules/$2/test_distro.sh +if [[ -f "$FILE" ]]; then + echo "Running module specific test" + $FILE $1 $2 $3 $4 + echo "Completed module specific test" +fi diff --git a/.github/actions/rapid-wsl/lib/commands/tips_distro.sh b/.github/actions/rapid-wsl/lib/commands/tips_distro.sh new file mode 100644 index 00000000000..6c827087e8d --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/tips_distro.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +echo -e " +======================= + +# GENERIC TIPS + +======================= + +## Resolve merge conflicts + +### Find conflict files: +grep -lr '<<<<<<<' . + +### Accept local/ours version: +git checkout --ours PATH/FILE + +### For multiple files: +grep -lr '<<<<<<<' . | xargs git checkout --ours + +### Accept remote/theirs version: +git checkout --theirs PATH/FILE + +### For multiple files: +grep -lr '<<<<<<<' . | xargs git checkout --theirs + +## Resolve failing git fetch: +rm -f .git/FETCH_HEAD +======================= +" + +FILE=./modules/$2/tips_distro.sh +if [[ -f $FILE ]]; then + echo -e "# MODULE SPECIFIC TIPS\n" + echo -e "=======================" + $FILE $1 $2 $3 $4 +fi diff --git a/.github/actions/rapid-wsl/lib/commands/up_distro.sh b/.github/actions/rapid-wsl/lib/commands/up_distro.sh new file mode 100644 index 00000000000..4a738bcdd67 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/commands/up_distro.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +./lib/commands/docker_distro.sh $1 $2 $3 $4 + +FILE=./modules/$2/up_distro.sh +if [[ -f "$FILE" ]]; then + echo "Running module specific startup" + $FILE $1 $2 $3 $4 + echo "Completed module specific startup" +fi + +./lib/commands/enter_distro.sh $1 $2 $3 $4 diff --git a/.github/actions/rapid-wsl/lib/controller.sh b/.github/actions/rapid-wsl/lib/controller.sh new file mode 100644 index 00000000000..a95aa00effc --- /dev/null +++ b/.github/actions/rapid-wsl/lib/controller.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +source ./lib/waiting.sh + +COMMAND_PATH=./lib/commands/ +COMMAND="\${COMMAND_PATH}\${FUNCNAME[0]}_distro.sh \$1 \$2 \$3 \$4" + +init() { + eval ${COMMAND} +} + +destroy() { + eval ${COMMAND} & waiting +} + +install() { + eval ${COMMAND} +} + +docker() { + eval ${COMMAND} & waiting +} + +setup() { + eval ${COMMAND} +} + +test() { + eval ${COMMAND} +} + +test-pipe() { + eval ${COMMAND} +} + +enter() { + eval ${COMMAND} +} + +backup() { + eval ${COMMAND} & waiting +} + +down() { + eval ${COMMAND} & waiting +} + +fix() { + eval ${COMMAND} +} + +restore() { + eval ${COMMAND} & waiting +} + +status() { + eval ${COMMAND} & waiting +} + +up() { + eval ${COMMAND} +} + +tips() { + eval ${COMMAND} +} diff --git a/.github/actions/rapid-wsl/lib/waiting.sh b/.github/actions/rapid-wsl/lib/waiting.sh new file mode 100644 index 00000000000..ccd1e093989 --- /dev/null +++ b/.github/actions/rapid-wsl/lib/waiting.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +waiting() { + pid=$! + + spin='-\|/' + + START_TIME=$SECONDS + i=0 + while kill -0 $pid 2>/dev/null; do + i=$(((i + 1) % 4)) + printf "\r${spin:$i:1} " + sleep .1 + done + ELAPSED_TIME=$(($SECONDS - $START_TIME)) + echo -e "\n$(($ELAPSED_TIME/60)) min $(($ELAPSED_TIME%60)) sec" +} diff --git a/.github/actions/rapid-wsl/modules/README.md b/.github/actions/rapid-wsl/modules/README.md new file mode 100644 index 00000000000..e094bec82d0 --- /dev/null +++ b/.github/actions/rapid-wsl/modules/README.md @@ -0,0 +1,4 @@ +# Modules + +A few open-source projects are included. +By default, new modules are ignored. diff --git a/.github/actions/rapid-wsl/modules/demo/install_distro.sh b/.github/actions/rapid-wsl/modules/demo/install_distro.sh new file mode 100644 index 00000000000..12f396991ff --- /dev/null +++ b/.github/actions/rapid-wsl/modules/demo/install_distro.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +wsl -d $1 -e bash -c \ +' \ +touch /home/'"$3"'/.hushlogin; \ +touch /home/'"$3"'/.landscape; \ +touch /home/'"$3"'/.motd_shown; \ +apt update; \ +apt -y upgrade; \ +apt --yes install lsb-release gpg; \ +curl https://packages.microsoft.com/keys/microsoft.asc \ + | gpg --dearmor \ + | tee "/etc/apt/trusted.gpg.d/microsoft.gpg"; \ +echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" \ + | tee "/etc/apt/sources.list.d/dotnetdev.list"; \ +apt update; \ +apt remove azure-cli -y && apt autoremove -y; \ +curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash; \ +apt -y install unzip; \ +apt update; \ +curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add -; \ +apt-add-repository "deb [arch=$(dpkg --print-architecture)] https://apt.releases.hashicorp.com $(lsb_release -cs) main"; \ +apt install terraform -y; \ +apt install strongswan -y; \ +apt install strongswan-pki -y; \ +apt install make -y; \ +apt install jq -y; \ +' diff --git a/.github/actions/rapid-wsl/modules/generic/README.md b/.github/actions/rapid-wsl/modules/generic/README.md new file mode 100644 index 00000000000..804b07a0d75 --- /dev/null +++ b/.github/actions/rapid-wsl/modules/generic/README.md @@ -0,0 +1,17 @@ +# generic + +```sh +d="Ubuntu"; # Distro name +m="generic"; # Module name + +./rwsl init -d $d -m $m; +./rwsl status -d $d -m $m; +./rwsl backup -d $d -m $m; +./rwsl down -d $d -m $m; +./rwsl up -d $d -m $m; +./rwsl destroy -d $d -m $m; +./rwsl restore -d $d -m $m; +./rwsl test -d $d -m $m; + +``` +--- diff --git a/.github/actions/rapid-wsl/modules/macos/install_distro.sh b/.github/actions/rapid-wsl/modules/macos/install_distro.sh new file mode 100644 index 00000000000..355391d6a70 --- /dev/null +++ b/.github/actions/rapid-wsl/modules/macos/install_distro.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +wsl -d $1 -e bash -c \ +' \ +touch /home/'"$3"'/.hushlogin; \ +touch /home/'"$3"'/.landscape; \ +touch /home/'"$3"'/.motd_shown; \ +add-apt-repository -y ppa:apt-fast/stable; \ +apt-get update; \ +DEBIAN_FRONTEND=noninteractive apt-get install -y apt-fast; \ +echo debconf apt-fast/maxdownloads string 16 | debconf-set-selections; \ +echo debconf apt-fast/dlflag boolean true | debconf-set-selections; \ +echo debconf apt-fast/aptmanager string apt-get | debconf-set-selections; \ +echo "alias apt-get='\''apt-fast'\''" >> ~/.bashrc; \ +apt update; \ +apt-get update; \ +apt-get -y upgrade; \ +apt-get update; \ +apt-get install cmake automake clang-15 bison flex libfuse-dev libudev-dev pkg-config libc6-dev-i386 \ +gcc-multilib libcairo2-dev libgl1-mesa-dev curl libglu1-mesa-dev libtiff5-dev \ +libfreetype6-dev git git-lfs libelf-dev libxml2-dev libegl1-mesa-dev libfontconfig1-dev \ +libbsd-dev libxrandr-dev libxcursor-dev libgif-dev libavutil-dev libpulse-dev \ +libavformat-dev libavcodec-dev libswresample-dev libdbus-1-dev libxkbfile-dev \ +libssl-dev libstdc++-12-dev -y \ +apt-get update; \ +apt-get install make -y; \ +apt-get install jq -y; \ +' diff --git a/.github/actions/rapid-wsl/modules/macos/setup_distro.sh b/.github/actions/rapid-wsl/modules/macos/setup_distro.sh new file mode 100644 index 00000000000..17be50fe920 --- /dev/null +++ b/.github/actions/rapid-wsl/modules/macos/setup_distro.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +wsl -d $1 -u $3 -e bash -c \ +' \ +git clone --recursive https://github.com/darlinghq/darling.git \ +cd darling \ +tools/uninstall \ +mkdir build && cd build \ +cmake .. \ +make -j4 \ +sudo make install \ +darling shell \ +sudo rm -rf /Library/Developer/CommandLineTools \ +sudo xcode-select --install \ +curl -fsSL -o install.sh https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh \ +NONINTERACTIVE=1 /bin/bash install.sh \ +(echo; echo 'eval "$(/usr/local/bin/brew shellenv)"') >> /Users/macuser/.profile \ +eval "$(/usr/local/bin/brew shellenv)" \ +brew tap homebrew/core \ +brew install gradle \ +brew install docker \ +brew install docker-compose \ +brew install openjdk@17 postgresql +' diff --git a/.github/actions/rapid-wsl/modules/prime-data-ingestion/README.md b/.github/actions/rapid-wsl/modules/prime-data-ingestion/README.md new file mode 100644 index 00000000000..4d8fd26b96a --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-data-ingestion/README.md @@ -0,0 +1,18 @@ +# [prime-data-ingestion](https://github.com/CDCgov/prime-data-ingestion) + +```sh +d="Ubuntu-18.04"; # Distro name +m="prime-data-ingestion"; # Module name +u="primeuser"; # Username + +./rwsl init -d $d -m $m -u $u; +./rwsl status -d $d -m $m -u $u; +./rwsl backup -d $d -m $m -u $u; +./rwsl down -d $d -m $m -u $u; +./rwsl up -d $d -m $m -u $u; +./rwsl destroy -d $d -m $m -u $u; +./rwsl restore -d $d -m $m -u $u; +./rwsl test -d $d -m $m -u $u; + +``` +--- diff --git a/.github/actions/rapid-wsl/modules/prime-data-ingestion/install_distro.sh b/.github/actions/rapid-wsl/modules/prime-data-ingestion/install_distro.sh new file mode 100644 index 00000000000..6331536052b --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-data-ingestion/install_distro.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +wsl -d $1 -e bash -c \ +' \ +touch /home/'"$3"'/.hushlogin; \ +touch /home/'"$3"'/.landscape; \ +touch /home/'"$3"'/.motd_shown; \ +add-apt-repository -y ppa:apt-fast/stable; \ +apt-get update; \ +DEBIAN_FRONTEND=noninteractive apt-get install -y apt-fast; \ +echo debconf apt-fast/maxdownloads string 16 | debconf-set-selections; \ +echo debconf apt-fast/dlflag boolean true | debconf-set-selections; \ +echo debconf apt-fast/aptmanager string apt-get | debconf-set-selections; \ +echo "alias apt-get='\''apt-fast'\''" >> ~/.bashrc; \ +apt update; \ +apt-get update; \ +apt-get -y upgrade; \ +apt -y install openjdk-11-jdk; \ +apt-get --yes install lsb-release gpg; \ +curl https://packages.microsoft.com/keys/microsoft.asc \ + | gpg --dearmor \ + | tee "/etc/apt/trusted.gpg.d/microsoft.gpg"; \ +echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" \ + | tee "/etc/apt/sources.list.d/dotnetdev.list"; \ +apt-get update; \ +apt-get --yes install azure-functions-core-tools-3; \ +apt remove azure-cli -y && apt autoremove -y; \ +curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash; \ +apt-get install python3.6 -y; \ +apt-get install python3-venv -y; \ +apt-get -y install unzip; \ +apt -y install gradle; \ +wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -; \ +echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list; \ +apt-get update; \ +apt-get -y install postgresql-11; \ +curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose; \ +curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add -; \ +apt-add-repository "deb [arch=$(dpkg --print-architecture)] https://apt.releases.hashicorp.com $(lsb_release -cs) main"; \ +apt install terraform=1.0.5 -y; \ +apt-get install make -y; \ +chmod +x /usr/local/bin/docker-compose; \ +curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash; \ +' diff --git a/.github/actions/rapid-wsl/modules/prime-data-ingestion/setup_distro.sh b/.github/actions/rapid-wsl/modules/prime-data-ingestion/setup_distro.sh new file mode 100644 index 00000000000..13a24ecf8e2 --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-data-ingestion/setup_distro.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +wsl -d $1 -u $3 -e bash -c \ +' \ +docker --version; \ +git --version; \ +az --version; \ +mkdir -p ~/repos/; \ +cd ~/repos/; \ +git clone https://github.com/CDCgov/prime-public-health-data-infrastructure.git; \ +cd prime-public-health-data-infrastructure/; \ +python3 -m venv .venv; \ +source .venv/bin/activate; \ +sudo chown -R '"$3"':'"$3"' .; \ +echo "cd ~/repos/prime-public-health-data-infrastructure/" >>~/.bashrc; \ +' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/README.md b/.github/actions/rapid-wsl/modules/prime-reportstream/README.md new file mode 100644 index 00000000000..4434b96a816 --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-reportstream/README.md @@ -0,0 +1,18 @@ +# [prime-reportstream](https://github.com/CDCgov/prime-reportstream) + +```sh +d="Ubuntu-20.04"; # Distro name +m="prime-reportstream"; # Module name +u="primeuser"; # Username + +./rwsl init -d $d -m $m -u $u; +./rwsl status -d $d -m $m -u $u; +./rwsl backup -d $d -m $m -u $u; +./rwsl down -d $d -m $m -u $u; +./rwsl up -d $d -m $m -u $u; +./rwsl destroy -d $d -m $m -u $u; +./rwsl restore -d $d -m $m -u $u; +./rwsl test -d $d -m $m -u $u; + +``` +--- diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/down_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/down_distro.sh new file mode 100644 index 00000000000..6548eb67d22 --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-reportstream/down_distro.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +wsl -d $1 -u $3 -e bash -c \ +' \ +cd ~/repos/prime-reportstream/prime-router/; \ +./devenv-infrastructure.sh down; \ +docker-compose down --remove-orphans; \ +' + +docker stop $(docker ps -a --format "{{.ID}} {{.Names}}" | grep prime-router.*) +docker rm -f $(docker ps -a --format "{{.ID}} {{.Names}}" | grep prime-router.*) diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/fix_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/fix_distro.sh new file mode 100644 index 00000000000..e0ce6c7e640 --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-reportstream/fix_distro.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +wsl -d $1 -u $3 -e bash -c \ +' \ +cd ~/repos/prime-reportstream/prime-router/; \ +./cleanslate.sh --prune-volumes; \ +' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/install_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/install_distro.sh new file mode 100644 index 00000000000..eb47327cf99 --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-reportstream/install_distro.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +wsl -d $1 -e bash -c \ +' \ +touch /home/'"$3"'/.hushlogin; \ +touch /home/'"$3"'/.landscape; \ +touch /home/'"$3"'/.motd_shown; \ +add-apt-repository -y ppa:apt-fast/stable; \ +apt-get update; \ +DEBIAN_FRONTEND=noninteractive apt-get install -y apt-fast; \ +echo debconf apt-fast/maxdownloads string 16 | debconf-set-selections; \ +echo debconf apt-fast/dlflag boolean true | debconf-set-selections; \ +echo debconf apt-fast/aptmanager string apt-get | debconf-set-selections; \ +echo "alias apt-get='\''apt-fast'\''" >> ~/.bashrc; \ +apt update; \ +apt-get update; \ +apt-get -y upgrade; \ +apt -y install openjdk-17-jdk; \ +apt-get --yes install lsb-release gpg; \ +curl https://packages.microsoft.com/keys/microsoft.asc \ + | gpg --dearmor \ + | tee "/etc/apt/trusted.gpg.d/microsoft.gpg"; \ +echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" \ + | tee "/etc/apt/sources.list.d/dotnetdev.list"; \ +apt-get update; \ +apt-get --yes install azure-functions-core-tools-4; \ +apt remove azure-cli -y && apt autoremove -y; \ +curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash; \ +apt-get -y install unzip; \ +apt -y install gradle; \ +wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -; \ +echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list; \ +apt-get update; \ +apt-get -y install postgresql-11; \ +curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose; \ +curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add -; \ +apt-add-repository "deb [arch=$(dpkg --print-architecture)] https://apt.releases.hashicorp.com $(lsb_release -cs) main"; \ +apt install terraform=1.0.5 -y; \ +apt-get install strongswan -y; \ +apt-get install strongswan-pki -y; \ +apt-get install make -y; \ +apt-get install jq -y; \ +chmod +x /usr/local/bin/docker-compose; \ +curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash; \ +' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/setup_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/setup_distro.sh new file mode 100644 index 00000000000..9496aa4fe2f --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-reportstream/setup_distro.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +wsl -d $1 -u $3 -e bash -c \ +' \ +gradle --version; \ +docker --version; \ +git --version; \ +java --version; \ +psql --version; \ +az --version; \ +docker-compose --version; \ +mkdir -p ~/repos/; \ +cd ~/repos/; \ +git clone --filter=tree:0 https://github.com/CDCgov/prime-reportstream.git; \ +git config --global --add safe.directory prime-reportstream; \ +cd prime-reportstream/; \ +sudo chown -R '"$3"':'"$3"' .; \ +cd prime-router/; \ +echo "cleanslate.sh started"; \ +./cleanslate.sh --prune-volumes; \ +echo "cleanslate.sh finished"; \ +export $(xargs < .vault/env/.env.local); \ +echo "docker-compose started"; \ +docker-compose --file "docker-compose.build.yml" up --detach; \ +echo "docker-compose finished"; \ +echo "devenv-infrastructure.sh started"; \ +./devenv-infrastructure.sh; \ +echo "devenv-infrastructure.sh finished"; \ +./prime create-credential --type=UserPass \ + --persist=DEFAULT-SFTP \ + --user foo \ + --pass pass; \ +./prime multiple-settings \ + set --silent --input settings/organizations.yml; \ +cd ../; \ +echo "cd ~/repos/prime-reportstream/" >>~/.bashrc; \ +cp -n operations/app/src/environments/configurations/dev-sample.tfbackend operations/app/src/environments/configurations/dev.tfbackend; \ +cp -n operations/app/src/environments/configurations/dev-sample.tfvars operations/app/src/environments/configurations/dev.tfvars; \ +cp -n operations/app/src/environments/configurations/dev.tfbackend /mnt'${PWD}'/etc/; \ +cp -n operations/app/src/environments/configurations/dev.tfvars /mnt'${PWD}'/etc/; \ +cp /mnt'${PWD}'/etc/dev.tfbackend operations/app/src/environments/configurations/; \ +cp /mnt'${PWD}'/etc/dev.tfvars operations/app/src/environments/configurations/; \ +sudo chown -R '"$3"':'"$3"' .; \ +' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/status_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/status_distro.sh new file mode 100644 index 00000000000..557a514fbb9 --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-reportstream/status_distro.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +wsl -d $1 -u $3 -e bash -c \ +' \ +cd ~/repos/prime-reportstream/prime-router/; \ +./gradlew ; \ +' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/test-pipe_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/test-pipe_distro.sh new file mode 100644 index 00000000000..481fd09353a --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-reportstream/test-pipe_distro.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +wsl -d $1 -u $3 -e bash -c \ +' \ +cd ~/repos/prime-reportstream/; \ +sudo chown -R '"$3"':'"$3"' .git; \ +act -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04 -W .github/workflows/release.yml --graph; \ +' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/test_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/test_distro.sh new file mode 100644 index 00000000000..945615ddd83 --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-reportstream/test_distro.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +wsl -d $1 -u $3 -e bash -c \ +' \ +cd ~/repos/prime-reportstream/prime-router/; \ +DB_USER='\''prime'\''; \ +DB_PASSWORD='\''changeIT!'\''; \ +./gradlew ktlintCheck; \ +docker-compose -f docker-compose.postgres.yml up -d; \ +./gradlew package -x fatjar -Pshowtests; \ +mkdir -p .vault/env; \ +touch .vault/env/.env.local; \ +docker-compose -f docker-compose.yml up -d vault; \ +sleep 30; \ +docker-compose -f docker-compose.yml up -d prime_dev sftp azurite azurite-stage; \ +./gradlew reloadTables; \ +./gradlew reloadSettings; \ +./gradlew testIntegration -Pshowtests; \ +docker-compose exec -T sftp chmod 777 /home/foo/upload; \ +export $(xargs < .vault/env/.env.local); \ +./prime create-credential --type=UserPass --persist=DEFAULT-SFTP --user foo --pass pass; \ +./gradlew testSmoke; \ +' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/tips_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/tips_distro.sh new file mode 100644 index 00000000000..a89a2404634 --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-reportstream/tips_distro.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +echo -e " +## Run local terraform options: + +1. +(cd operations/app/src/environments/01-network/ && \ +sed -i -e 's/backend \"azurerm\"/backend \"local\"/g' main.tf && \ +terraform init && ../tf --verbose -c \"validate\") + +2. +(cd operations/ make tf-cmd TF_STAGE=\"01-network\" TF_CMD=\"tf validate\") + +## Run local frontend: + +sudo apt-get install node -y +sudo apt-get install npm -y +sudo npm install -g n +n 14 + +sudo apt remove cmdtest +sudo apt remove yarn +curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - +echo \"deb https://dl.yarnpkg.com/debian/ stable main\" | sudo tee /etc/apt/sources.list.d/yarn.list +sudo apt-get update +sudo apt-get install yarn -y + +sudo npm install pm2 -g +pm2 --name HelloWorld start npm -- start + +#pm2 ps +#pm2 delete 0 +#pm2 logs +" diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/up_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/up_distro.sh new file mode 100644 index 00000000000..09fffa53b0d --- /dev/null +++ b/.github/actions/rapid-wsl/modules/prime-reportstream/up_distro.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +wsl -d $1 -u $3 -e bash -c \ +' \ +cd ~/repos/prime-reportstream/prime-router; \ +docker-compose --file "docker-compose.build.yml" up --detach; \ +./devenv-infrastructure.sh up; \ +cd ../; \ +' diff --git a/.github/actions/rapid-wsl/rwsl b/.github/actions/rapid-wsl/rwsl new file mode 100644 index 00000000000..64549a1ae4d --- /dev/null +++ b/.github/actions/rapid-wsl/rwsl @@ -0,0 +1,4 @@ +#!/bin/bash + +source lib/waiting.sh +source lib/args.sh From 2a75e41d33b9963f0ae583b316ee5c3765d05e74 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Wed, 9 Oct 2024 16:55:24 -0700 Subject: [PATCH 19/32] Importing JosiahSiegel's stackoverflow_in_pg GitHub Action --- .github/actions/stackoverflow_in_pg | 1 + 1 file changed, 1 insertion(+) create mode 160000 .github/actions/stackoverflow_in_pg diff --git a/.github/actions/stackoverflow_in_pg b/.github/actions/stackoverflow_in_pg new file mode 160000 index 00000000000..c4c3bbe59d7 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg @@ -0,0 +1 @@ +Subproject commit c4c3bbe59d766c3e406ec77c502a198ebc14e1e2 From c4146904a72fba698475bcda635f4f916d3202b1 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Wed, 9 Oct 2024 16:56:04 -0700 Subject: [PATCH 20/32] Importing JosiahSiegel's terraform-templates GitHub Action --- .github/actions/terraform-templates | 1 + 1 file changed, 1 insertion(+) create mode 160000 .github/actions/terraform-templates diff --git a/.github/actions/terraform-templates b/.github/actions/terraform-templates new file mode 160000 index 00000000000..a11c320f28c --- /dev/null +++ b/.github/actions/terraform-templates @@ -0,0 +1 @@ +Subproject commit a11c320f28c7fdcd58750cc9b3e2ac4387acaf15 From b11a870cfd2a7a958b42978b743bccaa6ddaf0ec Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Wed, 9 Oct 2024 17:01:29 -0700 Subject: [PATCH 21/32] Correcting imported JosiahSiegel's repos as submodules --- .github/actions/stackoverflow_in_pg | 1 - .../actions/stackoverflow_in_pg/.gitignore | 145 ++++++++++ .github/actions/stackoverflow_in_pg/README.md | 87 ++++++ .../install_pg/install_centos.md | 105 ++++++++ .../install_pg/install_ubuntu.md | 98 +++++++ .../python_src/sample_xml/Badges.xml | 11 + .../python_src/sample_xml/Comments.xml | 11 + .../python_src/sample_xml/PostHistory.xml | 11 + .../python_src/sample_xml/PostLinks.xml | 11 + .../python_src/sample_xml/Posts.xml | 12 + .../python_src/sample_xml/Tags.xml | 11 + .../python_src/sample_xml/Users.xml | 11 + .../python_src/sample_xml/Votes.xml | 11 + .../python_src/so2pg-badges.py | 59 ++++ .../python_src/so2pg-comments.py | 62 +++++ .../python_src/so2pg-posthistory.py | 77 ++++++ .../python_src/so2pg-postlinks.py | 62 +++++ .../python_src/so2pg-posts.py | 113 ++++++++ .../python_src/so2pg-tags.py | 63 +++++ .../python_src/so2pg-users.py | 104 +++++++ .../python_src/so2pg-votes.py | 52 ++++ .../scripts/backup-restore.sh | 20 ++ .../scripts/convert-xml-2-sql.sh | 25 ++ .../stackoverflow_in_pg/scripts/push-2-pg.sh | 22 ++ .../actions/stackoverflow_in_pg/so-create.sql | 94 +++++++ .github/actions/terraform-templates | 1 - .../.devcontainer/Dockerfile | 4 + .../.devcontainer/devcontainer.json | 24 ++ .../.devcontainer/docker-compose.yml | 19 ++ .../.devops/adf-build-and-release.yml | 83 ++++++ .../.devops/adf-release-tasks.yml | 57 ++++ .../.devops/db-restore.yml | 73 +++++ .../.github/dependabot.yml | 12 + .../actions/terraform-templates/.gitignore | 36 +++ .../.scripts/data/psql_table_massive.sh | 35 +++ .../.scripts/data/psql_table_tiny.sh | 32 +++ .../.scripts/destroy_template.sh | 79 ++++++ .../.scripts/provision_template.sh | 203 ++++++++++++++ .../.scripts/utils/psql_install_16.sh | 8 + .../.scripts/utils/ssh_key_auth.sh | 32 +++ .../.scripts/utils/whats_running.sh | 1 + .github/actions/terraform-templates/README.md | 176 ++++++++++++ .../azure/env/01/config.tf | 35 +++ .../terraform-templates/azure/env/01/data.tf | 10 + .../terraform-templates/azure/env/01/main.tf | 62 +++++ .../azure/env/01/~locals.tf | 39 +++ .../azure/env/02/config.tf | 35 +++ .../terraform-templates/azure/env/02/data.tf | 15 ++ .../terraform-templates/azure/env/02/main.tf | 42 +++ .../azure/env/02/~locals.tf | 64 +++++ .../azure/env/03/README.md | 142 ++++++++++ .../azure/env/03/config.tf | 35 +++ .../terraform-templates/azure/env/03/data.tf | 18 ++ .../terraform-templates/azure/env/03/main.tf | 104 +++++++ .../azure/env/03/~locals.tf | 102 +++++++ .../azure/env/05/config.tf | 31 +++ .../terraform-templates/azure/env/05/data.tf | 18 ++ .../terraform-templates/azure/env/05/main.tf | 41 +++ .../azure/env/05/~locals.tf | 41 +++ .../azure/env/06/config.tf | 27 ++ .../terraform-templates/azure/env/06/data.tf | 10 + .../terraform-templates/azure/env/06/main.tf | 27 ++ .../azure/env/06/~locals.tf | 35 +++ .../azure/env/07/config.tf | 27 ++ .../terraform-templates/azure/env/07/data.tf | 10 + .../terraform-templates/azure/env/07/main.tf | 31 +++ .../azure/env/07/~locals.tf | 42 +++ .../modules/api_management/api_default.tf | 83 ++++++ .../azure/modules/api_management/main.tf | 13 + .../azure/modules/api_management/~inputs.tf | 4 + .../azure/modules/api_management/~outputs.tf | 0 .../azure/modules/azure_ad/v1/main.tf | 131 +++++++++ .../azure/modules/azure_ad/v1/~inputs.tf | 3 + .../azure/modules/azure_ad/v1/~outputs.tf | 3 + .../azure_ad/v2/files/FirstLogonCommands.xml | 22 ++ .../azure/modules/azure_ad/v2/files/winrm.ps1 | 21 ++ .../azure/modules/azure_ad/v2/main.tf | 93 +++++++ .../azure/modules/azure_ad/v2/~inputs.tf | 31 +++ .../azure/modules/azure_ad/v2/~outputs.tf | 12 + .../azure/modules/azure_mssql/v1/main.tf | 84 ++++++ .../azure/modules/azure_mssql/v1/~inputs.tf | 3 + .../azure/modules/azure_mssql/v1/~outputs.tf | 11 + .../modules/azure_mssql/v1/~providers.tf | 8 + .../azure/modules/azure_mssql/v2/databases.tf | 22 ++ .../modules/azure_mssql/v2/elastic_pool.tf | 22 ++ .../azure/modules/azure_mssql/v2/main.tf | 43 +++ .../azure/modules/azure_mssql/v2/~inputs.tf | 11 + .../azure/modules/azure_mssql/v2/~outputs.tf | 7 + .../azure/modules/cloudshell/main.tf | 162 +++++++++++ .../azure/modules/cloudshell/outputs.tf | 7 + .../azure/modules/cloudshell/variables.tf | 65 +++++ .../azure/modules/container_app/main.tf | 59 ++++ .../azure/modules/container_app/~inputs.tf | 3 + .../azure/modules/container_app/~outputs.tf | 0 .../azure/modules/container_instance/main.tf | 58 ++++ .../modules/container_instance/~inputs.tf | 27 ++ .../modules/container_instance/~outputs.tf | 3 + .../modules/cosmosdb_postgresql/v1/main.tf | 9 + .../modules/cosmosdb_postgresql/v1/~inputs.tf | 5 + .../cosmosdb_postgresql/v1/~outputs.tf | 3 + .../azure/modules/data_factory/v1/main.tf | 85 ++++++ .../azure/modules/data_factory/v1/~inputs.tf | 4 + .../azure/modules/data_factory/v1/~outputs.tf | 0 .../azure/modules/data_factory/v2/main.tf | 17 ++ .../azure/modules/data_factory/v2/~inputs.tf | 3 + .../azure/modules/data_factory/v2/~outputs.tf | 0 .../azure/modules/dns_zone/main.tf | 14 + .../azure/modules/dns_zone/~inputs.tf | 7 + .../azure/modules/dns_zone/~outputs.tf | 3 + .../azure/modules/init/v1/main.tf | 18 ++ .../azure/modules/init/v1/~inputs.tf | 2 + .../azure/modules/init/v1/~outputs.tf | 3 + .../azure/modules/init/v2/main.tf | 7 + .../azure/modules/init/v2/~inputs.tf | 2 + .../azure/modules/init/v2/~outputs.tf | 0 .../azure/modules/key_vault/main.tf | 23 ++ .../azure/modules/key_vault/~inputs.tf | 3 + .../azure/modules/key_vault/~outputs.tf | 7 + .../azure/modules/logic_app/main.tf | 110 ++++++++ .../modules/logic_app/workflows/workflow.json | 114 ++++++++ .../azure/modules/logic_app/~inputs.tf | 5 + .../azure/modules/logic_app/~outputs.tf | 16 ++ .../azure/modules/logic_app/~providers.tf | 8 + .../azure/modules/mssql_vm/main.tf | 254 ++++++++++++++++++ .../azure/modules/mssql_vm/~inputs.tf | 12 + .../azure/modules/private_endpoint/main.tf | 18 ++ .../azure/modules/private_endpoint/~inputs.tf | 7 + .../modules/private_endpoint/~outputs.tf | 0 .../azure/modules/storage_account/v1/main.tf | 26 ++ .../modules/storage_account/v1/~inputs.tf | 2 + .../modules/storage_account/v1/~outputs.tf | 3 + .../azure/modules/storage_account/v2/main.tf | 15 ++ .../modules/storage_account/v2/~inputs.tf | 8 + .../modules/storage_account/v2/~outputs.tf | 3 + .../azure/modules/vnet/main.tf | 22 ++ .../azure/modules/vnet/~inputs.tf | 4 + .../azure/modules/vnet/~outputs.tf | 11 + 137 files changed, 5058 insertions(+), 2 deletions(-) delete mode 160000 .github/actions/stackoverflow_in_pg create mode 100755 .github/actions/stackoverflow_in_pg/.gitignore create mode 100644 .github/actions/stackoverflow_in_pg/README.md create mode 100644 .github/actions/stackoverflow_in_pg/install_pg/install_centos.md create mode 100644 .github/actions/stackoverflow_in_pg/install_pg/install_ubuntu.md create mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/Badges.xml create mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/Comments.xml create mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/PostHistory.xml create mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/PostLinks.xml create mode 100755 .github/actions/stackoverflow_in_pg/python_src/sample_xml/Posts.xml create mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/Tags.xml create mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/Users.xml create mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/Votes.xml create mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-badges.py create mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-comments.py create mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-posthistory.py create mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-postlinks.py create mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-posts.py create mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-tags.py create mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-users.py create mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-votes.py create mode 100644 .github/actions/stackoverflow_in_pg/scripts/backup-restore.sh create mode 100644 .github/actions/stackoverflow_in_pg/scripts/convert-xml-2-sql.sh create mode 100644 .github/actions/stackoverflow_in_pg/scripts/push-2-pg.sh create mode 100755 .github/actions/stackoverflow_in_pg/so-create.sql delete mode 160000 .github/actions/terraform-templates create mode 100644 .github/actions/terraform-templates/.devcontainer/Dockerfile create mode 100644 .github/actions/terraform-templates/.devcontainer/devcontainer.json create mode 100644 .github/actions/terraform-templates/.devcontainer/docker-compose.yml create mode 100644 .github/actions/terraform-templates/.devops/adf-build-and-release.yml create mode 100644 .github/actions/terraform-templates/.devops/adf-release-tasks.yml create mode 100644 .github/actions/terraform-templates/.devops/db-restore.yml create mode 100644 .github/actions/terraform-templates/.github/dependabot.yml create mode 100644 .github/actions/terraform-templates/.gitignore create mode 100755 .github/actions/terraform-templates/.scripts/data/psql_table_massive.sh create mode 100755 .github/actions/terraform-templates/.scripts/data/psql_table_tiny.sh create mode 100755 .github/actions/terraform-templates/.scripts/destroy_template.sh create mode 100755 .github/actions/terraform-templates/.scripts/provision_template.sh create mode 100755 .github/actions/terraform-templates/.scripts/utils/psql_install_16.sh create mode 100755 .github/actions/terraform-templates/.scripts/utils/ssh_key_auth.sh create mode 100755 .github/actions/terraform-templates/.scripts/utils/whats_running.sh create mode 100644 .github/actions/terraform-templates/README.md create mode 100644 .github/actions/terraform-templates/azure/env/01/config.tf create mode 100644 .github/actions/terraform-templates/azure/env/01/data.tf create mode 100644 .github/actions/terraform-templates/azure/env/01/main.tf create mode 100644 .github/actions/terraform-templates/azure/env/01/~locals.tf create mode 100644 .github/actions/terraform-templates/azure/env/02/config.tf create mode 100644 .github/actions/terraform-templates/azure/env/02/data.tf create mode 100644 .github/actions/terraform-templates/azure/env/02/main.tf create mode 100644 .github/actions/terraform-templates/azure/env/02/~locals.tf create mode 100644 .github/actions/terraform-templates/azure/env/03/README.md create mode 100644 .github/actions/terraform-templates/azure/env/03/config.tf create mode 100644 .github/actions/terraform-templates/azure/env/03/data.tf create mode 100644 .github/actions/terraform-templates/azure/env/03/main.tf create mode 100644 .github/actions/terraform-templates/azure/env/03/~locals.tf create mode 100644 .github/actions/terraform-templates/azure/env/05/config.tf create mode 100644 .github/actions/terraform-templates/azure/env/05/data.tf create mode 100644 .github/actions/terraform-templates/azure/env/05/main.tf create mode 100644 .github/actions/terraform-templates/azure/env/05/~locals.tf create mode 100644 .github/actions/terraform-templates/azure/env/06/config.tf create mode 100644 .github/actions/terraform-templates/azure/env/06/data.tf create mode 100644 .github/actions/terraform-templates/azure/env/06/main.tf create mode 100644 .github/actions/terraform-templates/azure/env/06/~locals.tf create mode 100644 .github/actions/terraform-templates/azure/env/07/config.tf create mode 100644 .github/actions/terraform-templates/azure/env/07/data.tf create mode 100644 .github/actions/terraform-templates/azure/env/07/main.tf create mode 100644 .github/actions/terraform-templates/azure/env/07/~locals.tf create mode 100644 .github/actions/terraform-templates/azure/modules/api_management/api_default.tf create mode 100644 .github/actions/terraform-templates/azure/modules/api_management/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/api_management/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/api_management/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v1/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v1/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v1/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v2/files/FirstLogonCommands.xml create mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v2/files/winrm.ps1 create mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v2/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v2/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v2/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v1/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v1/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v1/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v1/~providers.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v2/databases.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v2/elastic_pool.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v2/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v2/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v2/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/cloudshell/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/cloudshell/outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/cloudshell/variables.tf create mode 100644 .github/actions/terraform-templates/azure/modules/container_app/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/container_app/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/container_app/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/container_instance/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/container_instance/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/container_instance/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/data_factory/v1/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/data_factory/v1/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/data_factory/v1/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/data_factory/v2/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/data_factory/v2/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/data_factory/v2/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/dns_zone/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/dns_zone/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/dns_zone/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/init/v1/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/init/v1/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/init/v1/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/init/v2/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/init/v2/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/init/v2/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/key_vault/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/key_vault/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/key_vault/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/logic_app/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/logic_app/workflows/workflow.json create mode 100644 .github/actions/terraform-templates/azure/modules/logic_app/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/logic_app/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/logic_app/~providers.tf create mode 100644 .github/actions/terraform-templates/azure/modules/mssql_vm/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/mssql_vm/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/private_endpoint/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/private_endpoint/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/private_endpoint/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/storage_account/v1/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/storage_account/v1/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/storage_account/v1/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/storage_account/v2/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/storage_account/v2/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/storage_account/v2/~outputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/vnet/main.tf create mode 100644 .github/actions/terraform-templates/azure/modules/vnet/~inputs.tf create mode 100644 .github/actions/terraform-templates/azure/modules/vnet/~outputs.tf diff --git a/.github/actions/stackoverflow_in_pg b/.github/actions/stackoverflow_in_pg deleted file mode 160000 index c4c3bbe59d7..00000000000 --- a/.github/actions/stackoverflow_in_pg +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c4c3bbe59d766c3e406ec77c502a198ebc14e1e2 diff --git a/.github/actions/stackoverflow_in_pg/.gitignore b/.github/actions/stackoverflow_in_pg/.gitignore new file mode 100755 index 00000000000..cbc82b7d9b1 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/.gitignore @@ -0,0 +1,145 @@ +/Temp-Scripts/* +/Org-Project*/* +/Sensitive/* +/Work/* +/Work-*/* +/Sql-Queries-Office/* +/Sql-Queries-Work/* +/Sql-Queries-Personal/* +/Sql-Queries-Private/* +/Sql-Queries-Volatile/* +/Volatile/* +/Private/* +/Private/Private.ssmssqlproj +/Sensitive/Sensitive.ssmssqlproj +/Volatile/Volatile.ssmssqlproj +/Work/Work.ssmssqlproj + + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +.vscode +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject +.vscode + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/.github/actions/stackoverflow_in_pg/README.md b/.github/actions/stackoverflow_in_pg/README.md new file mode 100644 index 00000000000..65af91c44b7 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/README.md @@ -0,0 +1,87 @@ + +# Requirement + +## install postgres + +On ubuntu, [install postgres on ubuntu](install_pg/install_ubuntu.md)\ +On centos, [install postgres on centos](install_pg/install_centos.md) + +# Import Stackoverflow database in pg + + +Original source: http://www.bortzmeyer.org/stackoverflow-to-postgresql.html + +The social network Stackoverflow (https://stackoverflow.com/) regularly publishes a dump of its database under a Creative Commons free licence. We can find dump file here: + +Main download link: https://archive.org/download/stackexchange + +File download link: +https://archive.org/download/stackexchange/stackoverflow.com-Badges.7z +https://archive.org/download/stackexchange/stackoverflow.com-Comments.7z +https://archive.org/download/stackexchange/stackoverflow.com-PostHistory.7z +https://archive.org/download/stackexchange/stackoverflow.com-PostLinks.7z +https://archive.org/download/stackexchange/stackoverflow.com-Posts.7z +https://archive.org/download/stackexchange/stackoverflow.com-Tags.7z +https://archive.org/download/stackexchange/stackoverflow.com-Users.7z +https://archive.org/download/stackexchange/stackoverflow.com-Votes.7z + +Extract the XML file of each downloaded file. + +At the time of writing this document, the dump files are from June 2017 +Each XML file store a class of Stack Overflow objects: + +| Object | 7zip file size | XML file size | SQL file size (parsed xml) | Lines in pg table | Pg table size | +|---------------|:----------------: |:-------------:|:--------------------------: |:-----------------:|--------------:| +| Badges | 166 MB | 2.51 GB | 1.2 GB | 22 997 200 lines | 1 658 MB | +| Comments | 3.16 GB | 14.2 GB | 10.6 GB | 58 187 400 lines | 11 GB | +| PostHistory | 18.2 GB | 88.9 GB | 66.4 GB | 93 512 900 lines | 54 GB | +| PostLinks | 57 MB | 492 MB | 215 MB | 4 214 710 lines | 242 MB | +| Posts | 10.4 GB | 52.3 GB | 38.1 GB | 36 149 100 lines | 31 GB | +| Tags | 704 KB | 4.18 MB | 1.67 MB | 49 306 lines | 2 808 kB | +| Users | 284 MB | 2.07 GB | 753 MB | 7 246 590 lines | 773 MB | +| Votes | 757 MB | 11.7 GB | 5.23 GB | 128 369 000 lines | 5 422 MB | + +## Transform xml file to sql file + +The python_src directory contains a python file per xml file to parse. Copy these `*.py` files and `*.xml` dump files in same directory. +> [!NOTE] +> Following code has been tested with Python 3.11.5 with Postgres 14. + +To launch the parser +```bash +python so2pg-<xml_to_parse>.py <xml_file>.xml > <parsed_xml>.sql +``` +If you want test it, in python directory there is a subdirectory named sample_xml. +```bash +#change to directory python_src +#launch +python so2pg-badges.py sample_xml > badges.sql + +#you obtain a sql file ready to load to postgres +``` + +## Load sql file in pg database + +```bash +# connect to user postgres +sudo su - postgres +# create your database +createdb --encoding=UTF-8 StackOverflow +#create database table in database StackOverflow +psql -U postgres -d StackOverflow -f so-create.sql +``` +All you have to do now is to load the sql files. +You can load them in the order you want. +I disable all integrity constraints. +Move to the directory where the sql files are located and start the loads like this + +```bash +psql -U postgres -d StackOverflow -f badges.sql +psql -U postgres -d StackOverflow -f comments.sql +psql -U postgres -d StackOverflow -f posthistory.sql +psql -U postgres -d StackOverflow -f postlinks.sql +psql -U postgres -d StackOverflow -f posts.sql +psql -U postgres -d StackOverflow -f tags.sql +psql -U postgres -d StackOverflow -f users.sql +psql -U postgres -d StackOverflow -f votes.sql +``` diff --git a/.github/actions/stackoverflow_in_pg/install_pg/install_centos.md b/.github/actions/stackoverflow_in_pg/install_pg/install_centos.md new file mode 100644 index 00000000000..1c5190abbfb --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/install_pg/install_centos.md @@ -0,0 +1,105 @@ +# Install postgresql from source on Centos 7 + +For example, I want to install a postgresql 9.6.3 (pg) database on Centos 7 \ +Pg will be installed in /opt/pgsql/9.6 \ +My data (PGDATA) will be in /opt/pgsql/9.6/data \ +My Logs (PGLOG) will be in /opt/pgsql/9.6/logs + +### Step 1: Download the sources of postgresql +Download link: https://ftp.postgresql.org/pub/source/v9.6.3/postgresql-9.6.3.tar.bz2 + +### Step 2: Install the required pacquets +```bash +yum install -y bison-devel readline-devel zlib-devel openssl-devel +yum groupinstall -y 'Development Tools' +``` + +### Step 3: Creating the postgres user with for homedir /opt/pgsql +```bash +sudo useradd -d /opt/pgsql -m -r -s /bin/bash postgres +``` + +### Step 4: Move postgres sources to /opt/pgsql/src +```bash +sudo mkdir -p /opt/pgsql/src +sudo mv postgresql-9.6.3.tar.bz2 /opt/pgsql/src/postgresql-9.6.3.tar.bz2 +sudo chown -R postgres: postgres /opt/pgsql/ +``` +### Step 5: Connect with postgres user +```bash +sudo su - postgres +``` + +### Step 6: Export the PATH, LD\_LIBRARY, PGDATA, PGLOG variables to .bashrc_profile +```bash +#config postgres +export PATH = /opt/pgsql/9.6/bin: $PATH +export LD_LIBRARY_PATH = /opt/pgsql/9.6/lib: $LD_LIBRARY_PATH +export PGDATA = /opt/pgsql/9.6/data +export PDLOG = /opt/pgsql/9.6/data/serverlog +``` +> You can add these lines to your .bashrc_profile + +### Step 7: Unpack the postgresql source +```bash +cd src/ +tar -xvjf postgresql-9.6.3.tar.bz2 +``` + +### Step 8: Install pg from sources +```bash +cd postgresql-9.6.3/ +./configure --prefix /opt/pgsql/9.6 +make +make install +``` + +### Step 9: Initialize the pg database +```bash +initdb -D $PGDATA -U postgres +``` + +### Step 10: Service settings for start pg on boot + +```bash +#with an account with a root/sudoer +sudo cp ~postgres/src/postgresql-9.6.3/contrib/start-scripts/linux /etc/init.d/postgresql +sudo chmod +x /etc/init.d/postgresql +``` +Modify the prefix, PGDATA, PGUSER and PGLOG variables in /etc/init.d/postgresql: +```bash +# Installation prefix +prefix = /opt/pgsql/9.6 + +# Data directory +PGDATA = "/opt/pgsql/9.6/data" + +# Who to run the postmaster as, usually "postgres". (NOT "root") +PGUSER = postgres + +# Where to keep a log file +PGLOG = "$PGDATA/ServerLog" +``` + +### Step 11: Starting the service +```bash +sudo chkconfig postgresql +sudo service postgresql start +``` + +### Step 12: Test Connect to Base +```bash +sudo su - postgres +psql +``` +> If you add this +> `export PATH = /opt/pgsql/9.6/bin: $PATH` +> In your .bash_profile, you can run the psql command without having to connect to the user system postgres +> By running the following command: +> `Psql -U postgres` + +### Step 13 (optional): Installing pgadmin4 +```bash +sudo yum install https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm +sudo yum install -y pgadmin4 +``` \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/install_pg/install_ubuntu.md b/.github/actions/stackoverflow_in_pg/install_pg/install_ubuntu.md new file mode 100644 index 00000000000..748c6225e37 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/install_pg/install_ubuntu.md @@ -0,0 +1,98 @@ +# Install Postgresql from source on Ubuntu 16.04 + +For example, I want to install a postgresql 9.6.3 (pg) database on xubuntu 16.04. +Pg will be installed in /opt/pgsql/9.6 \ +My data (PGDATA) will be in /opt/pgsql/9.6/data \ +My Logs (PGLOG) will be in /opt/pgsql/9.6/logs + +### Step 1: Download the sources of postgresql +Download link: https://ftp.postgresql.org/pub/source/v9.6.3/postgresql-9.6.3.tar.bz2 + +### Step 2: Install the required packages +```Bash +sudo apt install -y build-essential libreadline-dev zlib1g-dev flex bison libxml2-dev libxslt-dev libssl-dev libsystemd-dev +``` + +### Step 3: Creating the postgres user with for homedir /opt/pgsql +```Bash +sudo useradd -d /opt/pgsql -m -r -s /bin/bash postgres +``` + +### Step 4: Move postgres sources to /opt/pgsql/src +```Bash +sudo mkdir -p /opt/pgsql/src +sudo mv postgresql-9.6.3.tar.bz2 /opt/pgsql/src/postgresql-9.6.3.tar.bz2 +sudo chown -R postgres:postgres /opt/pgsql/ +``` +### Step 5: Connect with postgres user +```Bash +sudo su - postgres +``` + +### Step 6: Export the PATH, LD\_LIBRARY, PGDATA, PGLOG variables to .bashrc +```Bash +#config postgres +export PATH=/opt/pgsql/9.6/bin:$PATH +export LD_LIBRARY_PATH=/opt/pgsql/9.6/lib:$LD_LIBRARY_PATH +export PGDATA=/opt/pgsql/9.6/data +export PDLOG=/opt/pgsql/9.6/data/serverlog +``` +> You can add these lines to your .bashrc + +### Step 7: Uncompress the postgresql source +```Bash +cd src/ +tar -xvjf postgresql-9.6.3.tar.bz2 +``` + +### Step 8: Install pg from sources +```Bash +cd postgresql-9.6.3/ +./configure --prefix /opt/pgsql/9.6 --with-systemd +make +make install +``` + +### Step 9: Initialize the pg database +```Bash +initdb -D $PGDATA -U postgres +``` + +### Step 10: Service settings for start pg on boot + +```Bash +#with an account with a root/sudoer +sudo cp ~postgres/src/postgresql-9.6.3/contrib/start-scripts/linux /etc/init.d/postgresql +sudo chmod +x /etc/init.d/postgresql +``` +Modify the prefix, PGDATA, PGUSER and PGLOG variables in /etc/init.d/postgresql: +```Bash +# Installation prefix +prefix=/opt/pgsql/9.6 + +# Data directory +PGDATA="/opt/pgsql/9.6/data" + +# Who to run the postmaster as, usually "postgres". (NOT "root") +PGUSER=postgres + +# Where to keep a log file +PGLOG="$PGDATA/serverLog" +``` + +### Step 11: Start the Service +```Bash +sudo update-rc.d postgresql defaults +sudo systemctl start postgresql +``` + +### Step 12: Test connection to Base +```Bash +sudo su - postgres +psql +``` +> If you add this +> `export PATH = / opt / pgsql / 9.6 / bin: $ PATH` +> In your .bashrc, you can run the psql command without having to log on to the user system postgres +> By running the following command: +> `Psql -U postgres` diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Badges.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Badges.xml new file mode 100644 index 00000000000..c0c5504bfd0 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Badges.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<badges> + <row Id="82946" UserId="3718" Name="Teacher" Date="2008-09-15T08:55:03.923" Class="3" TagBased="False" /> + <row Id="82947" UserId="994" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> + <row Id="82949" UserId="3893" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> + <row Id="82950" UserId="4591" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> + <row Id="82951" UserId="5196" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> + <row Id="82952" UserId="2635" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> + <row Id="82953" UserId="1113" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> + <row Id="82954" UserId="4182" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> +</badges> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Comments.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Comments.xml new file mode 100644 index 00000000000..0e5a4666b5d --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Comments.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<comments> + <row Id="1" PostId="35314" Score="39" Text="not sure why this is getting downvoted -- it is correct! Double check it in your compiler if you don't believe him!" CreationDate="2008-09-06T08:07:10.730" UserId="1" /> + <row Id="2" PostId="35314" Score="8" Text="Yeah, I didn't believe it until I created a console app - but good lord! Why would they give you the rope to hang yourself! I hated that about VB.NET - the OrElse and AndAlso keywords!" CreationDate="2008-09-06T08:09:52.330" UserId="3" /> + <row Id="4" PostId="35195" Score="0" Text="I don't see an accepted answer now, I wonder how that got unaccepted. Incidentally, I would have marked an accepted answer based on the answers available at the time. Also, accepted doesn't mean Best :)" CreationDate="2008-09-06T08:42:16.980" UserId="380" /> + <row Id="9" PostId="47239" Score="0" Text="Jonathan: Wow! Thank you for all of that, you did an amazing amount of work!" CreationDate="2008-09-06T12:26:30.060" UserId="4550" /> + <row Id="10" PostId="45651" Score="6" Text="It will help if you give some details of which database you are using as techniques vary." CreationDate="2008-09-06T13:38:23.647" UserId="242" /> + <row Id="12" PostId="47428" Score="3" Text="One of the things that make a url user-friendly is 'discover-ability', meaning you can take a guess at url's simply from the address bar. http://i.love.pets.com/search/cats+dogs could easily lead to http://i.love.pets.com/search/pug+puppies etc" CreationDate="2008-09-06T13:51:47.843" UserId="4642" /> + <row Id="14" PostId="47481" Score="0" Text="I agree, both CodeRush and RefactorPro are visually impressive (most of which can be turned off BTW), but for navigating and refactoring Resharper is much better in my opinion of using both products." CreationDate="2008-09-06T14:15:46.897" UserId="4642" /> + <row Id="15" PostId="47373" Score="0" Text="Just wanted to mention that this is an excellent solution if you consider the problem to be linear (i.e. treating `A1B2` as a single number). I still think the problem is multi-dimensional, but I guess we'll just have to wait for the author to clarify :)" CreationDate="2008-09-06T14:30:40.217" UserId="2495" /> +</comments> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/PostHistory.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/PostHistory.xml new file mode 100644 index 00000000000..153319661e8 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/PostHistory.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<posthistory> + <row Id="6" PostHistoryTypeId="2" PostId="7" RevisionGUID="c30df0f4-a2d9-426e-a2dd-2cc3aa4d9205" CreationDate="2008-07-31T22:17:57.883" UserId="9" Text="The explicit cast to double in the first answer isn't necessary - identifying the constant as 5000.0 (or as 5000d) is sufficient." /> + <row Id="12" PostHistoryTypeId="1" PostId="17" RevisionGUID="0421fb42-a29a-4cb2-84ba-a828725410f8" CreationDate="2008-08-01T05:09:55.993" UserId="2" Text="Binary Data in MYSQL" /> + <row Id="13" PostHistoryTypeId="3" PostId="17" RevisionGUID="0421fb42-a29a-4cb2-84ba-a828725410f8" CreationDate="2008-08-01T05:09:55.993" UserId="2" Text="<database><mysql>" /> + <row Id="14" PostHistoryTypeId="2" PostId="17" RevisionGUID="0421fb42-a29a-4cb2-84ba-a828725410f8" CreationDate="2008-08-01T05:09:55.993" UserId="2" Text="How do I store binary data in mysql?" /> + <row Id="16" PostHistoryTypeId="2" PostId="18" RevisionGUID="0cfdfa19-039f-4645-8a48-1c316543b98f" CreationDate="2008-08-01T05:12:44.193" UserDisplayName="phpguy" Text="For a table like this:

 CREATE TABLE binary_data (
 id INT(4) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 description CHAR(50),
 bin_data LONGBLOB,
 filename CHAR(50),
 filesize CHAR(50),
 filetype CHAR(50)
 );

Here is a PHP example:

 <?php
 
 // store.php3 - by Florian Dittmer <dittmer@gmx.net>
 // Example php script to demonstrate the storing of binary files into
 // an sql database. More information can be found at http://www.phpbuilder.com/
 ?>
 
 <html>
 <head><title>Store binary data into SQL Database</title></head>
 <body>
 
 <?php
 // code that will be executed if the form has been submitted:
 
 if ($submit) {
 
 // connect to the database
 // (you may have to adjust the hostname,username or password)
 
 MYSQL_CONNECT("localhost","root","password");
 mysql_select_db("binary_data");
 
 $data = addslashes(fread(fopen($form_data, "r"), filesize($form_data)));
 
 $result=MYSQL_QUERY("INSERT INTO binary_data (description,bin_data,filename,filesize,filetype) ".
 "VALUES ('$form_description','$data','$form_data_name','$form_data_size','$form_data_type')");
 
 $id= mysql_insert_id();
 print "<p>This file has the following Database ID: <b>$id</b>";
 
 MYSQL_CLOSE();
 
 } else {
 
 // else show the form to submit new data:
 ?>
 
 <form method="post" action="<?php echo $PHP_SELF; ?>" enctype="multipart/form-data">
 File Description:<br>
 <input type="text" name="form_description" size="40">
 <input type="hidden" name="MAX_FILE_SIZE" value="1000000">
 <br>File to upload/store in database:<br>
 <input type="file" name="form_data" size="40">
 <p><input type="submit" name="submit" value="submit">
 </form>
 
 <?php
 
 }
 
 ?>
 
 </body>
 </html>" /> + <row Id="17" PostHistoryTypeId="1" PostId="25" RevisionGUID="617afb21-e2be-477c-bc74-95e3f319bc5d" CreationDate="2008-08-01T12:13:50.207" UserId="23" Text="How to use the C socket API in C++" /> + <row Id="18" PostHistoryTypeId="3" PostId="25" RevisionGUID="617afb21-e2be-477c-bc74-95e3f319bc5d" CreationDate="2008-08-01T12:13:50.207" UserId="23" Text="<c++><c><sockets><zos>" /> + <row Id="19" PostHistoryTypeId="2" PostId="25" RevisionGUID="617afb21-e2be-477c-bc74-95e3f319bc5d" CreationDate="2008-08-01T12:13:50.207" UserId="23" Text="I've been having issues getting the C sockets API to work properly in C++. Specifically, although I am including sys/socket.h, I still get compile time errors telling me that AF_INET is not defined. Am I missing something obvious, or could this be related to the fact that I'm doing this coding on z/OS and my problems are much more complicated? ;)" /> +</posthistory> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/PostLinks.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/PostLinks.xml new file mode 100644 index 00000000000..3f560d5c9c2 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/PostLinks.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<postlinks> + <row Id="19" CreationDate="2010-04-26T02:59:48.130" PostId="109" RelatedPostId="32412" LinkTypeId="1" /> + <row Id="37" CreationDate="2010-04-26T02:59:48.600" PostId="1970" RelatedPostId="617600" LinkTypeId="1" /> + <row Id="42" CreationDate="2010-04-26T02:59:48.647" PostId="2154" RelatedPostId="2451138" LinkTypeId="1" /> + <row Id="48" CreationDate="2010-04-26T02:59:48.740" PostId="2483" RelatedPostId="496096" LinkTypeId="1" /> + <row Id="52" CreationDate="2010-04-26T02:59:48.757" PostId="2572" RelatedPostId="209329" LinkTypeId="1" /> + <row Id="58" CreationDate="2010-04-26T02:59:48.943" PostId="3376" RelatedPostId="2187" LinkTypeId="1" /> + <row Id="59" CreationDate="2010-04-26T02:59:48.943" PostId="3376" RelatedPostId="18080" LinkTypeId="1" /> + <row Id="63" CreationDate="2010-04-26T02:59:49.083" PostId="3859" RelatedPostId="802573" LinkTypeId="1" /> +</postlinks> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Posts.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Posts.xml new file mode 100755 index 00000000000..8b2235f3376 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Posts.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<posts> + <row Id="44480366" PostTypeId="1" CreationDate="2017-06-11T04:27:05.437" Score="0" ViewCount="2" Body="<p>Novice: Firstly, I am just looking for a simplified explanation of the error in the context of the code below (ofcourse, without the use of <strong>unsafe</strong> keyword. I wrote it to for demonstration purposes (of Call by Reference) only.</p>

<p>This is the function being called:</p>

<pre><code>public int Add(int *x, int* y)
{
 int sumOfXY = *(x) + *(y);
 return sumOfXY;
}
</code></pre>

<p>Here is the Main():</p>

<pre><code>static void Main(string[] args)
{
 int a, b, sum=0;
 Console.Write("Enter the value for a: ");
 a = int.Parse(Console.ReadLine());
 Console.Write("Enter the value for b: ");
 b = int.Parse(Console.ReadLine());
 sum = Add(&amp;a, &amp;b);
}
</code></pre>

<p>So I am getting an error as stated in the title.</p>
" OwnerUserId="5715945" LastActivityDate="2017-06-11T04:27:05.437" Title="Call by Reference: Pointers and fixed size buffers may only be used in an unsafe context" Tags="<c#><pass-by-reference><unsafe-pointers>" AnswerCount="0" CommentCount="0" /> + <row Id="44480367" PostTypeId="2" ParentId="44480222" CreationDate="2017-06-11T04:27:42.857" Score="0" Body="<p>Actually, I do not think Postgresql has this feature to store history of a table though I do not use Postgresql but it's similar to MySQL.You can use two tables to implement your feature. Here is an example(just an example that uses MySQL syntax):</p>

<pre><code>Table 1: 
CREATE TABLE `dog_now`(
 `id` int(11) AUTO_INCREMENT ,
 `dog_id` int(11) DEAFULT '0', -- dog id
 `weight` float DEFAULT '0',
 `owner` varchar(32) DEFAULT '',
 PRIMARY KEY(`id`),
 UNIQUE KEY `idx_dog_id`(`dog_id`)
)
</code></pre>

<p>Table 1 is used to store the current state of a dog.</p>

<pre><code>Table 2:
CREATE TABLE `dog_history`(
 `id` int(11) AUTO_INCREMENT,
 `dog_id` int(11) DEAFULT '0', -- dog id
 `weight` float DEFAULT '0',
 `owner` varchar(32) DEFAULT '',
 PRIMARY KEY (`id`),
 KEY `idx_dog_id`(`dog_id`)
)
</code></pre>

<p>Table 2 is used to store the history of a dog.</p>

<p>Now maybe you want to know how to store the data.
When you want to save the new dog data in database, please query the dog_now table to achieve the data by dog_id. And put the data into dog_history table, update new dog data in dog_now table.</p>
" OwnerUserId="3978491" LastActivityDate="2017-06-11T04:27:42.857" CommentCount="0" /> + <row Id="44480368" PostTypeId="2" ParentId="37219311" CreationDate="2017-06-11T04:28:14.400" Score="0" Body="<p>yes this is possible:</p>

<p><strong>Swift 3</strong></p>

<pre><code>protocol Thing {
 static func genericFunction()
}

//... in another file

var things:[Thing] = []

for thing in things {
 type(of: thing).genericFunction()
}
</code></pre>
" OwnerUserId="7263704" LastActivityDate="2017-06-11T04:28:14.400" CommentCount="0" /> + <row Id="44480369" PostTypeId="1" CreationDate="2017-06-11T04:28:17.503" Score="0" ViewCount="2" Body="<p>My code takes a image from letImageVIEW and tries to place the image on lets just call it imageviewB. The problem is that if there is no image on letImageVIEW the app crashes. How can I use like a check function to prevent my app crashing if there is no image on letImageVIEW. </p>

<pre><code> @IBAction func add(_ sender: Any) {


 let left:UIImage = letImageVIEW.image!
 left.draw(in: CGRect(x: newSize2.width/10.0,y: newSize2.height/8.9,width: newSize2.width/2.5,height: newSize2.height/1.29), blendMode:CGBlendMode.normal, alpha:1.0)
 }
</code></pre>
" OwnerUserId="8105388" LastActivityDate="2017-06-11T04:28:17.503" Title="Create check for button that will create a error (swift3)" Tags="<ios><button><swift3><compiler-errors><null>" AnswerCount="0" CommentCount="0" /> + <row Id="44480370" PostTypeId="2" ParentId="40339914" CreationDate="2017-06-11T04:28:44.900" Score="0" Body="<p>Please check the link below if this answer solves your query.</p>

<p><a href="https://stackoverflow.com/questions/34570064/gmail-fsockopen-ssl-operation-failed-error-with-codeigniter-and-xampp?answertab=votes#tab-top">GMail fsockopen(): SSL operation failed error with Codeigniter and XAMPP</a></p>
" OwnerUserId="4133851" LastActivityDate="2017-06-11T04:28:44.900" CommentCount="0" /> + <row Id="44480371" PostTypeId="2" ParentId="44480279" CreationDate="2017-06-11T04:28:51.170" Score="0" Body="<p>You have created table <code>songs</code> at first and then you have created table <code>albums</code>. When you try to add foreign key <code>album_id</code> at <code>songs</code> table, <code>albums</code> table hasn't been created so that you can't add foreign key to a table with out creation of that table.</p>

<p>So, What you need to is, Create <code>albums</code> table before <code>songs</code> table.</p>
" OwnerUserId="6708661" LastActivityDate="2017-06-11T04:28:51.170" CommentCount="0" /> + <row Id="44480372" PostTypeId="2" ParentId="44447790" CreationDate="2017-06-11T04:28:59.180" Score="0" Body="<p>Your problem is that the images in your banner are too wide to fit in most viewports, so they force the page to be wider.</p>

<p>There is no easy way to correct this purely with CSS without either causing the images to overlap each other or warping the images' aspect ratio.</p>

<p>This CSS will fix the horizontal scrolling, but it will squish the first and last images:</p>

<pre><code>.dittomall-banner img {
 width: 33%;
}
</code></pre>

<p>To truly fix things so that your images can maintain their original aspect ratio and also not cause the page to be too wide, you should edit the images so that they are cropped tightly, then use a <code>background-color</code> on <code>.dittomall-banner</code> that's the same as the background color in the first and third images. Then, center the images within the banner.</p>

<p>Assuming you crop the images so they are all equally sized (e.g., 300px x 100px), your final HTML and CSS could look like this:</p>

<p>HTML:</p>

<pre><code>&lt;div class="dittomall-banner"&gt;
 &lt;div class="banner-inner-wrapper"&gt;
 &lt;img src="http://www.dittomall.com/theme/dittomall/img/banner1.png" alt="banner1"&gt;
 &lt;img src="http://www.dittomall.com/theme/dittomall/img/banner2.png" alt="banner2"&gt;
 &lt;img src="http://www.dittomall.com/theme/dittomall/img/banner3.png" alt="banner3"&gt;
 &lt;/div&gt;
&lt;/div&gt;
</code></pre>

<p>CSS:</p>

<pre><code>.dittomall-banner {
 display: flex;
 background-color: #ed8c7b;
}
.banner-inner-wrapper {
 margin: 0 auto;
}
</code></pre>
" OwnerUserId="6656062" LastActivityDate="2017-06-11T04:28:59.180" CommentCount="0" /> + <row Id="44480373" PostTypeId="1" CreationDate="2017-06-11T04:28:59.997" Score="0" ViewCount="1" Body="<p>I defined a type something like this:</p>

<pre><code>type Similarity = (String, Int) =&gt; Boolean
</code></pre>

<p>Now I created a function that expects this type as an argument to method like this:</p>

<pre><code>def calculate(param: Similarity) = {
 println("hi")
 }
</code></pre>

<p>My question is how can I pass arguments to calculate function? for instance if I want to pass a string and a number?</p>
" OwnerUserId="3839735" LastActivityDate="2017-06-11T04:28:59.997" Title="How to pass type argument to method" Tags="<scala>" AnswerCount="0" CommentCount="0" /> + <row Id="44480374" PostTypeId="2" ParentId="44480319" CreationDate="2017-06-11T04:29:07.157" Score="0" Body="<p>You can do this way,</p>

<pre><code>private void finalizeButton_Click(object sender, EventArgs e)
 {
 foreach (var cmbObj in cartComboBox.Items)
 {
 if (prices.Keys.Contains(cmbObj.ToString()))
 {
 cartListView.Items.Add(cmbObj.ToString());

 }
 }

 }
</code></pre>
" OwnerUserId="1749403" LastActivityDate="2017-06-11T04:29:07.157" CommentCount="0" /> +</posts> diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Tags.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Tags.xml new file mode 100644 index 00000000000..7b6dfcc6918 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Tags.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<tags> + <row Id="1" TagName=".net" Count="253350" ExcerptPostId="3624959" WikiPostId="3607476" /> + <row Id="2" TagName="html" Count="660738" ExcerptPostId="3673183" WikiPostId="3673182" /> + <row Id="3" TagName="javascript" Count="1404363" ExcerptPostId="3624960" WikiPostId="3607052" /> + <row Id="4" TagName="css" Count="474072" ExcerptPostId="3644670" WikiPostId="3644669" /> + <row Id="5" TagName="php" Count="1085188" ExcerptPostId="3624936" WikiPostId="3607050" /> + <row Id="8" TagName="c" Count="252720" ExcerptPostId="3624961" WikiPostId="3607013" /> + <row Id="9" TagName="c#" Count="1102833" ExcerptPostId="3624962" WikiPostId="3607007" /> + <row Id="10" TagName="c++" Count="518248" ExcerptPostId="3624963" WikiPostId="3606997" /> +</tags> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Users.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Users.xml new file mode 100644 index 00000000000..0dc967c79b7 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Users.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<users> + <row Id="-1" Reputation="1" CreationDate="2008-07-31T00:00:00.000" DisplayName="Community" LastAccessDate="2008-08-26T00:16:53.810" WebsiteUrl="http://meta.stackexchange.com/" Location="on the server farm" AboutMe="<p>Hi, I'm not really a person.</p>

<p>I'm a background process that helps keep this site clean!</p>

<p>I do things like</p>

<ul>
<li>Randomly poke old unanswered questions every hour so they get some attention</li>
<li>Own community questions and answers so nobody gets unnecessary reputation from them</li>
<li>Own downvotes on spam/evil posts that get permanently deleted</li>
<li>Own suggested edits from anonymous users</li>
<li><a href="http://meta.stackexchange.com/a/92006">Remove abandoned questions</a></li>
</ul>
" Views="649" UpVotes="210564" DownVotes="829321" AccountId="-1" /> + <row Id="1" Reputation="40983" CreationDate="2008-07-31T14:22:31.287" DisplayName="Jeff Atwood" LastAccessDate="2017-06-10T03:51:00.423" WebsiteUrl="http://www.codinghorror.com/blog/" Location="El Cerrito, CA" AboutMe="<p><a href="http://www.codinghorror.com/blog/archives/001169.html" rel="nofollow">Stack Overflow Valued Associate #00001</a></p>

<p>Wondering how our software development process works? <a href="http://www.youtube.com/watch?v=08xQLGWTSag" rel="nofollow">Take a look!</a></p>

<p>Find me <a href="http://twitter.com/codinghorror" rel="nofollow">on twitter</a>, or <a href="http://www.codinghorror.com/blog" rel="nofollow">read my blog</a>. Don't say I didn't warn you <em>because I totally did</em>.</p>

<p>However, <a href="http://www.codinghorror.com/blog/2012/02/farewell-stack-exchange.html" rel="nofollow">I no longer work at Stack Exchange, Inc</a>. I'll miss you all. Well, <em>some</em> of you, anyway. :)</p>
" Views="266244" UpVotes="3341" DownVotes="1307" ProfileImageUrl="https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8?s=128&amp;d=identicon&amp;r=PG" Age="47" AccountId="1" /> + <row Id="2" Reputation="3005" CreationDate="2008-07-31T14:22:31.287" DisplayName="Geoff Dalgas" LastAccessDate="2017-06-10T15:11:17.273" WebsiteUrl="http://stackoverflow.com" Location="Corvallis, OR, United States" AboutMe="<p>Developer on the Stack Overflow team. Find me on</p>

<p><a href="http://www.twitter.com/SuperDalgas" rel="nofollow">Twitter</a>
<br><br>
<a href="http://blog.stackoverflow.com/2009/05/welcome-stack-overflow-valued-associate-00003/">Stack Overflow Valued Associate #00003</a></p>
" Views="21519" UpVotes="639" DownVotes="88" Age="40" AccountId="2" /> + <row Id="3" Reputation="12887" CreationDate="2008-07-31T14:22:31.287" DisplayName="Jarrod Dixon" LastAccessDate="2017-06-11T02:09:54.423" WebsiteUrl="http://jarroddixon.com" Location="Raleigh, NC, United States" AboutMe="<p><a href="http://blog.stackoverflow.com/2009/01/welcome-stack-overflow-valued-associate-00002/">Developer on the Stack Overflow team</a>.</p>

<p>Was dubbed <strong>SALTY SAILOR</strong> by Jeff Atwood, as filth and flarn would oft-times fly when dealing with a particularly nasty bug!</p>

<ul>
<li>Twitter me: <a href="http://twitter.com/jarrod_dixon" rel="nofollow noreferrer">jarrod_dixon</a></li>
<li>Email me: jarrod.m.dixon@gmail.com</li>
</ul>
" Views="22600" UpVotes="6747" DownVotes="100" Age="38" AccountId="3" /> + <row Id="4" Reputation="27228" CreationDate="2008-07-31T14:22:31.317" DisplayName="Joel Spolsky" LastAccessDate="2017-06-08T18:19:58.113" WebsiteUrl="http://www.joelonsoftware.com/" Location="New York, NY" AboutMe="<p>I am:</p>

<ul>
<li>the co-founder and CEO of <a href="http://stackexchange.com">Stack Exchange</a></li>
<li>the co-founder of <a href="http://www.fogcreek.com" rel="nofollow">Fog Creek Software</a></li>
<li>the creator and chairman of the board of <a href="http://trello.com" rel="nofollow">Trello</a></li>
<li>owner of Taco, the most famous Siberian Husky on the Upper West Side.</li>
</ul>

<p>You can find me on Twitter (as <a href="http://twitter.com/spolsky" rel="nofollow">@spolsky</a>) or on my rarely-updated blog, <a href="http://joelonsoftware.com" rel="nofollow">Joel on Software</a>.</p>
" Views="69136" UpVotes="785" DownVotes="96" ProfileImageUrl="https://i.stack.imgur.com/C5gBG.jpg?s=128&g=1" AccountId="4" /> + <row Id="5" Reputation="36565" CreationDate="2008-07-31T14:22:31.317" DisplayName="Jon Galloway" LastAccessDate="2017-05-20T19:07:51.353" WebsiteUrl="http://weblogs.asp.net/jgalloway/" Location="San Diego, CA" AboutMe="<p>Technical Evangelist at Microsoft, specializing in ASP.NET MVC.</p>

<p>I don't use this site anymore because the moderators close or delete far too many of the useful questions.</p>
" Views="10398" UpVotes="778" DownVotes="34" Age="47" AccountId="5" /> + <row Id="8" Reputation="942" CreationDate="2008-07-31T21:33:24.057" DisplayName="Eggs McLaren" LastAccessDate="2012-10-15T22:00:45.510" WebsiteUrl="" Location="" AboutMe="<p>This is a puppet test account I use to validate "regular user" stuff on the site</p>
<p>-- <a href="http://stackoverflow.com/users/1/jeff-atwood" rel="nofollow">Jeff Atwood</a>" Views="5576" UpVotes="12" DownVotes="9" AccountId="6" /> + <row Id="9" Reputation="12364" CreationDate="2008-07-31T21:35:26.517" DisplayName="Kevin Dente" LastAccessDate="2017-06-07T19:05:51.593" WebsiteUrl="http://weblogs.asp.net/kdente" Location="Oakland, CA" AboutMe="<p>Independent software engineer</p>
" Views="4244" UpVotes="45" DownVotes="4" Age="46" AccountId="7" /> +</users> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Votes.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Votes.xml new file mode 100644 index 00000000000..4d09bde6005 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Votes.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<votes> + <row Id="1" PostId="1" VoteTypeId="2" CreationDate="2008-07-31T00:00:00.000" /> + <row Id="2" PostId="3" VoteTypeId="2" CreationDate="2008-07-31T00:00:00.000" /> + <row Id="3" PostId="2" VoteTypeId="2" CreationDate="2008-07-31T00:00:00.000" /> + <row Id="4" PostId="4" VoteTypeId="2" CreationDate="2008-07-31T00:00:00.000" /> + <row Id="5" PostId="6" VoteTypeId="2" CreationDate="2008-07-31T00:00:00.000" /> + <row Id="6" PostId="7" VoteTypeId="2" CreationDate="2008-07-31T00:00:00.000" /> + <row Id="7" PostId="13" VoteTypeId="2" CreationDate="2008-08-01T00:00:00.000" /> + <row Id="9" PostId="4" VoteTypeId="2" CreationDate="2008-08-01T00:00:00.000" /> +</votes> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-badges.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-badges.py new file mode 100644 index 00000000000..20139ca1780 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/so2pg-badges.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +import xml.etree.cElementTree as ElementTree +import os +import sys +import re + +db = "so" +encoding = "UTF-8" + +def escape(str): + str = re.sub("\s", " ", str) + # \ are special for Python strings *and* for regexps. Hence the + # multiple escaping. Here,we just replace every \ by \\ for + # PostgreSQL + str = re.sub("\\\\", "\\\\\\\\", str) + return str + +def tag_parse(str): + index = 0 + while index < len(str): + if str[index] == '<': + try: + end_tag = str[index:].index('>') + yield str[(index+1):(index+end_tag)] + index += end_tag + 1 + except ValueError: + raise Exception("Tag parsing error in \"%s\"" % str); + else: + raise Exception("Tag parsing error in \"%s\"" % str); + +if len(sys.argv) != 2: + raise Exception("Usage: %s so-files-directory" % sys.argv[0]) + +#os.chdir(sys.argv[1]) + +filename = "Badges.xml" +badges = ElementTree.iterparse(filename) +print ("COPY Badges (id, userid, name, date, badgeclass, tagbased) FROM stdin;") + +for event, badge in badges: + if event == "end" and badge.tag == "row": + id = int(badge.attrib["Id"]) + + userid = int(badge.attrib["UserId"]) + + name = escape(badge.attrib["Name"]) + + date = escape(badge.attrib["Date"]) + + badgeclass = badge.attrib["Class"] + + tagbased = badge.attrib["TagBased"] + + print ("%i\t%s\t%s\t%s\t%s\t%s" % (id, userid, name.encode(encoding), date, badgeclass, tagbased)) + badge.clear() + +print ("\.") + diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-comments.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-comments.py new file mode 100644 index 00000000000..11e07bed35e --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/so2pg-comments.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +import xml.etree.cElementTree as ElementTree +import os +import sys +import re + +db = "so" +encoding = "UTF-8" + +def escape(str): + str = re.sub("\s", " ", str) + # \ are special for Python strings *and* for regexps. Hence the + # multiple escaping. Here,we just replace every \ by \\ for + # PostgreSQL + str = re.sub("\\\\", "\\\\\\\\", str) + return str + +def tag_parse(str): + index = 0 + while index < len(str): + if str[index] == '<': + try: + end_tag = str[index:].index('>') + yield str[(index+1):(index+end_tag)] + index += end_tag + 1 + except ValueError: + raise Exception("Tag parsing error in \"%s\"" % str); + else: + raise Exception("Tag parsing error in \"%s\"" % str); + +if len(sys.argv) != 2: + raise Exception("Usage: %s so-files-directory" % sys.argv[0]) + +#os.chdir(sys.argv[1]) + +filename = "Comments.xml" +comments = ElementTree.iterparse(filename) +print ("COPY Comments (id, postid, score, text, creation, userid) FROM stdin;") + +for event, comment in comments: + if event == "end" and comment.tag == "row": + id = int(comment.attrib["Id"]) + + postid = int(comment.attrib["PostId"]) + + score = int(comment.attrib["Score"]) + + text = escape(comment.attrib["Text"]) + + creation = comment.attrib["CreationDate"] + + if "UserId" in comment.attrib: + userid = int(comment.attrib["UserId"]) + else: + userid = -1 + + print ("%i\t%s\t%s\t%s\t%s\t%s" % (id, postid, score, text.encode(encoding), creation, userid)) + comment.clear() + +print ("\.") + diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-posthistory.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-posthistory.py new file mode 100644 index 00000000000..10f14b54aaa --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/so2pg-posthistory.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +import xml.etree.cElementTree as ElementTree +import os +import sys +import re + +db = "so" +encoding = "UTF-8" + +def escape(str): + str = re.sub("\s", " ", str) + # \ are special for Python strings *and* for regexps. Hence the + # multiple escaping. Here,we just replace every \ by \\ for + # PostgreSQL + str = re.sub("\\\\", "\\\\\\\\", str) + return str + +def tag_parse(str): + index = 0 + while index < len(str): + if str[index] == '<': + try: + end_tag = str[index:].index('>') + yield str[(index+1):(index+end_tag)] + index += end_tag + 1 + except ValueError: + raise Exception("Tag parsing error in \"%s\"" % str); + else: + raise Exception("Tag parsing error in \"%s\"" % str); + +if len(sys.argv) != 2: + raise Exception("Usage: %s so-files-directory" % sys.argv[0]) + +#os.chdir(sys.argv[1]) + +filename = "PostHistory.xml" +postHistory = ElementTree.iterparse(filename) +tags = {} +tag_id = 1 +print ("COPY posthistory (id, type, postid, revisionguid, creation, userid, userdisplaymame, text) FROM stdin;") + +for event, post in postHistory: + if event == "end" and post.tag == "row": + id = int(post.attrib["Id"]) + + type = int(post.attrib["PostHistoryTypeId"]) + + postid = int(post.attrib["PostId"]) + + revisionguid = post.attrib["RevisionGUID"] + + creation = post.attrib["CreationDate"] + + #if post.attrib.has_key("UserId"): + if "UserId" in post.attrib: + userid = int(post.attrib["UserId"]) + else: + userid = -1 + + #if post.attrib.has_key("UserDisplayName"): + if "UserDisplayName" in post.attrib: + userdisplaymame = escape(post.attrib["UserDisplayName"]) + else: + userdisplaymame = "\n" + + #if post.attrib.has_key("Text"): + if "Text" in post.attrib: + text = escape(post.attrib["Text"]) + else: + text = "\n" + + print ("%i\t%s\t%s\t%s\t%s\t%s\t%s\t%s" % (id, type, postid, revisionguid, creation, userid, userdisplaymame.encode(encoding), text.encode(encoding))) + post.clear() + +print ("\.") + diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-postlinks.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-postlinks.py new file mode 100644 index 00000000000..438aae5681a --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/so2pg-postlinks.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +import xml.etree.cElementTree as ElementTree +import os +import sys +import re + +db = "so" +encoding = "UTF-8" + +def escape(str): + str = re.sub("\s", " ", str) + # \ are special for Python strings *and* for regexps. Hence the + # multiple escaping. Here,we just replace every \ by \\ for + # PostgreSQL + str = re.sub("\\\\", "\\\\\\\\", str) + return str + +def tag_parse(str): + index = 0 + while index < len(str): + if str[index] == '<': + try: + end_tag = str[index:].index('>') + yield str[(index+1):(index+end_tag)] + index += end_tag + 1 + except ValueError: + raise Exception("Tag parsing error in \"%s\"" % str); + else: + raise Exception("Tag parsing error in \"%s\"" % str); + +if len(sys.argv) != 2: + raise Exception("Usage: %s so-files-directory" % sys.argv[0]) + +#os.chdir(sys.argv[1]) + +filename = "PostLinks.xml" +postlinks = ElementTree.iterparse(filename) +print ("COPY postlinks (id, creation, postid, relatedpostid, linktypeid) FROM stdin;") +for event, postlink in postlinks: + if event == "end" and postlink.tag == "row": + id = int(postlink.attrib["Id"]) + + creation = postlink.attrib["CreationDate"] + + postid = int(postlink.attrib["PostId"]) + + #if postlink.attrib.has_key("RelatedPostId"): + if "RelatedPostId" in postlink.attrib: + relatedpostid = postlink.attrib["RelatedPostId"] + else: + relatedpostid = "\n" + + if "LinkTypeId" in postlink.attrib: + #if postlink.attrib.has_key("LinkTypeId"): + linktypeid = postlink.attrib["LinkTypeId"] + else: + linktypeid = "\n" + + print ("%i\t%s\t%s\t%s\t%s" % (id, creation, postid, relatedpostid, linktypeid)) + postlink.clear() +print ("\.") \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-posts.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-posts.py new file mode 100644 index 00000000000..748fca1186f --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/so2pg-posts.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python + +import xml.etree.cElementTree as ElementTree +import os +import sys +import re + +db = "so" +encoding = "UTF-8" + +def escape(str): + str = re.sub("\s", " ", str) + # \ are special for Python strings *and* for regexps. Hence the + # multiple escaping. Here,we just replace every \ by \\ for + # PostgreSQL + str = re.sub("\\\\", "\\\\\\\\", str) + return str + +def tag_parse(str): + index = 0 + while index < len(str): + if str[index] == '<': + try: + end_tag = str[index:].index('>') + yield str[(index+1):(index+end_tag)] + index += end_tag + 1 + except ValueError: + raise Exception("Tag parsing error in \"%s\"" % str); + else: + raise Exception("Tag parsing error in \"%s\"" % str); + +if len(sys.argv) != 2: + raise Exception("Usage: %s so-files-directory" % sys.argv[0]) + +#os.chdir(sys.argv[1]) + +filename = "Posts.xml" +posts = ElementTree.iterparse(filename) +tags = {} +tag_id = 1 +print ("COPY posts (id, type, creation, score, viewcount, title, body, userid, lastactivity, tags, answercount, commentcount) FROM stdin;") + +for event, post in posts: + if event == "end" and post.tag == "row": + id = int(post.attrib["Id"]) + + #if post.attrib.has_key("PostTypeId"): + if "PostTypeId" in post.attrib: + type = int(post.attrib["PostTypeId"]) + else: + type = "\n" + + creation = post.attrib["CreationDate"] + + #if post.attrib.has_key("Score"): + if "Score" in post.attrib: + score = int(post.attrib["Score"]) + else: + score = "-1" + + #if post.attrib.has_key("ViewCount"): + if "ViewCount" in post.attrib: + viewcount = int(post.attrib["ViewCount"]) + else: + viewcount = "-1" + + #if post.attrib.has_key("Title"): + if "Title" in post.attrib: + title = escape(post.attrib["Title"]) + else: + title = "\n" + + #if post.attrib.has_key("Body"): + if "Body" in post.attrib: + body = escape(post.attrib["Body"]) + else: + body = "\n" + + #if post.attrib.has_key("OwnerUserId"): + if "OwnerUserId" in post.attrib: + owner = post.attrib["OwnerUserId"] + else: + owner = "-1" + + #if post.attrib.has_key("LastActivityDate"): + if "LastActivityDate" in post.attrib: + lastactivity = post.attrib["LastActivityDate"] + else: + lastactivity = "\n" + + #if post.attrib.has_key("Tags"): + if "Tags" in post.attrib: + tags = escape(post.attrib["Tags"]) + else: + tags = "\n" + + #if post.attrib.has_key("AnswerCount"): + if "AnswerCount" in post.attrib: + answercount = int(post.attrib["AnswerCount"]) + else: + answercount = "-1" + + #if post.attrib.has_key("CommentCount"): + if "CommentCount" in post.attrib: + commentcount = int(post.attrib["CommentCount"]) + else: + commentcount = "-1" + + print ("%i\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" % (id, type, creation, score, viewcount, title.encode(encoding), body.encode(encoding), owner, lastactivity, tags.encode(encoding), answercount, commentcount)) + post.clear() + +print ("\.") + diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-tags.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-tags.py new file mode 100644 index 00000000000..09dd44423f1 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/so2pg-tags.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +import xml.etree.cElementTree as ElementTree +import os +import sys +import re + +db = "so" +encoding = "UTF-8" + +def escape(str): + str = re.sub("\s", " ", str) + # \ are special for Python strings *and* for regexps. Hence the + # multiple escaping. Here,we just replace every \ by \\ for + # PostgreSQL + str = re.sub("\\\\", "\\\\\\\\", str) + return str + +def tag_parse(str): + index = 0 + while index < len(str): + if str[index] == '<': + try: + end_tag = str[index:].index('>') + yield str[(index+1):(index+end_tag)] + index += end_tag + 1 + except ValueError: + raise Exception("Tag parsing error in \"%s\"" % str); + else: + raise Exception("Tag parsing error in \"%s\"" % str); + +if len(sys.argv) != 2: + raise Exception("Usage: %s so-files-directory" % sys.argv[0]) + +#os.chdir(sys.argv[1]) + +filename = "Tags.xml" +tags = ElementTree.iterparse(filename) +print ("COPY tags (id, name, count, excerptpost, wikipost) FROM stdin;") +for event, tag in tags: + if event == "end" and tag.tag == "row": + id = int(tag.attrib["Id"]) + + name = tag.attrib["TagName"] + + count = int(tag.attrib["Count"]) + + #if tag.attrib.has_key("ExcerptPostId"): + if "ExcerptPostId" in tag.attrib: + excerptpost = int(tag.attrib["ExcerptPostId"]) + else: + excerptpost = int("-1") + + #if tag.attrib.has_key("WikiPostId"): + if "WikiPostId" in tag.attrib: + wikipost = int(tag.attrib["WikiPostId"]) + else: + #wikipost = "\n" + wikipost = int("-1") + + print ("%i\t%s\t%d\t%d\t%d" % (id, name, count, excerptpost, wikipost)) + tag.clear() +print ("\.") \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-users.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-users.py new file mode 100644 index 00000000000..314cdbda010 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/so2pg-users.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python + +import xml.etree.cElementTree as ElementTree +import os +import sys +import re + +db = "so" +encoding = "UTF-8" + +def escape(str): + str = re.sub("\s", " ", str) + # \ are special for Python strings *and* for regexps. Hence the + # multiple escaping. Here,we just replace every \ by \\ for + # PostgreSQL + str = re.sub("\\\\", "\\\\\\\\", str) + return str + +def tag_parse(str): + index = 0 + while index < len(str): + if str[index] == '<': + try: + end_tag = str[index:].index('>') + yield str[(index+1):(index+end_tag)] + index += end_tag + 1 + except ValueError: + raise Exception("Tag parsing error in \"%s\"" % str); + else: + raise Exception("Tag parsing error in \"%s\"" % str); + +if len(sys.argv) != 2: + raise Exception("Usage: %s so-files-directory" % sys.argv[0]) + +#os.chdir(sys.argv[1]) + +filename = "Users.xml" +users = ElementTree.iterparse(filename) +print ("COPY users (id, reputation, creation, name, lastaccess, website, location, aboutme, views, upvotes, downvotes, age) FROM stdin;") +for event, user in users: + if event == "end" and user.tag == "row": + id = int(user.attrib["Id"]) + + reputation = int(user.attrib["Reputation"]) + + creation = user.attrib["CreationDate"] + + #if user.attrib.has_key("DisplayName"): # Yes, some users have no name, for instance 155 :-( + if "DisplayName" in user.attrib: + name = escape(user.attrib["DisplayName"]) + else: + name = "\n" + + #if user.attrib.has_key("LastAccessDate"): + if "LastAccessDate" in user.attrib: + lastaccess = escape(user.attrib["LastAccessDate"]) + else: + lastaccess = "\n" + + #if user.attrib.has_key("WebsiteUrl"): + if "WebsiteUrl" in user.attrib: + website = escape(user.attrib["WebsiteUrl"]) + else: + website = "\n" + + #if user.attrib.has_key("Location"): + if "Location" in user.attrib: + location = escape(user.attrib["Location"]) + else: + location = "\n" + + #if user.attrib.has_key("AboutMe"): + if "AboutMe" in user.attrib: + aboutme = escape(user.attrib["AboutMe"]) + else: + aboutme = "\n" + + #if user.attrib.has_key("Views"): + if "Views" in user.attrib: + views = int(user.attrib["Views"]) + else: + views = 0 + + #if user.attrib.has_key("UpVotes"): + if "UpVotes" in user.attrib: + upvotes = int(user.attrib["UpVotes"]) + else: + upvotes = 0 + + #if user.attrib.has_key("DownVotes"): + if "DownVotes" in user.attrib: + downvotes = int(user.attrib["DownVotes"]) + else: + downvotes = 0 + + #if user.attrib.has_key("Age"): + if "Age" in user.attrib: + age = int(user.attrib["Age"]) + else: + age = -1 + + print ("%i\t%i\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" % (id, reputation, creation, name.encode(encoding), lastaccess, website.encode(encoding), location.encode(encoding), aboutme.encode(encoding), views, upvotes, downvotes, age)) + user.clear() +print ("\.") \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-votes.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-votes.py new file mode 100644 index 00000000000..5b615eca280 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/python_src/so2pg-votes.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +import xml.etree.cElementTree as ElementTree +import os +import sys +import re + +db = "so" +encoding = "UTF-8" + +def escape(str): + str = re.sub("\s", " ", str) + # \ are special for Python strings *and* for regexps. Hence the + # multiple escaping. Here,we just replace every \ by \\ for + # PostgreSQL + str = re.sub("\\\\", "\\\\\\\\", str) + return str + +def tag_parse(str): + index = 0 + while index < len(str): + if str[index] == '<': + try: + end_tag = str[index:].index('>') + yield str[(index+1):(index+end_tag)] + index += end_tag + 1 + except ValueError: + raise Exception("Tag parsing error in \"%s\"" % str); + else: + raise Exception("Tag parsing error in \"%s\"" % str); + +if len(sys.argv) != 2: + raise Exception("Usage: %s so-files-directory" % sys.argv[0]) + +#os.chdir(sys.argv[1]) + +filename = "Votes.xml" +votes = ElementTree.iterparse(filename) +print ("COPY votes (id, type, postid, creation) FROM stdin;") +for event, vote in votes: + if event == "end" and vote.tag == "row": + id = int(vote.attrib["Id"]) + + type = int(vote.attrib["VoteTypeId"]) + + postid = vote.attrib["PostId"] + + creation = vote.attrib["CreationDate"] + + print ("%i\t%s\t%s\t%s" % (id, type, postid, creation)) + vote.clear() +print ("\.") \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/scripts/backup-restore.sh b/.github/actions/stackoverflow_in_pg/scripts/backup-restore.sh new file mode 100644 index 00000000000..899f8fb968d --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/scripts/backup-restore.sh @@ -0,0 +1,20 @@ +# Save password +export PGPASSWORD='<password>' + +# Backup Database +pg_dump --help + +pg_dump -Fc -h localhost -U postgres DBA -f DBA.dump +pg_dump -Fc -h localhost -U postgres StackOverflow -f StackOverflow-Backup-20231214.dump -Z 9 + + +# Restore Database +pg_restore --help + +# connect, and create a database +psql -h localhost -d postgres -U postgres +create database DBA2 with owner = 'postgres'; + +# Restore database content from dump +pg_restore -d DBA2 -h localhost -U postgres DBA.dump + diff --git a/.github/actions/stackoverflow_in_pg/scripts/convert-xml-2-sql.sh b/.github/actions/stackoverflow_in_pg/scripts/convert-xml-2-sql.sh new file mode 100644 index 00000000000..957666dc9cb --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/scripts/convert-xml-2-sql.sh @@ -0,0 +1,25 @@ +# (base) saanvi@ryzen9:/hyperactive/StackOverflow-Dumps$ + +# done +python so2pg-badges.py /hyperactive/StackOverflow-Dumps/Badges.xml > badges.sql + +# done +python so2pg-comments.py /hyperactive/StackOverflow-Dumps/Comments.xml > comments.sql + +# done +python so2pg-posthistory.py /hyperactive/StackOverflow-Dumps/PostsHistory.xml > posthistory.sql + +# done +python so2pg-postlinks.py /hyperactive/StackOverflow-Dumps/PostLinks.xml > postlinks.sql + +# done +python so2pg-posts.py /hyperactive/StackOverflow-Dumps/Posts.xml > posts.sql + +# done +python so2pg-tags.py /hyperactive/StackOverflow-Dumps/Tags.xml > tags.sql + +# done +python so2pg-users.py /hyperactive/StackOverflow-Dumps/Users.xml > users.sql + +# done +python so2pg-votes.py /hyperactive/StackOverflow-Dumps/Votes.xml > votes.sql diff --git a/.github/actions/stackoverflow_in_pg/scripts/push-2-pg.sh b/.github/actions/stackoverflow_in_pg/scripts/push-2-pg.sh new file mode 100644 index 00000000000..29e383c38f1 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/scripts/push-2-pg.sh @@ -0,0 +1,22 @@ +# Below worked for me +export PGPASSWORD='<password>' +psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./badges.sql # done + +psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./comments.sql # done +psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./posthistory.sql # done + +psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./postlinks.sql # done + +psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./posts.sql # done +psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./users.sql # done +psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./tags.sql # done +psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./votes.sql # done + + +psql -U postgres -d StackOverflow -f comments.sql +psql -U postgres -d StackOverflow -f posthistory.sql +psql -U postgres -d StackOverflow -f postlinks.sql +psql -U postgres -d StackOverflow -f posts.sql +psql -U postgres -d StackOverflow -f tags.sql +psql -U postgres -d StackOverflow -f users.sql +psql -U postgres -d StackOverflow -f votes.sql \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/so-create.sql b/.github/actions/stackoverflow_in_pg/so-create.sql new file mode 100755 index 00000000000..43991379e90 --- /dev/null +++ b/.github/actions/stackoverflow_in_pg/so-create.sql @@ -0,0 +1,94 @@ +ALTER TABLE Comments DROP CONSTRAINT comments_id_fkey; +DROP TABLE Votes; +DROP TABLE Tags; +DROP TABLE Posts; +DROP TABLE Comments; +DROP TABLE Users; +DROP TABLE PostLinks; +DROP TABLE PostHistory; +DROP TABLE Badges; + + + +CREATE TABLE Users ( + id INTEGER UNIQUE NOT NULL, --Id + reputation INTEGER NOT NULL, --Reputation + creation TIMESTAMP NOT NULL, --CreationDate + name TEXT, --DisplayName Yes, can be null some times + lastaccess TIMESTAMP, --LastAccessDate + website TEXT, --WebsiteUrl + location TEXT, --Location + aboutme TEXT, --AboutMe + views INTEGER, --Views + upvotes INTEGER, --upvotes + downvotes INTEGER, --downvotes + age INTEGER --age +); + +CREATE TABLE Comments ( + id INTEGER UNIQUE NOT NULL, --Id + postid INTEGER NOT NULL, --PostId + score INTEGER, --Score + text TEXT, --Text + creation TIMESTAMP NOT NULL, --CreationDate + userid INTEGER --UserId +); + +CREATE TABLE Posts ( + id INTEGER UNIQUE NOT NULL, --Id + type INTEGER NOT NULL, --PostTypeId + creation TIMESTAMP NOT NULL, --CreationDate + score INTEGER, --Score + viewcount INTEGER, --ViewCount + title TEXT, --Title + body TEXT, --Body + userid INTEGER, --OwnerUserId + lastactivity TIMESTAMP, --LastActivityDate + tags TEXT, --Tags + answercount INTEGER, --AnswerCount + commentcount INTEGER --CommentCount + ); + +CREATE TABLE Tags ( + id INTEGER UNIQUE NOT NULL, --Id + name TEXT UNIQUE NOT NULL, --TagName + count INTEGER, --Count + excerptpost INTEGER, --ExcerptPostId + wikipost INTEGER --WikiPostId +); + +CREATE TABLE Votes ( + id INTEGER UNIQUE NOT NULL, --Id + type INTEGER NOT NULL, --VoteTypeId + postid INTEGER NOT NULL, --PostId + creation DATE NOT NULL --CreationDate +); + +CREATE TABLE PostLinks ( + id INTEGER UNIQUE NOT NULL, --Id + creation TIMESTAMP NOT NULL, --CreationDate + postid INTEGER, --PostId + relatedpostid INTEGER, --RelatedPostId + linktypeid INTEGER --LinkTypeId +); + +CREATE TABLE PostHistory ( + id INTEGER UNIQUE NOT NULL, --Id + type INTEGER, --PostHistoryTypeId + postid INTEGER, --PostId + revisionguid TEXT, --RevisionGUID + creation TIMESTAMP NOT NULL, --CreationDate + userid INTEGER, --UserId + userdisplaymame TEXT, --UserDisplayName + text TEXT --Text +); + +CREATE TABLE Badges ( + id INTEGER UNIQUE NOT NULL, --Id + userid INTEGER, --UserId + name TEXT, --Name + date TIMESTAMP, --Date + badgeclass INTEGER, --Class + tagbased TEXT --TagBased +); + diff --git a/.github/actions/terraform-templates b/.github/actions/terraform-templates deleted file mode 160000 index a11c320f28c..00000000000 --- a/.github/actions/terraform-templates +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a11c320f28c7fdcd58750cc9b3e2ac4387acaf15 diff --git a/.github/actions/terraform-templates/.devcontainer/Dockerfile b/.github/actions/terraform-templates/.devcontainer/Dockerfile new file mode 100644 index 00000000000..b1d4e8b77cb --- /dev/null +++ b/.github/actions/terraform-templates/.devcontainer/Dockerfile @@ -0,0 +1,4 @@ +FROM mcr.microsoft.com/azure-dev-cli-apps:latest + +#RUN sudo echo "nameserver 8.8.8.8" >> /etc/resolv.conf +#CMD [ "sleep", "infinity" ] \ No newline at end of file diff --git a/.github/actions/terraform-templates/.devcontainer/devcontainer.json b/.github/actions/terraform-templates/.devcontainer/devcontainer.json new file mode 100644 index 00000000000..7ef941d35cf --- /dev/null +++ b/.github/actions/terraform-templates/.devcontainer/devcontainer.json @@ -0,0 +1,24 @@ +{ + "name": "azure-dev-cli-apps", + //"build": { "dockerfile": "Dockerfile" }, + //"image": "mcr.microsoft.com/azure-dev-cli-apps:latest", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspace", + "remoteEnv": { + "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode-remote.remote-containers", + "github.codespaces", + "hashicorp.terraform", + "stateful.runme" + ] + } + }, + "otherPortsAttributes": { + "onAutoForward": "silent" + } +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/.devcontainer/docker-compose.yml b/.github/actions/terraform-templates/.devcontainer/docker-compose.yml new file mode 100644 index 00000000000..83095a62cd3 --- /dev/null +++ b/.github/actions/terraform-templates/.devcontainer/docker-compose.yml @@ -0,0 +1,19 @@ +version: "3.3" +services: + app: + build: + context: . + dockerfile: Dockerfile + volumes: + # Forwards the local Docker socket to the container. + - /var/run/docker.sock:/var/run/docker-host.sock + # Update this to wherever you want VS Code to mount the folder of your project + - ..:/workspace:cached + network_mode: bridge + dns: + - 1.1.1.1 + - 1.0.0.1 + + # Overrides default command so things don't shut down after the process ends. + #entrypoint: /usr/local/share/docker-init.sh + command: sleep infinity \ No newline at end of file diff --git a/.github/actions/terraform-templates/.devops/adf-build-and-release.yml b/.github/actions/terraform-templates/.devops/adf-build-and-release.yml new file mode 100644 index 00000000000..c67967f32bc --- /dev/null +++ b/.github/actions/terraform-templates/.devops/adf-build-and-release.yml @@ -0,0 +1,83 @@ +trigger: + - main # Collaboration branch + #- develop # Development branch + #- feature/* # Feature branches + +pool: + name: 'Default' + +variables: + #- group: azure-service-connections # Use variable groups for sensitive values + - name: ServiceConnection + value: 'demo-rg' + - name: keyVaultName + value: 'kv-42e-demo-primary' + - name: sourceDataFactoryName + value: 'df-42e-demo-primary' + - name: deployDataFactoryName + value: 'df-42e-demo-secondary' + - name: deploymentResourceGroupName + value: 'demo' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: Build + displayName: Build job + pool: + name: 'Default' + steps: + - task: UseNode@1 + inputs: + version: '18.x' + displayName: 'Install Node.js' + + - task: Npm@1 + inputs: + command: 'install' + workingDir: '$(Build.Repository.LocalPath)/.devops' + verbose: true + displayName: 'Install npm package' + + - task: Npm@1 + inputs: + command: 'custom' + workingDir: '$(Build.Repository.LocalPath)/.devops' + customCommand: 'run build export $(Build.Repository.LocalPath)/adf/$(SourceDataFactoryName) /subscriptions/ff5f335c-3727-493e-9dc0-0aad424b456d/resourceGroups/demo/providers/Microsoft.DataFactory/factories/$(SourceDataFactoryName) "ArmTemplate"' + displayName: 'Validate and Generate ARM template' + + - publish: '$(Build.Repository.LocalPath)/.devops/ArmTemplate/' + displayName: 'Publish ARM templates' + artifact: drop + +- stage: Deploy + displayName: Deploy to Environment + jobs: + - deployment: DeployDev + displayName: Deploy to Development + condition: succeeded() + #and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')) # Conditional execution for development branch + environment: Development + pool: + name: 'Default' + strategy: + runOnce: + deploy: + steps: + - template: adf-release-tasks.yml + parameters: + environment: 'Development' + azureSubscription: '$(ServiceConnection)' + keyVaultName: $(KeyVaultName) + sourceDataFactoryName: $(sourceDataFactoryName) + deployDataFactoryName: $(deployDataFactoryName) + deploymentResourceGroupName: $(deploymentResourceGroupName) + +# - deployment: DeployProd +# displayName: Deploy to Production +# condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) # Conditional execution for main branch +# environment: Production +# pool: +# name: 'Default' + diff --git a/.github/actions/terraform-templates/.devops/adf-release-tasks.yml b/.github/actions/terraform-templates/.devops/adf-release-tasks.yml new file mode 100644 index 00000000000..c07ba987704 --- /dev/null +++ b/.github/actions/terraform-templates/.devops/adf-release-tasks.yml @@ -0,0 +1,57 @@ +parameters: + environment: '' + azureSubscription: '' + keyVaultName: '' + sourceDataFactoryName: '' + deployDataFactoryName: '' + deploymentResourceGroupName: '' + + +steps: +- task: AzureKeyVault@1 + inputs: + azureSubscription: '${{ parameters.azureSubscription }}' + KeyVaultName: '${{ parameters.KeyVaultName }}' + SecretsFilter: '*' + RunAsPreJob: true + +- download: current + artifact: drop + +- task: AzurePowerShell@5 + displayName: Stop Triggers + inputs: + azureSubscription: '${{ parameters.azureSubscription }}' + ScriptType: 'InlineScript' + Inline: + $triggersADF = Get-AzDataFactoryV2Trigger -DataFactoryName + "${{ parameters.DeployDataFactoryName }}" -ResourceGroupName "${{ parameters.DeploymentResourceGroupName }}"; + $triggersADF | ForEach-Object { Stop-AzDataFactoryV2Trigger + –ResourceGroupName "${{ parameters.DeploymentResourceGroupName }}" -DataFactoryName + "${{ parameters.DeployDataFactoryName }}" -Name $_.name -Force } + azurePowerShellVersion: 'LatestVersion' +- task: AzurePowerShell@5 + displayName: Deploy ADF + inputs: + azureSubscription: '${{ parameters.azureSubscription }}' + ScriptType: 'InlineScript' + Inline: + 'New-AzResourceGroupDeployment + -ResourceGroupName "${{ parameters.DeploymentResourceGroupName }}" + -TemplateParameterFile "$(Pipeline.Workspace)/drop/ARMTemplateParametersForFactory.json" + -TemplateFile "$(Pipeline.Workspace)/drop/ARMTemplateForFactory.json" + -factoryName "${{ parameters.DeployDataFactoryName }}" + #<parameter-overridden> : <value-to-be-overridden> there are parameters in arm template and overriden by key vault secrets + #<parameter-overridden> : <value-to-be-overridden> + -Mode "Incremental"' + azurePowerShellVersion: 'LatestVersion' +- task: AzurePowerShell@5 + displayName: Restart Triggers + inputs: + azureSubscription: '${{ parameters.azureSubscription }}' + ScriptType: 'InlineScript' + Inline: + $triggersADF = Get-AzDataFactoryV2Trigger -DataFactoryName "${{ parameters.DeployDataFactoryName }}" -ResourceGroupName "${{ parameters.DeploymentResourceGroupName }}"; + $triggersADF | ForEach-Object { Start-AzDataFactoryV2Trigger -ResourceGroupName "${{ parameters.DeploymentResourceGroupName }}" -DataFactoryName "${{ parameters.DeployDataFactoryName }}" -Name $_.name -Force } + azurePowerShellVersion: 'LatestVersion' + \ No newline at end of file diff --git a/.github/actions/terraform-templates/.devops/db-restore.yml b/.github/actions/terraform-templates/.devops/db-restore.yml new file mode 100644 index 00000000000..b0e5066a1ca --- /dev/null +++ b/.github/actions/terraform-templates/.devops/db-restore.yml @@ -0,0 +1,73 @@ +trigger: none + +pool: + name: Default + +variables: + - group: demo-db-credentials + +stages: + +- stage: Backup + displayName: Backup Database + condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/pull/'))) + jobs: + - deployment: BackupDatabase + displayName: Backup Database + environment: demo + pool: + name: Default + strategy: + runOnce: + deploy: + steps: + - task: SqlAzureDacpacDeployment@1 + inputs: + azureSubscription: 'demo-rg' + AuthenticationType: 'server' + ServerName: 'sqlserver-$(Uid)-demo-primary.database.windows.net' + DatabaseName: 'trialdb' + SqlUsername: '$(SqlUsername)' + SqlPassword: '$(SqlPassword)' + deployType: 'DacpacTask' + DeploymentAction: 'Export' + IpDetectionMethod: 'AutoDetect' + BacpacFile: '$(Build.ArtifactStagingDirectory)\trialdb.bacpac' + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: 'GeneratedOutputFiles\trialdb.bacpac' + ArtifactName: 'drop' + +- stage: Restore + dependsOn: Backup + displayName: Restore Database + condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/pull/'))) + jobs: + - deployment: RestoreDatabase + displayName: Restore Database + environment: demo + pool: + name: Default + strategy: + runOnce: + deploy: + steps: + - task: DownloadBuildArtifacts@1 + inputs: + buildType: 'current' + artifactName: 'drop' + downloadPath: '$(System.ArtifactsDirectory)' + + - task: SqlAzureDacpacDeployment@1 + inputs: + azureSubscription: 'demo-rg' + AuthenticationType: 'server' + ServerName: 'sqlserver-$(Uid)-demo-secondary.database.windows.net' + DatabaseName: 'trialdb' + SqlUsername: '$(SqlUsername)' + SqlPassword: '$(SqlPassword)' + deployType: 'DacpacTask' + DeploymentAction: 'Import' + IpDetectionMethod: 'AutoDetect' + BacpacFile: '$(Build.ArtifactStagingDirectory)\drop\trialdb.bacpac' \ No newline at end of file diff --git a/.github/actions/terraform-templates/.github/dependabot.yml b/.github/actions/terraform-templates/.github/dependabot.yml new file mode 100644 index 00000000000..f33a02cd16e --- /dev/null +++ b/.github/actions/terraform-templates/.github/dependabot.yml @@ -0,0 +1,12 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for more information: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +# https://containers.dev/guide/dependabot + +version: 2 +updates: + - package-ecosystem: "devcontainers" + directory: "/" + schedule: + interval: weekly diff --git a/.github/actions/terraform-templates/.gitignore b/.github/actions/terraform-templates/.gitignore new file mode 100644 index 00000000000..374ca3b4f58 --- /dev/null +++ b/.github/actions/terraform-templates/.gitignore @@ -0,0 +1,36 @@ +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log +crash.*.log + +# Exclude all .tfvars files, which are likely to contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +*secrets.auto.tfvars +*secrets.auto.tfvars.json + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc + +**/.terraform.lock.hcl diff --git a/.github/actions/terraform-templates/.scripts/data/psql_table_massive.sh b/.github/actions/terraform-templates/.scripts/data/psql_table_massive.sh new file mode 100755 index 00000000000..30ed034c070 --- /dev/null +++ b/.github/actions/terraform-templates/.scripts/data/psql_table_massive.sh @@ -0,0 +1,35 @@ +sudo apt update +sudo apt install -y p7zip-full + +cd /mnt/storage/ +echo "restart script if slow download speed (<5Mbps):" +curl -L https://archive.org/download/stackexchange/stackoverflow.com-PostHistory.7z --output PostHistory.7z +7z x PostHistory.7z + +python stackoverflow_in_pg/python_src/so2pg-posthistory.py PostHistory.xml > posthistory.sql + +echo -e "Create table and insert data:\n==================" +cat << EOF +CREATE TABLE PostHistory ( + id INTEGER UNIQUE NOT NULL, + type INTEGER, + postid INTEGER, + revisionguid TEXT, + creation TIMESTAMP NOT NULL, + userid INTEGER, + userdisplaymame TEXT, + text TEXT +); + +export PGPASSWORD="<password>" +host="<cluster name>.postgres.cosmos.azure.com" +user="citus" +db="citus" +schema="public" +psql \ +-h $host \ +-d $db \ +-U $user \ +-f /mnt/storage/posthistory.sql +EOF +echo -e "==================" diff --git a/.github/actions/terraform-templates/.scripts/data/psql_table_tiny.sh b/.github/actions/terraform-templates/.scripts/data/psql_table_tiny.sh new file mode 100755 index 00000000000..efafbc60482 --- /dev/null +++ b/.github/actions/terraform-templates/.scripts/data/psql_table_tiny.sh @@ -0,0 +1,32 @@ +sudo apt update +sudo apt install -y p7zip-full + +cd /mnt/storage/ +echo "restart script if slow download speed (<5Mbps):" +curl -L https://archive.org/download/stackexchange/stackoverflow.com-Tags.7z --output Tags.7z +7z x Tags.7z + +python stackoverflow_in_pg/python_src/so2pg-tags.py Tags.xml > tags.sql + +echo -e "Create table and insert data:\n==================" +cat << EOF +CREATE TABLE Tags ( + id INTEGER UNIQUE NOT NULL, + name TEXT UNIQUE NOT NULL, + count INTEGER, + excerptpost INTEGER, + wikipost INTEGER +); + +export PGPASSWORD="<password>" +host="<cluster name>.postgres.cosmos.azure.com" +user="citus" +db="citus" +schema="public" +psql \ +-h $host \ +-d $db \ +-U $user \ +-f /mnt/storage/tags.sql +EOF +echo -e "==================" diff --git a/.github/actions/terraform-templates/.scripts/destroy_template.sh b/.github/actions/terraform-templates/.scripts/destroy_template.sh new file mode 100755 index 00000000000..f476492bf8b --- /dev/null +++ b/.github/actions/terraform-templates/.scripts/destroy_template.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +set -e + +# Function to check if a directory exists +check_directory() { + if [ ! -d "$1" ]; then + echo "Error: The specified template directory '$1' does not exist." + exit 1 + fi +} + +# Function to check if a file exists +check_file() { + if [ ! -f "$1" ]; then + echo "Error: The file '$1' does not exist." + exit 1 + fi +} + +# Function to check if a value is empty +check_empty() { + if [ -z "$1" ]; then + echo "Error: No value provided for $2. Please enter a valid value." + exit 1 + fi +} + +# Prompt for the path to the environment directory +read -p "Enter the template environment (01): " env_path + +# Check if env_path is empty +check_empty "$env_path" "env_path" + +# Set the environment directory path +env_dir="azure/env/$env_path" + +# Check if the environment directory exists +check_directory "$env_dir" + +# Set the JSON file path +json_file="$env_dir/_override.tf.json" + +# Check if the JSON file exists +check_file "$json_file" + +# Read values from the JSON file +storage_account_name=$(grep -oP '(?<="storage_account_name": ")[^"]*' "$json_file") +resource_group_name=$(grep -oP '(?<="resource_group_name": ")[^"]*' "$json_file") +container_name=$(grep -oP '(?<="container_name": ")[^"]*' "$json_file") +state_file_key=$(grep -oP '(?<="key": ")[^"]*' "$json_file") + +# Check if the storage account exists +if ! az storage account show --name "$storage_account_name" --resource-group "$resource_group_name" >/dev/null 2>&1; then + echo "Storage account '$storage_account_name' does not exist. Skipping all actions." + exit 0 +fi + +# Check if the container exists +if ! az storage container exists --account-name "$storage_account_name" --name "$container_name" --query "exists" -o tsv --only-show-errors | grep -q "^true$"; then + echo "Container '$container_name' does not exist in storage account '$storage_account_name'. Skipping Terraform destroy." +else + # Check if the state file exists in the container + if az storage blob exists --account-name "$storage_account_name" --container-name "$container_name" --name "$state_file_key" --query "exists" -o tsv --only-show-errors | grep -q "^true$"; then + # Run terraform destroy if the state file exists + terraform -chdir="$env_dir" destroy -auto-approve + else + echo "Skipping terraform destroy as the state file does not exist in the container." + fi +fi + +# Construct the az storage account delete command +delete_command="az storage account delete --name $storage_account_name --resource-group $resource_group_name --yes" + +# Print the command +echo "Running command: $delete_command" + +# Run the command +eval "$delete_command" diff --git a/.github/actions/terraform-templates/.scripts/provision_template.sh b/.github/actions/terraform-templates/.scripts/provision_template.sh new file mode 100755 index 00000000000..066254db1aa --- /dev/null +++ b/.github/actions/terraform-templates/.scripts/provision_template.sh @@ -0,0 +1,203 @@ +#!/bin/bash + +set -e + +# Configuration +DEFAULT_TEMPLATE_ENVIRONMENT="01" +DEFAULT_RESOURCE_GROUP_NAME="myRG" +DEFAULT_ENVIRONMENT_NICKNAME="demo" +DEFAULT_LOCATION="eastus" +DEFAULT_OWNER_EMAIL="owner@example.com" +CONTAINER_NAME="terraformstate" +STATE_FILE_KEY="terraform.tfstate" +USE_AZUREAD_AUTH=false + +parse_arguments() { + while [[ $# -gt 0 ]]; do + case "$1" in + --template-environment|-t) + template_environment="$2" + shift 2 + ;; + --resource-group|-r) + resource_group_name="$2" + shift 2 + ;; + --environment-nickname|-e) + environment_nickname="$2" + shift 2 + ;; + --location|-l) + location="$2" + shift 2 + ;; + --owner-email|-o) + owner_email="$2" + shift 2 + ;; + *) + echo "Unknown argument: $1" + exit 1 + ;; + esac + done +} + +# Parse command-line arguments +parse_arguments "$@" + +# Function to prompt for user input with a default value +prompt_with_default() { + local prompt=$1 + local default_value=$2 + read -p "$prompt [$default_value]: " value + echo "${value:-$default_value}" +} + +# Function to check if a directory exists +check_directory_exists() { + local directory=$1 + if [ ! -d "$directory" ]; then + echo "Error: The specified template directory '$directory' does not exist." + exit 1 + fi +} + +# Function to check if a resource group exists +check_resource_group_exists() { + local resource_group=$1 + if ! az group show --name "$resource_group" >/dev/null 2>&1; then + echo "Error: The specified resource group '$resource_group' does not exist." + echo "Please create the resource group or provide an existing one." + exit 1 + fi +} + +# Function to generate the _override.tf.json file +generate_override_file() { + local json_file="azure/env/$template_environment/_override.tf.json" + cat > "$json_file" <<EOL +{ + "terraform": { + "backend": { + "azurerm": { + "resource_group_name": "$resource_group_name", + "storage_account_name": "$storage_account_name", + "container_name": "$CONTAINER_NAME", + "key": "$STATE_FILE_KEY", + "use_azuread_auth": $USE_AZUREAD_AUTH + } + } + }, + "variable": { + "environment": { + "default": "$environment_nickname" + }, + "uid": { + "default": "$random_suffix" + }, + "location": { + "default": "$location" + }, + "owner_email": { + "default": "$owner_email" + }, + "resource_group": { + "default": "$resource_group_name" + }, + "owner_object_id": { + "default": "$owner_object_id" + } + } +} +EOL + + if [ ! -f "$json_file" ]; then + echo "Error: The JSON file '$json_file' was not created!" + exit 1 + fi +} + +# Function to create the storage account +create_storage_account() { + if ! az storage account create --allow-blob-public-access false --name "$storage_account_name" --resource-group "$resource_group_name" --location "$location" --sku 'Standard_LRS' -o 'none'; then + echo -e "Error: Failed to create the storage account '$storage_account_name'.\n" + echo "Tips to resolve:" + echo "- Re-authenticate with 'az login'." + echo "- Ensure that the resource group '$resource_group_name' exists and you have the necessary permissions to create resources in it." + echo "- Verify that the storage account name '$storage_account_name' is unique and meets the naming requirements." + echo "- Check if the location '$location' is valid and supported for storage account creation." + echo "- Make sure you have the required Azure CLI version and are authenticated with the correct Azure subscription." + exit 1 + fi +} + +# Function to create the storage container +create_storage_container() { + if ! az storage container create --account-name "$storage_account_name" -n "$CONTAINER_NAME" --only-show-errors -o 'none'; then + echo -e "Error: Failed to create the storage container '$CONTAINER_NAME' in the storage account '$storage_account_name'.\n" + echo "Tips to resolve:" + echo "- Re-authenticate with 'az login'." + echo "- Ensure that the storage account '$storage_account_name' exists and you have the necessary permissions to create containers in it." + echo "- Verify that the container name '$CONTAINER_NAME' is valid and meets the naming requirements." + echo "- Check if you have the required Azure CLI version and are authenticated with the correct Azure subscription." + echo "- If error 'SubscriptionNotFound', manually create a single resource (other than a Resource Group) if non-exist." + exit 1 + fi +} + +# Main script logic +# Check if owner_object_id is populated, fail the script if it is empty +if [ -z "$owner_object_id" ]; then + echo "Error: owner_object_id is empty. Please make sure it is populated before running the script." + exit 1 +fi +echo -e "Owner object ID: $owner_object_id\n" + +# Prompt for user inputs + +# Check if the template environment directory exists +if [ -z "$template_environment" ]; then + template_environment=$(prompt_with_default "Enter the template environment" "$DEFAULT_TEMPLATE_ENVIRONMENT") +fi +check_directory_exists "azure/env/$template_environment" +export template_path="azure/env/$template_environment" +echo -e "Using template path: $template_path\n" + +# Check if the resource group exists +if [ -z "$resource_group_name" ]; then + resource_group_name=$(prompt_with_default "Enter the resource group name" "$DEFAULT_RESOURCE_GROUP_NAME") +fi +check_resource_group_exists "$resource_group_name" + +if [ -z "$environment_nickname" ]; then + environment_nickname=$(prompt_with_default "Enter the environment nickname" "$DEFAULT_ENVIRONMENT_NICKNAME") +fi +environment_nickname=$(echo "$environment_nickname" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]') + +if [ -z "$location" ]; then + location=$(prompt_with_default "Enter the location" "$DEFAULT_LOCATION") +fi + +if [ -z "$owner_email" ]; then + owner_email=$(prompt_with_default "Enter the owner email" "$DEFAULT_OWNER_EMAIL") +fi + +# Generate a random alphanumeric string of length 3 +random_suffix=$(echo $(date +%s%N) | sha256sum | tr '[:upper:]' '[:lower:]' | head -c 3) + +# Set the storage account name +storage_account_name="sa${environment_nickname}${random_suffix}" +echo -e "Using storage account name: $storage_account_name\n" + +# Generate the _override.tf.json file +generate_override_file + +# Create the storage account +create_storage_account + +# Wait for the storage account to be fully provisioned +sleep 5 + +# Create the storage container +create_storage_container diff --git a/.github/actions/terraform-templates/.scripts/utils/psql_install_16.sh b/.github/actions/terraform-templates/.scripts/utils/psql_install_16.sh new file mode 100755 index 00000000000..fcea28a1e14 --- /dev/null +++ b/.github/actions/terraform-templates/.scripts/utils/psql_install_16.sh @@ -0,0 +1,8 @@ +apt update +apt install -y sudo +sudo apt install -y curl ca-certificates +sudo install -d '/usr/share/postgresql-common/pgdg' +sudo curl -o '/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc' --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc +sudo sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' +sudo apt update +sudo apt install -y postgresql-16 \ No newline at end of file diff --git a/.github/actions/terraform-templates/.scripts/utils/ssh_key_auth.sh b/.github/actions/terraform-templates/.scripts/utils/ssh_key_auth.sh new file mode 100755 index 00000000000..ea1b1237b9e --- /dev/null +++ b/.github/actions/terraform-templates/.scripts/utils/ssh_key_auth.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +DEFAULT_KEY_PATH="$HOME" + +prompt() { + local prompt=$1 + read -p "$prompt: " value + echo "${value}" +} + +prompt_with_default() { + local prompt=$1 + local default_value=$2 + read -p "$prompt [$default_value]: " value + echo "${value:-$default_value}" +} + +remote_user=$(prompt "Enter remote username") +remote_hostname=$(prompt "Enter remote hostname") +remote_port=$(prompt "Enter remote port") + +key_path=$(prompt_with_default "Enter the key path" "$DEFAULT_KEY_PATH") + +echo "y" | ssh-keygen -t ed25519 -b 4096 -f $key_path/.ssh/id_ed25519 -N "" + +USER_AT_HOST="${remote_user}@${remote_hostname}" +PUBKEYPATH="$key_path/.ssh/id_ed25519.pub" + +pubKey=$(cat "$PUBKEYPATH") +ssh -p $remote_port "$USER_AT_HOST" "mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo '$pubKey' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys" diff --git a/.github/actions/terraform-templates/.scripts/utils/whats_running.sh b/.github/actions/terraform-templates/.scripts/utils/whats_running.sh new file mode 100755 index 00000000000..2ac05d66407 --- /dev/null +++ b/.github/actions/terraform-templates/.scripts/utils/whats_running.sh @@ -0,0 +1 @@ +ps aux diff --git a/.github/actions/terraform-templates/README.md b/.github/actions/terraform-templates/README.md new file mode 100644 index 00000000000..ffeefc512d5 --- /dev/null +++ b/.github/actions/terraform-templates/README.md @@ -0,0 +1,176 @@ +--- +runme: + id: 01HVWMC7W5WGR5QF3H8KNK4JP4 + version: v3 +--- + +# Terraform Templates + +## Introduction + +This repository contains Terraform templates for deploying various resources in Azure. It is designed to help beginners get started with provisioning infrastructure using Terraform. + +## Prerequisites + +- Azure CLI installed +- Azure account with sufficient permissions +- Terraform installed + +## Getting Started + +> I recommend running in Codespaces or Dev Container in VS Code: +> +> [](https://codespaces.new/JosiahSiegel/terraform-templates) + +### 1. Login to Azure + +```sh {"id":"01HVWMC7W5WGR5QF3H80KE8XAZ"} +az login +``` + +or + +```sh {"id":"01HVWPXVEB7ZFQETZD308G7HXP"} +az login --use-device-code +``` + +### 2. Export Owner Object ID + +Export an owner object ID to a variable by running the following command: + +```sh {"id":"01HVWMC7W5WGR5QF3H83QR73CK"} +read -p "Enter AD user display name [Smith, Joe]: " display_name +owner_object_id=$(az ad user list --filter "displayname eq '$display_name'" --query '[0].id' -o tsv) +export owner_object_id=$owner_object_id +echo "Object Id: $owner_object_id" +``` + +### 3. Create Terraform Backend + +Create the Terraform state backend file and storage account for a template by running the following script: + +```sh {"id":"01HVWMC7W5WGR5QF3H85WFTTP6"} +source ./.scripts/provision_template.sh +``` + +> Optional: set `template_path=azure/env/<template>` if backend already exists: + +```sh {"id":"01HW67SE89X56V69ASEFGZB6AX"} +read -p "Enter the template environment: " template +export template_path="azure/env/${template:-_}" +if [ ! -d "$template_path" ]; then +echo "Error: The specified template directory '$template_path' does not exist." +else +echo "Set template environment '$template_path'!" +fi +``` + +### 4. Initialize Terraform + +Initialize Terraform using the desired template path (`azure/env/<template>`) by running: + +```sh {"id":"01HVWMC7W5WGR5QF3H89NDQK07"} +terraform -chdir=$template_path init -reconfigure +``` + +Replace 01 with the appropriate template directory. + +### 5. Plan Terraform Deployment + +Generate an execution plan for the Terraform deployment by running: + +```sh {"id":"01HVWMC7W5WGR5QF3H8BHW8T14"} +terraform -chdir=$template_path plan +``` + +### 6. Apply Terraform Deployment + +```sh {"id":"01HVWMC7W5WGR5QF3H8DQ9WBBE"} +terraform -chdir=$template_path apply +``` + +If a login failure occurs with the error "Login failed for user ''", try running `az logout` and then `az login` to resolve the issue. + +### 7. Destroy the Terraform deployment and terraform storage account + +```sh {"id":"01HVWMC7W5WGR5QF3H8GNPS01Z"} +./.scripts/destroy_template.sh +``` + +## Available Templates + +### Template 01 + +|Azure Resource|Purpose| +|---|---| +|API Management|Securely share and manage REST endpoints| +|Logic App|Make database data available via REST endpoint| +|Data Factory|Ingest APIs and store data in a database and storage account| +|Key Vault|Manage secrets| +|Storage Account|Store results from ingested APIs| +|Azure SQL DB|Store data from ingested APIs and expose via Logic App and API Management| + +### Template 02 + +|Azure Resource|Purpose| +|---|---| +|Key Vault|Manage secrets| +|Azure SQL DB|Store data| + +### Template 03 + +|Azure Resource|Purpose| +|---|---| +|CosmosDB for PostgreSQL|| +|VNet|Private network| +|Private Endpoint|CosmosDB traffic over vnet| +|Key Vault|CosmosDB password| +|Container Instance|Run DB backup/restore to file share| +|Storage Account|File share storage for container instance| + +### Template 04 + +|Azure Resource|Purpose| +|---|---| +pending...|| + +### Template 05 + +|Azure Resource|Purpose| +|---|---| +|Container Instance|Linux container with file share mount| +|Storage Account|File share storage for container instance| + +### Template 06 + +|Azure Resource|Purpose| +|---|---| +|Container Instance|Windows container| + +## Troubleshooting + +1. If login failure during plan or apply, try running `az logout` then `az login` +2. If you encounter any issues, please check the Terraform state backend and `_override.tf.json` files and storage account created in step 3 +3. If error "SubscriptionNotFound", manually create a single resource (other than a Resource Group) if non-exist + +## Contributing + +Feel free to submit pull requests or open issues if you have any suggestions or improvements for these Terraform templates. + +### Bonus + + * Run scripts and enter terminal for container instance + +```sh {"id":"01HWZY6RPNNYHM0MN1F6MF9QZB"} +az container exec --name cinst-dev --resource-group demo --container-name cinst-dev --exec-command "/bin/bash -c /app/repo1/terraform-templates/.scripts/utils/psql_install_16.sh" +``` + +```sh {"id":"01HWY6JGNS1CTNGATQE5KMKNCR"} +az container exec --name cinst-dev --resource-group demo --container-name cinst-dev --exec-command "/bin/bash" +``` + +* Add existing public SSH key to container instance + +```sh +ssh-copy-id -p 2222 -i ~/.ssh/id_ed25519.pub app@cinst-dev.eastus.azurecontainer.io +``` diff --git a/.github/actions/terraform-templates/azure/env/01/config.tf b/.github/actions/terraform-templates/azure/env/01/config.tf new file mode 100644 index 00000000000..adf3cd66be2 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/01/config.tf @@ -0,0 +1,35 @@ +terraform { + required_version = ">= 1.7.5" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.99.0" + } + azuread = { + source = "hashicorp/azuread" + version = "2.48.0" + } + mssql = { + source = "betr-io/mssql" + version = "~> 0.3" + } + azapi = { + source = "Azure/azapi" + version = "1.12.1" + } + } +} + +provider "azurerm" { + # This is only required when the User, Service Principal, or Identity running Terraform lacks the permissions to register Azure Resource Providers. + skip_provider_registration = false + features { + template_deployment { + delete_nested_items_during_deletion = true + } + key_vault { + recover_soft_deleted_key_vaults = true + purge_soft_delete_on_destroy = true + } + } +} diff --git a/.github/actions/terraform-templates/azure/env/01/data.tf b/.github/actions/terraform-templates/azure/env/01/data.tf new file mode 100644 index 00000000000..b62ec85569e --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/01/data.tf @@ -0,0 +1,10 @@ +data "azuread_users" "owner" { + object_ids = [var.owner_object_id] +} + +data "azurerm_resource_group" "default" { + name = var.resource_group +} + +data "azurerm_subscription" "default" { +} diff --git a/.github/actions/terraform-templates/azure/env/01/main.tf b/.github/actions/terraform-templates/azure/env/01/main.tf new file mode 100644 index 00000000000..f6fa83888b2 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/01/main.tf @@ -0,0 +1,62 @@ +module "init" { + source = "../../modules/init/v1" + common = local.common + dev_roles = local.dev_roles +} + +module "logic_app" { + source = "../../modules/logic_app" + common = local.common + storage_account = module.storage_account.meta + workflow_file = "workflow.json" + sql_server_fqdn = module.azure_db.fqdn + db_name = module.azure_db.db_name + + depends_on = [module.init] +} + +module "azure_db" { + source = "../../modules/azure_mssql/v1" + common = local.common + logic_app_ids = module.logic_app.ids + logic_app_name = module.logic_app.name + + depends_on = [module.init] +} + +module "storage_account" { + source = "../../modules/storage_account/v1" + common = local.common + logic_app = module.logic_app.ids + + depends_on = [module.init] +} + +module "api_management" { + source = "../../modules/api_management" + common = local.common + logic_app_endpoint = module.logic_app.endpoint + + depends_on = [module.init] +} + +module "data_factory" { + source = "../../modules/data_factory/v1" + common = local.common + storage_account = module.storage_account.meta + key_vault = module.key_vault["primary"].meta + secrets = module.key_vault["primary"].secrets + + depends_on = [module.init] +} + +module "key_vault" { + for_each = local.key_vaults + + source = "../../modules/key_vault" + common = local.common + secrets = each.value.secrets + key = each.key + + depends_on = [module.init] +} diff --git a/.github/actions/terraform-templates/azure/env/01/~locals.tf b/.github/actions/terraform-templates/azure/env/01/~locals.tf new file mode 100644 index 00000000000..8ab2c1ce2e8 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/01/~locals.tf @@ -0,0 +1,39 @@ +variable "environment" {} +variable "owner_object_id" {} +variable "owner_email" {} +variable "uid" {} +variable "location" {} +variable "resource_group" {} + +locals { + env = var.environment + common = { + env = local.env + uid = var.uid + subscription = data.azurerm_subscription.default + tenant_id = data.azurerm_subscription.default.tenant_id + location = var.location + resource_group = data.azurerm_resource_group.default + owner_email = var.owner_email + owner = data.azuread_users.owner.users[0] + } + key_vaults = { + primary = { + secrets = { + "${local.env}-SECRET" = { + value = "PLACEHOLDER_${local.env}-SECRET" + } + "${local.env}-KEY" = { + value = "PLACEHOLDER_${local.env}-KEY" + } + "${local.env}-API-ENDPOINT" = { + value = "PLACEHOLDER_${local.env}-API-ENDPOINT" + } + "${local.env}-API-URL" = { + value = "PLACEHOLDER_${local.env}-API-URL" + } + } + } + } + dev_roles = ["Contributor", "Storage Table Data Contributor", "Storage Blob Data Contributor", "Key Vault Administrator"] +} diff --git a/.github/actions/terraform-templates/azure/env/02/config.tf b/.github/actions/terraform-templates/azure/env/02/config.tf new file mode 100644 index 00000000000..48ba68ca1a0 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/02/config.tf @@ -0,0 +1,35 @@ +terraform { + required_version = ">= 1.7.5" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.99.0" + } + azuread = { + source = "hashicorp/azuread" + version = "2.48.0" + } + random = { + source = "hashicorp/random" + version = "3.6.1" + } + azapi = { + source = "Azure/azapi" + version = "1.12.1" + } + } +} + +provider "azurerm" { + # This is only required when the User, Service Principal, or Identity running Terraform lacks the permissions to register Azure Resource Providers. + skip_provider_registration = false + features { + template_deployment { + delete_nested_items_during_deletion = true + } + key_vault { + recover_soft_deleted_key_vaults = true + purge_soft_delete_on_destroy = true + } + } +} diff --git a/.github/actions/terraform-templates/azure/env/02/data.tf b/.github/actions/terraform-templates/azure/env/02/data.tf new file mode 100644 index 00000000000..6f095f4479a --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/02/data.tf @@ -0,0 +1,15 @@ +data "azuread_users" "owner" { + object_ids = [var.owner_object_id] +} + +data "azurerm_resource_group" "default" { + name = var.resource_group +} + +data "azurerm_subscription" "default" { +} + +resource "random_password" "sql_password" { + length = 16 + special = true +} diff --git a/.github/actions/terraform-templates/azure/env/02/main.tf b/.github/actions/terraform-templates/azure/env/02/main.tf new file mode 100644 index 00000000000..edeaa50763c --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/02/main.tf @@ -0,0 +1,42 @@ +module "init" { + source = "../../modules/init/v1" + common = local.common + dev_roles = local.dev_roles +} + +module "key_vault" { + for_each = local.key_vaults + + source = "../../modules/key_vault" + common = local.common + secrets = each.value.secrets + key = each.key + + depends_on = [module.init] +} + +module "azure_mssql" { + for_each = local.mssql_instances + + source = "../../modules/azure_mssql/v2" + common = local.common + key = each.key + sqladmins = module.init.sqladmins + databases = each.value.databases + epool = each.value.epool + + admin_username = local.key_vaults.primary.secrets.SqlUsername.value + admin_password = local.key_vaults.primary.secrets.SqlPassword.value + + depends_on = [module.init, module.key_vault] +} + +module "data_factory" { + for_each = local.data_factories + + source = "../../modules/data_factory/v2" + common = local.common + roles = each.value.roles + key = each.key + +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/env/02/~locals.tf b/.github/actions/terraform-templates/azure/env/02/~locals.tf new file mode 100644 index 00000000000..9849525a882 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/02/~locals.tf @@ -0,0 +1,64 @@ +variable "environment" {} +variable "owner_object_id" {} +variable "owner_email" {} +variable "uid" {} +variable "location" {} +variable "resource_group" {} + +locals { + env = var.environment + common = { + env = local.env + uid = var.uid + subscription = data.azurerm_subscription.default + tenant_id = data.azurerm_subscription.default.tenant_id + location = var.location + resource_group = data.azurerm_resource_group.default + owner_email = var.owner_email + owner = data.azuread_users.owner.users[0] + } + mssql_instances = { + primary = { + databases = ["trialdb", "admindb"] + epool = true + }, + secondary = { + databases = ["trialdb"] + epool = false + }, + demo = { + databases = [] + epool = true + } + } + key_vaults = { + primary = { + secrets = { + SqlUsername = { + value = "${local.env}admin" + } + SqlPassword = { + value = random_password.sql_password.result + } + Uid = { + value = var.uid + } + } + } + } + data_factories = { + dev = { + roles = toset(["Storage Blob Data Contributor", "Key Vault Reader", "Key Vault Secrets User"]) + } + qa = { + roles = toset(["Key Vault Reader", "Key Vault Secrets User"]) + } + uat = { + roles = toset(["Key Vault Reader", "Key Vault Secrets User"]) + } + prod = { + roles = toset(["Key Vault Reader", "Key Vault Secrets User"]) + } + } + dev_roles = ["Contributor", "Storage Table Data Contributor", "Storage Blob Data Contributor", "Key Vault Administrator"] +} diff --git a/.github/actions/terraform-templates/azure/env/03/README.md b/.github/actions/terraform-templates/azure/env/03/README.md new file mode 100644 index 00000000000..a7f80d68c2e --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/03/README.md @@ -0,0 +1,142 @@ +# [PostgreSQL/Citus Migrate Production Data](https://docs.citusdata.com/en/stable/develop/migration_data.html#migrate-production-data) + +## [Big Database Migration](https://docs.citusdata.com/en/stable/develop/migration_data_big.html#big-database-migration) + +> ⚠️Recommends support request⚠️ + +## [Small Database Migration](https://docs.citusdata.com/en/stable/develop/migration_data_small.html#small-database-migration) + +> No support request + +### 1. Set variables + +```sh {"id":"01HXCRAJCEMCY0AF3403N8NGVJ"} +export PGPASSWORD="<password>" + +host="<cluster name>.postgres.cosmos.azure.com" +user="citus" +db="citus" +schema="public" +``` + +<details> + <summary>Optional: create test table</summary> + +```sql {"id":"01HXCRAJCEMCY0AF3405CYZXX0"} +create table public.test (name varchar) +insert into public.test (name) values ('test') +``` + +</details> + +### 2. Dump schema + +```sh {"id":"01HXCRAJCEMCY0AF3406ARMBC1"} +pg_dump \ +--format=plain \ +--no-owner \ +--schema-only \ +--file=/mnt/storage/$schema.sql \ +--schema=$schema \ +postgres://$user@$host:5432/$db +``` + +### 3. Dump data + +```sh {"id":"01HXCRAJCEMCY0AF3407N1BHNM"} +pg_dump \ +-Z 0 \ +--format=custom \ +--no-owner \ +--data-only \ +--file=/mnt/storage/${schema}data.dump \ +--schema=$schema \ +postgres://$user@$host:5432/$db +``` + +<details> + <summary>Dump slow?</summary> + +### Table bloats and vacuuming + +High `dead_pct` (dead tubles percentage) indicates the table may need to be vacuumed: + +```sql {"id":"01HXCRAJCEMCY0AF340AX8TQQ7"} +select +schemaname, +relname as table_name, +n_dead_tup, +n_live_tup, +round(n_dead_tup::float/n_live_tup::float*100) dead_pct, +autovacuum_count, +last_vacuum, +last_autovacuum, +last_autoanalyze, +last_analyze +from pg_stat_all_tables +where n_live_tup >0 +order by round(n_dead_tup::float/n_live_tup::float*100) desc; +``` + +Vacuum: + +```sql {"id":"01HXCRAJCEMCY0AF340CJ0QBCK"} +vacuum(analyze, verbose) <table_name> +``` + +### Reduce network bottlenecks + +For Azure, create a [Container Instance](https://azure.microsoft.com/en-us/products/container-instances) with a mounted storage account file share. Install a version of PostgreSQL that matches the target server. Open the IP of the container instance on the PostgreSQL server. Run `pg_dump` on the container instance with the file targeting the storage account mount. + +</details> + +<details> + <summary>Optional: drop test table</summary> + +```sql {"id":"01HXCRAJCEMCY0AF340DZNSZJ4"} +drop table public.test; +``` + +</details> + +### 4. Restore schema + +```sh {"id":"01HXCRAJCEMCY0AF340HGJYP00"} +psql \ +-h $host \ +-d $db \ +-U $user \ +-f /mnt/storage/$schema.sql +``` + +### 5. (Citus) Run configuration functions + +* Run your [create_distributed_table](https://docs.citusdata.com/en/stable/develop/api_udf.html#create-distributed-table) and [create_reference_table](https://docs.citusdata.com/en/stable/develop/api_udf.html#create-reference-table) statements. If you get an error about foreign keys, it’s generally due to the order of operations. Drop foreign keys before distributing tables and then re-add them. +* Put the application into maintenance mode, and disable any other writes to the old database. + +### 6. Restore data + +```sh {"id":"01HXCRAJCEMCY0AF340HXZ9BJY"} +pg_restore \ +--host=$host \ +--dbname=$db \ +--username=$user \ +/mnt/storage/${schema}data.dump +``` + +<details> + <summary>Example: SQL file restore</summary> + +```sh {"id":"01HXCRAJCEMCY0AF340NX40Q7C"} +psql "host=$host port=5432 dbname=$db user=$user sslmode=require password=$PGPASSWORD" < /mnt/storage/posthistory.sql +``` + +</details> +<details> + <summary>Optional: select test table</summary> + +```sql {"id":"01HXCRAJCEMCY0AF340QGP2J3K"} +select * from public.test limit 1; +``` + +</details> \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/env/03/config.tf b/.github/actions/terraform-templates/azure/env/03/config.tf new file mode 100644 index 00000000000..48ba68ca1a0 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/03/config.tf @@ -0,0 +1,35 @@ +terraform { + required_version = ">= 1.7.5" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.99.0" + } + azuread = { + source = "hashicorp/azuread" + version = "2.48.0" + } + random = { + source = "hashicorp/random" + version = "3.6.1" + } + azapi = { + source = "Azure/azapi" + version = "1.12.1" + } + } +} + +provider "azurerm" { + # This is only required when the User, Service Principal, or Identity running Terraform lacks the permissions to register Azure Resource Providers. + skip_provider_registration = false + features { + template_deployment { + delete_nested_items_during_deletion = true + } + key_vault { + recover_soft_deleted_key_vaults = true + purge_soft_delete_on_destroy = true + } + } +} diff --git a/.github/actions/terraform-templates/azure/env/03/data.tf b/.github/actions/terraform-templates/azure/env/03/data.tf new file mode 100644 index 00000000000..e533dda2eb0 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/03/data.tf @@ -0,0 +1,18 @@ +data "azuread_users" "owner" { + object_ids = [var.owner_object_id] +} + +data "azurerm_resource_group" "default" { + name = var.resource_group +} + +data "azurerm_subscription" "default" { +} + +resource "random_password" "sql_password" { + length = 16 + special = false + upper = true + lower = true + numeric = true +} diff --git a/.github/actions/terraform-templates/azure/env/03/main.tf b/.github/actions/terraform-templates/azure/env/03/main.tf new file mode 100644 index 00000000000..7efaf1bdffd --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/03/main.tf @@ -0,0 +1,104 @@ +module "init" { + source = "../../modules/init/v1" + common = local.common + dev_roles = local.dev_roles +} + +module "key_vault" { + for_each = local.key_vaults + + source = "../../modules/key_vault" + common = local.common + secrets = each.value.secrets + key = each.key + + depends_on = [module.init] +} + +module "cosmosdb_postgresql" { + for_each = local.cosmosdb_postgresql + + source = "../../modules/cosmosdb_postgresql/v1" + common = local.common + key = each.key + admin_password = local.key_vaults.primary.secrets.postgresPass.value + + depends_on = [module.init] +} + +module "vnet" { + for_each = local.vnets + + source = "../../modules/vnet" + common = local.common + address_space = each.value.address_space + subnets = each.value.subnets + key = each.key + + depends_on = [module.init] +} + +module "dns_zone" { + for_each = local.dns_zones + + source = "../../modules/dns_zone" + common = local.common + dns_name = each.value.name + dns_links = each.value.vnet_links + key = each.key + + vnets = { for vnet in module.vnet : vnet.meta.name => vnet.meta.id } + + depends_on = [module.init, module.vnet] +} + +module "private_endpoint" { + for_each = local.private_endpoints + + source = "../../modules/private_endpoint" + common = local.common + key = each.key + subresource_names = each.value.subresource_names + is_manual_connection = each.value.is_manual_connection + + subnet_id = module.vnet[each.value.vnet_key].subnets[each.value.subnet_key].id + dns_zone_ids = [lookup({ for zone in module.dns_zone : zone.meta.name => zone.meta.id }, each.value.dns_zone_key, "")] + resource_id = lookup({ for psql in module.cosmosdb_postgresql : psql.meta.name => psql.meta.id }, "cluster-${local.common.uid}-${local.common.env}-${each.value.resource_id_key}", "") + + depends_on = [module.init, module.vnet, module.dns_zone, module.cosmosdb_postgresql] +} + +module "storage_account" { + for_each = local.storage_accounts + + source = "../../modules/storage_account/v2" + common = local.common + key = each.key + account_tier = each.value.account_tier + account_kind = each.value.account_kind + + depends_on = [module.init] +} + +module "container_instance" { + for_each = local.container_instances + + source = "../../modules/container_instance" + common = local.common + key = each.key + storage_account = module.storage_account[each.value.storage_account_key].meta + image = each.value.image + cpu_cores = each.value.cpu_cores + mem_gb = each.value.mem_gb + commands = each.value.commands + shares = each.value.shares + repos = each.value.repos + exec = each.value.exec + os_type = each.value.os_type + + depends_on = [module.init, module.storage_account, module.vnet, module.key_vault] +} + +output "cinst_exec" { + value = module.container_instance[*] +} diff --git a/.github/actions/terraform-templates/azure/env/03/~locals.tf b/.github/actions/terraform-templates/azure/env/03/~locals.tf new file mode 100644 index 00000000000..f1f279a8787 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/03/~locals.tf @@ -0,0 +1,102 @@ +variable "environment" {} +variable "owner_object_id" {} +variable "owner_email" {} +variable "uid" {} +variable "location" {} +variable "resource_group" {} + +locals { + env = var.environment + common = { + env = local.env + uid = var.uid + subscription = data.azurerm_subscription.default + tenant_id = data.azurerm_subscription.default.tenant_id + location = var.location + resource_group = data.azurerm_resource_group.default + owner_email = var.owner_email + owner = data.azuread_users.owner.users[0] + } + key_vaults = { + primary = { + secrets = { + postgresUser = { + value = "${local.env}admin" + } + postgresPass = { + value = random_password.sql_password.result + } + Uid = { + value = var.uid + } + } + } + } + cosmosdb_postgresql = { + dev = {} + } + vnets = { + primary = { + address_space = { + value = ["10.5.0.0/16"] + } + subnets = { + service = { + address_prefixes = { + value = ["10.5.1.0/24"] + } + link_service_policies = true + endpoint_policies = false + } + endpoint = { + address_prefixes = { + value = ["10.5.2.0/24"] + } + link_service_policies = false + endpoint_policies = true + } + } + } + } + dns_zones = { + postgreshsc = { + name = "privatelink.postgreshsc.database.azure.com" + vnet_links = { + primary = { + registration_enabled = false + vnet_key = "primary" + } + } + } + } + private_endpoints = { + cosmosdb_postgresql = { + vnet_key = "primary" + subnet_key = "endpoint" + dns_zone_key = "privatelink.postgreshsc.database.azure.com" + resource_id_key = "dev" + subresource_names = ["coordinator"] + is_manual_connection = false + } + } + storage_accounts = { + dev = { + account_tier = "Premium" //Standard, Premium + account_kind = "FileStorage" //StorageV2, FileStorage, BlockBlobStorage, BlobStorage + } + } + container_instances = { + dev = { + storage_account_key = "dev" + os_type = "Linux" + image = "mcr.microsoft.com/azure-dev-cli-apps:latest" + cpu_cores = 4 + mem_gb = 16 + commands = ["/bin/bash", "-c", "sleep infinity"] + exec = "/bin/bash" + shares = { storage = { mount_path = "/mnt/storage", gb = 1000, tier = "Premium" } } //TransactionOptimized, Premium, Hot, Cool + repos = { terraform-templates = { url = "https://github.com/JosiahSiegel/terraform-templates.git", mount_path = "/app/repo1" }, so2pg = { url = "https://github.com/JosiahSiegel/stackoverflow_in_pg.git", mount_path = "/app/repo2" } } + } + } + dev_roles = ["Contributor", "Storage Table Data Contributor", "Storage Blob Data Contributor", "Key Vault Administrator", "Storage File Data Privileged Contributor", "Storage File Data SMB Share Elevated Contributor"] +} diff --git a/.github/actions/terraform-templates/azure/env/05/config.tf b/.github/actions/terraform-templates/azure/env/05/config.tf new file mode 100644 index 00000000000..90b2ad6b054 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/05/config.tf @@ -0,0 +1,31 @@ +terraform { + required_version = ">= 1.7.5" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.99.0" + } + azuread = { + source = "hashicorp/azuread" + version = "2.48.0" + } + random = { + source = "hashicorp/random" + version = "3.6.1" + } + } +} + +provider "azurerm" { + # This is only required when the User, Service Principal, or Identity running Terraform lacks the permissions to register Azure Resource Providers. + skip_provider_registration = false + features { + template_deployment { + delete_nested_items_during_deletion = true + } + key_vault { + recover_soft_deleted_key_vaults = true + purge_soft_delete_on_destroy = true + } + } +} diff --git a/.github/actions/terraform-templates/azure/env/05/data.tf b/.github/actions/terraform-templates/azure/env/05/data.tf new file mode 100644 index 00000000000..8252edfe813 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/05/data.tf @@ -0,0 +1,18 @@ +data "azuread_users" "owner" { + object_ids = [var.owner_object_id] +} + +data "azurerm_resource_group" "default" { + name = var.resource_group +} + +data "azurerm_subscription" "default" { +} + +resource "random_password" "user_password" { + length = 8 + special = false + upper = true + lower = true + numeric = true +} diff --git a/.github/actions/terraform-templates/azure/env/05/main.tf b/.github/actions/terraform-templates/azure/env/05/main.tf new file mode 100644 index 00000000000..6d3e180a1cb --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/05/main.tf @@ -0,0 +1,41 @@ +module "init" { + source = "../../modules/init/v2" + common = local.common + dev_roles = local.dev_roles +} + +module "storage_account" { + for_each = local.storage_accounts + + source = "../../modules/storage_account/v2" + common = local.common + key = each.key + account_tier = each.value.account_tier + account_kind = each.value.account_kind + + depends_on = [module.init] +} + +module "container_instance" { + for_each = local.container_instances + + source = "../../modules/container_instance" + common = local.common + key = each.key + storage_account = module.storage_account[each.value.storage_account_key].meta + image = each.value.image + cpu_cores = each.value.cpu_cores + mem_gb = each.value.mem_gb + commands = each.value.commands + shares = each.value.shares + repos = each.value.repos + exec = each.value.exec + os_type = each.value.os_type + user_password = each.value.user_password + + depends_on = [module.init, module.storage_account] +} + +output "cinst_exec" { + value = module.container_instance[*] +} diff --git a/.github/actions/terraform-templates/azure/env/05/~locals.tf b/.github/actions/terraform-templates/azure/env/05/~locals.tf new file mode 100644 index 00000000000..0414c5b4840 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/05/~locals.tf @@ -0,0 +1,41 @@ +variable "environment" {} +variable "owner_object_id" {} +variable "owner_email" {} +variable "uid" {} +variable "location" {} +variable "resource_group" {} + +locals { + env = var.environment + common = { + env = local.env + uid = var.uid + subscription = data.azurerm_subscription.default + tenant_id = data.azurerm_subscription.default.tenant_id + location = var.location + resource_group = data.azurerm_resource_group.default + owner_email = var.owner_email + owner = data.azuread_users.owner.users[0] + } + storage_accounts = { + standard = { + account_tier = "Standard" //Standard, Premium + account_kind = "StorageV2" //StorageV2, FileStorage, BlockBlobStorage, BlobStorage + } + } + container_instances = { + standard = { + storage_account_key = "standard" + os_type = "Linux" + image = "ghcr.io/josiahsiegel/dev-image:latest" + cpu_cores = 4 + mem_gb = 8 + commands = [] + exec = "/bin/bash" + shares = { storage = { mount_path = "/mnt/storage", gb = 500, tier = "TransactionOptimized" } } //TransactionOptimized, Premium, Hot, Cool + repos = { terraform-templates = { url = "https://github.com/JosiahSiegel/terraform-templates.git", mount_path = "/mnt/storage/repo1" } } + user_password = random_password.user_password.result + } + } + dev_roles = ["Contributor", "Storage File Data Privileged Contributor", "Storage File Data SMB Share Elevated Contributor"] +} diff --git a/.github/actions/terraform-templates/azure/env/06/config.tf b/.github/actions/terraform-templates/azure/env/06/config.tf new file mode 100644 index 00000000000..f67ddac36e9 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/06/config.tf @@ -0,0 +1,27 @@ +terraform { + required_version = ">= 1.7.5" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.99.0" + } + azuread = { + source = "hashicorp/azuread" + version = "2.48.0" + } + } +} + +provider "azurerm" { + # This is only required when the User, Service Principal, or Identity running Terraform lacks the permissions to register Azure Resource Providers. + skip_provider_registration = false + features { + template_deployment { + delete_nested_items_during_deletion = true + } + key_vault { + recover_soft_deleted_key_vaults = true + purge_soft_delete_on_destroy = true + } + } +} diff --git a/.github/actions/terraform-templates/azure/env/06/data.tf b/.github/actions/terraform-templates/azure/env/06/data.tf new file mode 100644 index 00000000000..b62ec85569e --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/06/data.tf @@ -0,0 +1,10 @@ +data "azuread_users" "owner" { + object_ids = [var.owner_object_id] +} + +data "azurerm_resource_group" "default" { + name = var.resource_group +} + +data "azurerm_subscription" "default" { +} diff --git a/.github/actions/terraform-templates/azure/env/06/main.tf b/.github/actions/terraform-templates/azure/env/06/main.tf new file mode 100644 index 00000000000..dbb2306b745 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/06/main.tf @@ -0,0 +1,27 @@ +module "init" { + source = "../../modules/init/v2" + common = local.common + dev_roles = local.dev_roles +} + +module "container_instance" { + for_each = local.container_instances + + source = "../../modules/container_instance" + common = local.common + key = each.key + image = each.value.image + cpu_cores = each.value.cpu_cores + mem_gb = each.value.mem_gb + commands = each.value.commands + shares = each.value.shares + repos = each.value.repos + exec = each.value.exec + os_type = each.value.os_type + + depends_on = [module.init] +} + +output "cinst_exec" { + value = module.container_instance[*] +} diff --git a/.github/actions/terraform-templates/azure/env/06/~locals.tf b/.github/actions/terraform-templates/azure/env/06/~locals.tf new file mode 100644 index 00000000000..2ac4d693baf --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/06/~locals.tf @@ -0,0 +1,35 @@ +variable "environment" {} +variable "owner_object_id" {} +variable "owner_email" {} +variable "uid" {} +variable "location" {} +variable "resource_group" {} + +locals { + env = var.environment + common = { + env = local.env + uid = var.uid + subscription = data.azurerm_subscription.default + tenant_id = data.azurerm_subscription.default.tenant_id + location = var.location + resource_group = data.azurerm_resource_group.default + owner_email = var.owner_email + owner = data.azuread_users.owner.users[0] + } + storage_accounts = { + } + container_instances = { + windev = { + os_type = "Windows" + image = "mcr.microsoft.com/windows:1809" + cpu_cores = 2 + mem_gb = 4 + commands = ["cmd.exe", "/c", "ping -t localhost > NUL"] + exec = "" + shares = {} + repos = {} + } + } + dev_roles = [] +} diff --git a/.github/actions/terraform-templates/azure/env/07/config.tf b/.github/actions/terraform-templates/azure/env/07/config.tf new file mode 100644 index 00000000000..f67ddac36e9 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/07/config.tf @@ -0,0 +1,27 @@ +terraform { + required_version = ">= 1.7.5" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.99.0" + } + azuread = { + source = "hashicorp/azuread" + version = "2.48.0" + } + } +} + +provider "azurerm" { + # This is only required when the User, Service Principal, or Identity running Terraform lacks the permissions to register Azure Resource Providers. + skip_provider_registration = false + features { + template_deployment { + delete_nested_items_during_deletion = true + } + key_vault { + recover_soft_deleted_key_vaults = true + purge_soft_delete_on_destroy = true + } + } +} diff --git a/.github/actions/terraform-templates/azure/env/07/data.tf b/.github/actions/terraform-templates/azure/env/07/data.tf new file mode 100644 index 00000000000..b62ec85569e --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/07/data.tf @@ -0,0 +1,10 @@ +data "azuread_users" "owner" { + object_ids = [var.owner_object_id] +} + +data "azurerm_resource_group" "default" { + name = var.resource_group +} + +data "azurerm_subscription" "default" { +} diff --git a/.github/actions/terraform-templates/azure/env/07/main.tf b/.github/actions/terraform-templates/azure/env/07/main.tf new file mode 100644 index 00000000000..610475ff7fa --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/07/main.tf @@ -0,0 +1,31 @@ +module "init" { + source = "../../modules/init/v2" + common = local.common + dev_roles = local.dev_roles +} + +module "azure_ad" { + source = "../../modules/azure_ad/v2" + + common = local.common + active_directory_domain_name = "${local.azure_ad.key}.local" + active_directory_netbios_name = local.azure_ad.key + prefix = local.azure_ad.key + + depends_on = [module.init] +} + + +module "mssql_vm" { + for_each = local.mssql_vms + + source = "../../modules/mssql_vm" + common = local.common + key = each.key + ad_dns_ips = module.azure_ad.dns_ips + domain_name = "${local.azure_ad.key}.local" + ad_username = module.azure_ad.domain_admin_username + ad_password = module.azure_ad.domain_admin_password + + depends_on = [module.init, module.azure_ad] +} diff --git a/.github/actions/terraform-templates/azure/env/07/~locals.tf b/.github/actions/terraform-templates/azure/env/07/~locals.tf new file mode 100644 index 00000000000..bcc937e5774 --- /dev/null +++ b/.github/actions/terraform-templates/azure/env/07/~locals.tf @@ -0,0 +1,42 @@ +variable "environment" {} +variable "owner_object_id" {} +variable "owner_email" {} +variable "uid" {} +variable "location" {} +variable "resource_group" {} + +locals { + env = var.environment + common = { + env = local.env + uid = var.uid + subscription = data.azurerm_subscription.default + tenant_id = data.azurerm_subscription.default.tenant_id + location = var.location + resource_group = data.azurerm_resource_group.default + owner_email = var.owner_email + owner = data.azuread_users.owner.users[0] + } + storage_accounts = { + } + mssql_vms = { + dev = {} + } + azure_ad = { + key = var.uid + domain_name = "josiah0601gmail.onmicrosoft.com" + } + container_instances = { + windev = { + os_type = "Windows" + image = "mcr.microsoft.com/windows:1809" + cpu_cores = 2 + mem_gb = 4 + commands = ["cmd.exe", "/c", "ping -t localhost > NUL"] + exec = "" + shares = {} + repos = {} + } + } + dev_roles = [] +} diff --git a/.github/actions/terraform-templates/azure/modules/api_management/api_default.tf b/.github/actions/terraform-templates/azure/modules/api_management/api_default.tf new file mode 100644 index 00000000000..0d08d881896 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/api_management/api_default.tf @@ -0,0 +1,83 @@ +resource "azurerm_api_management_api" "default" { + name = "${var.common.env}-api" + resource_group_name = var.common.resource_group.name + api_management_name = azurerm_api_management.default.name + revision = "1" + display_name = "${var.common.env} API" + path = var.common.env + protocols = ["https"] +} + +resource "azurerm_api_management_product" "default" { + product_id = "${var.common.env}-product" + api_management_name = azurerm_api_management.default.name + resource_group_name = var.common.resource_group.name + display_name = "${var.common.env} Product" + subscription_required = true + subscriptions_limit = 10 + approval_required = true + published = true +} + +resource "azurerm_api_management_product_api" "default" { + api_name = azurerm_api_management_api.default.name + product_id = azurerm_api_management_product.default.product_id + api_management_name = azurerm_api_management.default.name + resource_group_name = var.common.resource_group.name +} + +resource "azurerm_api_management_api_operation" "default_fetch" { + operation_id = "${var.common.env}-fetch" + api_name = azurerm_api_management_api.default.name + api_management_name = azurerm_api_management.default.name + resource_group_name = var.common.resource_group.name + display_name = "${var.common.env} fetch db" + method = "GET" + url_template = "/db" + description = "This can only be done by the logged in user." + + response { + status_code = 200 + } +} + +resource "azurerm_api_management_api_operation" "default_insert" { + operation_id = "${var.common.env}-insert" + api_name = azurerm_api_management_api.default.name + api_management_name = azurerm_api_management.default.name + resource_group_name = var.common.resource_group.name + display_name = "${var.common.env} insert db" + method = "POST" + url_template = "/db" + description = "This can only be done by the logged in user." + + response { + status_code = 200 + } +} + +resource "azurerm_api_management_backend" "default" { + name = "${var.common.env}-la-db-backend" + resource_group_name = var.common.resource_group.name + api_management_name = azurerm_api_management.default.name + protocol = "http" + url = var.logic_app_endpoint +} + +resource "azurerm_api_management_api_operation_policy" "default" { + api_name = azurerm_api_management_api.default.name + operation_id = azurerm_api_management_api_operation.default_fetch.operation_id + api_management_name = azurerm_api_management.default.name + resource_group_name = var.common.resource_group.name + xml_content = <<XML +<policies> + <inbound> + <base /> + <set-backend-service backend-id="${azurerm_api_management_backend.default.name}" /> + <set-method>POST</set-method> + <rewrite-uri template="?" /> + <set-header name="Ocp-Apim-Subscription-Key" exists-action="delete" /> + </inbound> +</policies> +XML +} diff --git a/.github/actions/terraform-templates/azure/modules/api_management/main.tf b/.github/actions/terraform-templates/azure/modules/api_management/main.tf new file mode 100644 index 00000000000..f8fe2bb88d6 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/api_management/main.tf @@ -0,0 +1,13 @@ +resource "azurerm_api_management" "default" { + name = "apim-${var.common.uid}-${var.common.env}" + resource_group_name = var.common.resource_group.name + location = var.common.location + publisher_name = var.common.env + publisher_email = var.common.owner_email + + sku_name = "Consumption_0" + + identity { + type = "SystemAssigned" + } +} diff --git a/.github/actions/terraform-templates/azure/modules/api_management/~inputs.tf b/.github/actions/terraform-templates/azure/modules/api_management/~inputs.tf new file mode 100644 index 00000000000..5e02f48586a --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/api_management/~inputs.tf @@ -0,0 +1,4 @@ +variable "common" {} +variable "logic_app_endpoint" { + sensitive = true +} diff --git a/.github/actions/terraform-templates/azure/modules/api_management/~outputs.tf b/.github/actions/terraform-templates/azure/modules/api_management/~outputs.tf new file mode 100644 index 00000000000..e69de29bb2d diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v1/main.tf b/.github/actions/terraform-templates/azure/modules/azure_ad/v1/main.tf new file mode 100644 index 00000000000..0c0c8d25c36 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_ad/v1/main.tf @@ -0,0 +1,131 @@ +# https://schnerring.net/blog/set-up-azure-active-directory-domain-services-aadds-with-terraform-updated/ +# service principal for Domain Controller Services +resource "azuread_service_principal" "aadds" { + client_id = "2565bd9d-da50-47d4-8b85-4c97f669dc36" +} + +# Microsoft.AAD Resource Provider Registration +resource "azurerm_resource_provider_registration" "aadds" { + name = "Microsoft.AAD" +} + +# DC Admin Group and User +resource "azuread_group" "dc_admins" { + display_name = "AAD DC Administrators" + description = "AADDS Administrators" + members = [azuread_user.dc_admin.object_id] + security_enabled = true +} + +resource "random_password" "dc_admin" { + length = 64 +} + +resource "azuread_user" "dc_admin" { + user_principal_name = "dc-admin@${var.domain_name}" + display_name = "AADDS DC Administrator" + password = random_password.dc_admin.result +} + +# Resource Group for Azure Active Directory Domain Services (AADDS) +resource "azurerm_resource_group" "aadds" { + name = "aadds-${var.key}-rg" + location = "East US" +} + +# Network Resources +resource "azurerm_virtual_network" "aadds" { + name = "aadds-vnet" + location = azurerm_resource_group.aadds.location + resource_group_name = azurerm_resource_group.aadds.name + address_space = ["10.0.0.0/16"] +} + +resource "azurerm_subnet" "aadds" { + name = "aadds-snet" + resource_group_name = azurerm_resource_group.aadds.name + virtual_network_name = azurerm_virtual_network.aadds.name + address_prefixes = ["10.0.0.0/24"] +} + +resource "azurerm_network_security_group" "aadds" { + name = "aadds-nsg" + location = azurerm_resource_group.aadds.location + resource_group_name = azurerm_resource_group.aadds.name + + security_rule { + name = "AllowRD" + priority = 201 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "3389" + source_address_prefix = "CorpNetSaw" + destination_address_prefix = "*" + } + + security_rule { + name = "AllowPSRemoting" + priority = 301 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "5986" + source_address_prefix = "AzureActiveDirectoryDomainServices" + destination_address_prefix = "*" + } + + /* + security_rule { + name = "AllowLDAPS" + priority = 401 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "636" + source_address_prefix = "<Authorized LDAPS IPs>" + destination_address_prefix = "*" + } + */ +} + +resource azurerm_subnet_network_security_group_association "aadds" { + subnet_id = azurerm_subnet.aadds.id + network_security_group_id = azurerm_network_security_group.aadds.id +} + +# AADDS Managed Domain +resource "azurerm_active_directory_domain_service" "aadds" { + name = "aadds" + location = azurerm_resource_group.aadds.location + resource_group_name = azurerm_resource_group.aadds.name + domain_configuration_type = "FullySynced" + + domain_name = var.domain_name + sku = "Standard" + + initial_replica_set { + subnet_id = azurerm_subnet.aadds.id + } + + notifications { + additional_recipients = ["josiah0601@gmail.com"] + notify_dc_admins = true + notify_global_admins = true + } + + security { + sync_kerberos_passwords = true + sync_ntlm_passwords = true + sync_on_prem_passwords = true + } + + depends_on = [ + azuread_service_principal.aadds, + azurerm_resource_provider_registration.aadds, + azurerm_subnet_network_security_group_association.aadds, + ] +} diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v1/~inputs.tf b/.github/actions/terraform-templates/azure/modules/azure_ad/v1/~inputs.tf new file mode 100644 index 00000000000..76558c828dd --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_ad/v1/~inputs.tf @@ -0,0 +1,3 @@ +variable "common" {} +variable "key" {} +variable "domain_name" {} diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v1/~outputs.tf b/.github/actions/terraform-templates/azure/modules/azure_ad/v1/~outputs.tf new file mode 100644 index 00000000000..3eaefa0b195 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_ad/v1/~outputs.tf @@ -0,0 +1,3 @@ +output "dns_ips" { + value = azurerm_active_directory_domain_service.aadds.initial_replica_set.0.domain_controller_ip_addresses +} diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/files/FirstLogonCommands.xml b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/files/FirstLogonCommands.xml new file mode 100644 index 00000000000..41a339dc946 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/files/FirstLogonCommands.xml @@ -0,0 +1,22 @@ +<!-- + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: MPL-2.0 +--> + +<FirstLogonCommands> + <SynchronousCommand> + <CommandLine>cmd /c "mkdir C:\terraform"</CommandLine> + <Description>Create the Terraform working directory</Description> + <Order>11</Order> + </SynchronousCommand> + <SynchronousCommand> + <CommandLine>cmd /c "copy C:\AzureData\CustomData.bin C:\terraform\winrm.ps1"</CommandLine> + <Description>Move the CustomData file to the working directory</Description> + <Order>12</Order> + </SynchronousCommand> + <SynchronousCommand> + <CommandLine>powershell.exe -sta -ExecutionPolicy Unrestricted -file C:\terraform\winrm.ps1</CommandLine> + <Description>Execute the WinRM enabling script</Description> + <Order>13</Order> + </SynchronousCommand> +</FirstLogonCommands> \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/files/winrm.ps1 b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/files/winrm.ps1 new file mode 100644 index 00000000000..afdf95de2c0 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/files/winrm.ps1 @@ -0,0 +1,21 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +$Cert = New-SelfSignedCertificate -DnsName $RemoteHostName, $ComputerName ` + -CertStoreLocation "cert:\LocalMachine\My" ` + -FriendlyName "Test WinRM Cert" + +$Cert | Out-String + +$Thumbprint = $Cert.Thumbprint + +Write-Host "Enable HTTPS in WinRM" +$WinRmHttps = "@{Hostname=`"$RemoteHostName`"; CertificateThumbprint=`"$Thumbprint`"}" +winrm create winrm/config/Listener?Address=*+Transport=HTTPS $WinRmHttps + +Write-Host "Set Basic Auth in WinRM" +$WinRmBasic = "@{Basic=`"true`"}" +winrm set winrm/config/service/Auth $WinRmBasic + +Write-Host "Open Firewall Port" +netsh advfirewall firewall add rule name="Windows Remote Management (HTTPS-In)" dir=in action=allow protocol=TCP localport=5985 \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/main.tf b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/main.tf new file mode 100644 index 00000000000..5d326661bc1 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/main.tf @@ -0,0 +1,93 @@ +# https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/virtual-machines/windows/vm-joined-to-active-directory + +resource "random_password" "domain_password" { + length = 16 + special = false + upper = true + lower = true + numeric = true +} + +resource "azurerm_virtual_network" "example" { + name = join("-", [var.prefix, "network"]) + location = var.common.location + address_space = ["10.0.0.0/16"] + resource_group_name = var.common.resource_group.name + dns_servers = ["10.0.1.4", "8.8.8.8"] +} + +resource "azurerm_subnet" "domain-controllers" { + name = "domain-controllers" + address_prefixes = ["10.0.1.0/24"] + resource_group_name = var.common.resource_group.name + virtual_network_name = azurerm_virtual_network.example.name +} + +resource "azurerm_subnet" "domain-members" { + name = "domain-members" + address_prefixes = ["10.0.2.0/24"] + resource_group_name = var.common.resource_group.name + virtual_network_name = azurerm_virtual_network.example.name +} + + +resource "azurerm_network_interface" "dc_nic" { + name = join("-", [var.prefix, "dc-primary"]) + location = var.common.location + resource_group_name = var.common.resource_group.name + ip_configuration { + name = "primary" + private_ip_address_allocation = "Static" + private_ip_address = "10.0.1.4" + subnet_id = azurerm_subnet.domain-controllers.id + } +} + +resource "azurerm_windows_virtual_machine" "domain-controller" { + name = local.virtual_machine_name + resource_group_name = var.common.resource_group.name + location = var.common.location + size = "Standard_B2s" + admin_username = "${var.prefix}admin" + admin_password = random_password.domain_password.result + custom_data = local.custom_data + + network_interface_ids = [ + azurerm_network_interface.dc_nic.id, + ] + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter" + version = "latest" + } + + additional_unattend_content { + content = local.auto_logon_data + setting = "AutoLogon" + } + + additional_unattend_content { + content = local.first_logon_data + setting = "FirstLogonCommands" + } +} + +resource "azurerm_virtual_machine_extension" "create-ad-forest" { + name = "create-active-directory-forest" + virtual_machine_id = azurerm_windows_virtual_machine.domain-controller.id + publisher = "Microsoft.Compute" + type = "CustomScriptExtension" + type_handler_version = "1.9" + settings = <<SETTINGS + { + "commandToExecute": "powershell.exe -Command \"${local.powershell_command}\"" + } +SETTINGS +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/~inputs.tf b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/~inputs.tf new file mode 100644 index 00000000000..50a788aaee7 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/~inputs.tf @@ -0,0 +1,31 @@ +variable "common" {} + +variable "prefix" { + description = "The prefix which should be used for all resources in this example" +} + +variable "active_directory_domain_name" { + description = "the domain name for Active Directory, for example `consoto.local`" +} + +variable "active_directory_netbios_name" { + description = "The netbios name of the Active Directory domain, for example `consoto`" +} + +locals { + virtual_machine_name = join("-", [var.prefix, "dc"]) + virtual_machine_fqdn = join(".", [local.virtual_machine_name, var.active_directory_domain_name]) + auto_logon_data = "<AutoLogon><Password><Value>${random_password.domain_password.result}</Value></Password><Enabled>true</Enabled><LogonCount>1</LogonCount><Username>${var.prefix}admin</Username></AutoLogon>" + first_logon_data = file("${path.module}/files/FirstLogonCommands.xml") + custom_data_params = "Param($RemoteHostName = \"${local.virtual_machine_fqdn}\", $ComputerName = \"${local.virtual_machine_name}\")" + custom_data = base64encode(join(" ", [local.custom_data_params, file("${path.module}/files/winrm.ps1")])) + + import_command = "Import-Module ADDSDeployment" + password_command = "$password = ConvertTo-SecureString ${random_password.domain_password.result} -AsPlainText -Force" + install_ad_command = "Add-WindowsFeature -name ad-domain-services -IncludeManagementTools" + configure_ad_command = "Install-ADDSForest -CreateDnsDelegation:$false -DomainMode Win2012R2 -DomainName ${var.active_directory_domain_name} -DomainNetbiosName ${var.active_directory_netbios_name} -ForestMode Win2012R2 -InstallDns:$true -SafeModeAdministratorPassword $password -Force:$true" + shutdown_command = "shutdown -r -t 10" + exit_code_hack = "exit 0" + powershell_command = "${local.import_command}; ${local.password_command}; ${local.install_ad_command}; ${local.configure_ad_command}; ${local.shutdown_command}; ${local.exit_code_hack}" + +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/~outputs.tf b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/~outputs.tf new file mode 100644 index 00000000000..c712a0268cd --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/~outputs.tf @@ -0,0 +1,12 @@ +output "dns_ips" { + value = ["10.0.1.4", "8.8.8.8"]//azurerm_active_directory_domain_service.aadds.initial_replica_set.0.domain_controller_ip_addresses +} + +output "domain_admin_username" { + value = "${var.prefix}admin" +} + +output "domain_admin_password" { + value = random_password.domain_password.result +} + diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/main.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/main.tf new file mode 100644 index 00000000000..8fc930a74d3 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/main.tf @@ -0,0 +1,84 @@ +data "http" "icanhazip" { + url = "http://icanhazip.com" +} + +locals { + public_ip = chomp(data.http.icanhazip.response_body) +} + +resource "azuread_group" "sqladmins" { + display_name = "sqladmins-${var.common.env}" + owners = [var.common.owner.object_id] + security_enabled = true + + members = [ + var.common.owner.object_id, + /* more users */ + ] +} + +# Create an Azure SQL Server +resource "azurerm_mssql_server" "default" { + name = "sqlserver-${var.common.uid}-${var.common.env}" + resource_group_name = var.common.resource_group.name + location = var.common.location + version = "12.0" + minimum_tls_version = "1.2" + + azuread_administrator { + login_username = azuread_group.sqladmins.display_name + object_id = azuread_group.sqladmins.object_id + azuread_authentication_only = true + } + + identity { + type = "SystemAssigned" + } +} + +# Create an Azure SQL Database +resource "azurerm_mssql_database" "default" { + name = "db-${var.common.env}" + server_id = azurerm_mssql_server.default.id + collation = "SQL_Latin1_General_CP1_CI_AS" + license_type = "LicenseIncluded" + max_size_gb = 10 + read_scale = false + sku_name = "S0" + zone_redundant = false + enclave_type = "VBS" + storage_account_type = "Local" + #auto_pause_delay_in_minutes = 10 + + # prevent the possibility of accidental data loss + lifecycle { + #prevent_destroy = true + } +} + +resource "mssql_user" "la" { + server { + host = azurerm_mssql_server.default.fully_qualified_domain_name + azuread_default_chain_auth {} + } + + database = azurerm_mssql_database.default.name + username = var.logic_app_name + roles = ["db_datareader", "db_datawriter"] + + depends_on = [azurerm_mssql_firewall_rule.default] +} + +resource "azurerm_mssql_firewall_rule" "default" { + name = "terraform-firewall-rule" + server_id = azurerm_mssql_server.default.id + start_ip_address = local.public_ip + end_ip_address = local.public_ip +} + +resource "azurerm_mssql_firewall_rule" "azure_access" { + name = "allow-access-from-azure" + server_id = azurerm_mssql_server.default.id + start_ip_address = "0.0.0.0" + end_ip_address = "0.0.0.0" +} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~inputs.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~inputs.tf new file mode 100644 index 00000000000..1357cab6441 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~inputs.tf @@ -0,0 +1,3 @@ +variable "common" {} +variable "logic_app_ids" {} +variable "logic_app_name" {} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~outputs.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~outputs.tf new file mode 100644 index 00000000000..50b94d9bbc9 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~outputs.tf @@ -0,0 +1,11 @@ +output "id" { + value = azurerm_mssql_server.default.identity[0].principal_id +} + +output "fqdn" { + value = azurerm_mssql_server.default.fully_qualified_domain_name +} + +output "db_name" { + value = azurerm_mssql_database.default.name +} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~providers.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~providers.tf new file mode 100644 index 00000000000..74f3280d715 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~providers.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + mssql = { + version = "~> 0.3" + source = "betr-io/mssql" + } + } +} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/databases.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/databases.tf new file mode 100644 index 00000000000..460f2f6232e --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/databases.tf @@ -0,0 +1,22 @@ +# Create an Azure SQL Database +resource "azurerm_mssql_database" "default" { + for_each = toset(var.databases) + + name = each.value + server_id = azurerm_mssql_server.default.id + collation = "SQL_Latin1_General_CP1_CI_AS" + license_type = "LicenseIncluded" + read_scale = false + zone_redundant = false + enclave_type = "VBS" + storage_account_type = "Local" + #auto_pause_delay_in_minutes = 10 + + sku_name = try(var.epool) == true ? "ElasticPool" : "S0" + elastic_pool_id = try(var.epool) == true ? azurerm_mssql_elasticpool.default["default"].id : null + + # prevent the possibility of accidental data loss + lifecycle { + #prevent_destroy = true + } +} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/elastic_pool.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/elastic_pool.tf new file mode 100644 index 00000000000..a099515fb25 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/elastic_pool.tf @@ -0,0 +1,22 @@ +resource "azurerm_mssql_elasticpool" "default" { + for_each = try(var.epool) == true ? toset(["default"]) : [] + + name = "epool-${var.common.uid}-${var.common.env}-${var.key}" + resource_group_name = var.common.resource_group.name + location = var.common.location + server_name = azurerm_mssql_server.default.name + license_type = "LicenseIncluded" + enclave_type = "VBS" + max_size_gb = 4.8828125 + + sku { + name = "BasicPool" + tier = "Basic" + capacity = 50 + } + + per_database_settings { + min_capacity = 0 + max_capacity = 5 + } +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/main.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/main.tf new file mode 100644 index 00000000000..a3dde048639 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/main.tf @@ -0,0 +1,43 @@ +data "http" "icanhazip" { + url = "http://icanhazip.com" +} + +locals { + public_ip = chomp(data.http.icanhazip.response_body) +} + +# Create an Azure SQL Server +resource "azurerm_mssql_server" "default" { + name = "sqlserver-${var.common.uid}-${var.common.env}-${var.key}" + resource_group_name = var.common.resource_group.name + location = var.common.location + version = "12.0" + minimum_tls_version = "1.2" + + administrator_login = var.admin_username + administrator_login_password = var.admin_password + + azuread_administrator { + login_username = var.sqladmins.display_name + object_id = var.sqladmins.object_id + azuread_authentication_only = false + } + + identity { + type = "SystemAssigned" + } +} + +resource "azurerm_mssql_firewall_rule" "default" { + name = "terraform-firewall-rule" + server_id = azurerm_mssql_server.default.id + start_ip_address = local.public_ip + end_ip_address = local.public_ip +} + +resource "azurerm_mssql_firewall_rule" "azure_access" { + name = "allow-access-from-azure" + server_id = azurerm_mssql_server.default.id + start_ip_address = "0.0.0.0" + end_ip_address = "0.0.0.0" +} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/~inputs.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/~inputs.tf new file mode 100644 index 00000000000..e7dfef678a6 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/~inputs.tf @@ -0,0 +1,11 @@ +variable "common" {} +variable "key" {} +variable "sqladmins" {} +variable "databases" { + type = list(string) +} +variable "epool" {} +variable "admin_username" {} +variable "admin_password" { + sensitive = true +} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/~outputs.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/~outputs.tf new file mode 100644 index 00000000000..ed913fbd2d3 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/~outputs.tf @@ -0,0 +1,7 @@ +output "id" { + value = azurerm_mssql_server.default.identity[0].principal_id +} + +output "fqdn" { + value = azurerm_mssql_server.default.fully_qualified_domain_name +} diff --git a/.github/actions/terraform-templates/azure/modules/cloudshell/main.tf b/.github/actions/terraform-templates/azure/modules/cloudshell/main.tf new file mode 100644 index 00000000000..15bcd4295f7 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/cloudshell/main.tf @@ -0,0 +1,162 @@ +#================ Existing Resources ================ +data "azurerm_resource_group" "existing-rg" { + name = var.existing-vnet-resource-group +} + +data "azurerm_virtual_network" "existing-vnet" { + name = var.existing-vnet-name + resource_group_name = data.azurerm_resource_group.existing-rg.name +} + +#================ Subnets ================ +resource "azurerm_subnet" "container-subnet" { + name = var.container-subnet-name + address_prefixes = var.container-subnet-prefix + resource_group_name = data.azurerm_resource_group.existing-rg.name + virtual_network_name = data.azurerm_virtual_network.existing-vnet.name + service_endpoints = ["Microsoft.Storage"] + delegation { + name = "delegation" + service_delegation { + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + name = "Microsoft.ContainerInstance/containerGroups" + } + } +} + +resource "azurerm_subnet" "relay-subnet" { + name = var.relay-subnet-name + address_prefixes = var.relay-subnet-prefix + resource_group_name = data.azurerm_resource_group.existing-rg.name + virtual_network_name = data.azurerm_virtual_network.existing-vnet.name + enforce_private_link_endpoint_network_policies = true #true = Disable; false = Enable + enforce_private_link_service_network_policies = false #true = Disable; false = Enable + depends_on = [ + azurerm_subnet.container-subnet + ] +} + +#================ Network Profile ================ +resource "azurerm_network_profile" "network-profile" { + name = "aci-networkProfile-${data.azurerm_resource_group.existing-rg.location}" + resource_group_name = data.azurerm_resource_group.existing-rg.name + location = data.azurerm_resource_group.existing-rg.location + container_network_interface { + name = "eth-${azurerm_subnet.container-subnet.name}" + ip_configuration { + name = "ipconfig-${azurerm_subnet.container-subnet.name}" + subnet_id = azurerm_subnet.container-subnet.id + } + } + tags = var.tags + lifecycle { ignore_changes = [tags] } + depends_on = [ + azurerm_subnet.container-subnet + ] +} + +#================ Relay Namespace ================ +resource "azurerm_relay_namespace" "relay-namespace" { + name = var.relay-namespace-name # must be unique + resource_group_name = data.azurerm_resource_group.existing-rg.name + location = data.azurerm_resource_group.existing-rg.location + sku_name = "Standard" + tags = var.tags + lifecycle { ignore_changes = [tags] } +} + +#================ Role Assignments ================ +resource "random_uuid" "network" { +} + +resource "random_uuid" "contributor" { +} + +data "azurerm_role_definition" "networkRoleDefinition" { + role_definition_id = "4d97b98b-1d4f-4787-a291-c67834d212e7" +} + +data "azurerm_role_definition" "contributorRoleDefinition" { + role_definition_id = "b24988ac-6180-42a0-ab88-20f7382dd24c" +} + +resource "azurerm_role_assignment" "role-assignment-network" { + scope = azurerm_network_profile.network-profile.id + role_definition_name = data.azurerm_role_definition.networkRoleDefinition.name + principal_id = var.ACI-OID + depends_on = [ + azurerm_network_profile.network-profile + ] +} + +resource "azurerm_role_assignment" "role-assignment-contributor" { + scope = azurerm_relay_namespace.relay-namespace.id + role_definition_name = data.azurerm_role_definition.contributorRoleDefinition.name + principal_id = var.ACI-OID + depends_on = [ + azurerm_relay_namespace.relay-namespace + ] +} + +#================ Private Endpoints ================ +resource "azurerm_private_endpoint" "private-endpoint" { + name = var.private-endpoint-name + resource_group_name = data.azurerm_resource_group.existing-rg.name + location = data.azurerm_resource_group.existing-rg.location + subnet_id = azurerm_subnet.relay-subnet.id + private_service_connection { + name = "${data.azurerm_virtual_network.existing-vnet.location}-privsvc" # Max Length 80 characters + private_connection_resource_id = azurerm_relay_namespace.relay-namespace.id + is_manual_connection = false + subresource_names = ["namespace"] + } + tags = var.tags + lifecycle { ignore_changes = [tags] } + depends_on = [ + azurerm_relay_namespace.relay-namespace, + azurerm_subnet.relay-subnet + ] +} + +#================ Private DNS ================ +resource "azurerm_private_dns_zone" "global-private-dns-zone" { + name = "privatelink.servicebus.windows.net" + resource_group_name = data.azurerm_resource_group.existing-rg.name + tags = var.tags + lifecycle { ignore_changes = [tags] } +} + +resource "azurerm_private_dns_zone_virtual_network_link" "dns-zone-link" { + name = azurerm_relay_namespace.relay-namespace.name + resource_group_name = data.azurerm_resource_group.existing-rg.name + private_dns_zone_name = "privatelink.servicebus.windows.net" + virtual_network_id = data.azurerm_virtual_network.existing-vnet.id + depends_on = [azurerm_private_dns_zone.global-private-dns-zone] +} + +resource "azurerm_private_dns_a_record" "ussc-dns-a-record" { + name = azurerm_relay_namespace.relay-namespace.name + zone_name = azurerm_private_dns_zone.global-private-dns-zone.name + resource_group_name = data.azurerm_resource_group.existing-rg.name + ttl = 3600 + records = [cidrhost(var.relay-subnet-prefix[0], 4)] + depends_on = [azurerm_private_dns_zone.global-private-dns-zone] +} + +#================ Storage ================ +resource "azurerm_storage_account" "storageaccount" { + name = var.storageaccount-name + resource_group_name = data.azurerm_resource_group.existing-rg.name + location = data.azurerm_resource_group.existing-rg.location + account_tier = "Standard" + account_replication_type = "LRS" + min_tls_version = "TLS1_2" + allow_nested_items_to_be_public = false + network_rules { + default_action = "Deny" + virtual_network_subnet_ids = [azurerm_subnet.container-subnet.id] + } + + tags = merge(var.tags, { ms-resource-usage = "azure-cloud-shell" }) + lifecycle { ignore_changes = [tags] } +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/cloudshell/outputs.tf b/.github/actions/terraform-templates/azure/modules/cloudshell/outputs.tf new file mode 100644 index 00000000000..9da5083260a --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/cloudshell/outputs.tf @@ -0,0 +1,7 @@ +output "container-subnet-id" { + value = azurerm_subnet.container-subnet.id +} + +output "relay-subnet-id" { + value = azurerm_subnet.relay-subnet.id +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/cloudshell/variables.tf b/.github/actions/terraform-templates/azure/modules/cloudshell/variables.tf new file mode 100644 index 00000000000..eefb31f075b --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/cloudshell/variables.tf @@ -0,0 +1,65 @@ +variable "existing-vnet-name" { + type = string + description = "the name of the existing virtual network" +} + +variable "existing-vnet-resource-group" { + type = string + description = "the name of the resource containing the existing virtual network" +} + +variable "relay-namespace-name" { + type = string + description = "the name to be assigned to the relay namespace" + default = "cshrelay" +} + +variable "ACI-OID" { + type = string + description = "Azure Container Instance OID" +} + +variable "container-subnet-name" { + type = string + description = "the name to be assigned to the cloudshell container subnet" + default = "cloudshellsubnet" +} + +variable "container-subnet-prefix" { + type = list(string) + description = "the list of address prefix(es) to be assigned to the cloudshell container subnet" +} + +variable "relay-subnet-name" { + type = string + description = "the name to be assigned to the relay subnet" + default = "relaysubnet" +} + +variable "relay-subnet-prefix" { + type = list(string) + description = "the list of address prefix(es) to be assigned to the relay subnet" +} + +variable "storage-subnet-name" { + type = string + description = "the name to be assigned to the storage subnet" + default = "storagesubnet" +} + +variable "storageaccount-name" { + type = string + description = "the name of the storage account to create" +} + +variable "private-endpoint-name" { + type = string + description = "the name to be assigned to the private endpoint" + default = "cloudshellRelayEndpoint" +} + +variable "tags" { + type = map(string) + description = "the list of tags to be assigned" + default = {} +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/container_app/main.tf b/.github/actions/terraform-templates/azure/modules/container_app/main.tf new file mode 100644 index 00000000000..3bb6080b45b --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/container_app/main.tf @@ -0,0 +1,59 @@ +resource "azurerm_container_app_environment" "default" { + name = "capp-env-${var.key}" + location = var.common.location + resource_group_name = var.common.resource_group.name +} + +resource "azurerm_storage_share" "default" { + name = "capp-share-${var.key}" + quota = "200" + storage_account_name = var.storage_account.name +} + +resource "azurerm_container_app_environment_storage" "example" { + name = "capp-env-storage-${var.key}" + container_app_environment_id = azurerm_container_app_environment.default.id + account_name = var.storage_account.name + share_name = azurerm_storage_share.default.name + access_key = var.storage_account.primary_access_key + access_mode = "ReadWrite" +} + +resource "azurerm_container_app" "default" { + name = "capp-${var.key}" + container_app_environment_id = azurerm_container_app_environment.default.id + resource_group_name = var.common.resource_group.name + revision_mode = "Single" + + ingress { + allow_insecure_connections = true + target_port = 22 + transport = "auto" + traffic_weight { + latest_revision = true + percentage = 100 + } + } + template { + container { + name = "examplecontainerapp" + image = "mcr.microsoft.com/azure-dev-cli-apps:latest" + cpu = 1.0 + memory = "2Gi" + command = [ + "/bin/bash", + "-c", + "echo 'Hello, Azure Container App!' && sleep infinity" + ] + volume_mounts { + name = azurerm_storage_share.default.name + path = "/mnt/storage" + } + } + volume { + name = azurerm_storage_share.default.name + storage_name = azurerm_container_app_environment_storage.example.name + storage_type = "AzureFile" + } + } +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/container_app/~inputs.tf b/.github/actions/terraform-templates/azure/modules/container_app/~inputs.tf new file mode 100644 index 00000000000..66fe143f1a6 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/container_app/~inputs.tf @@ -0,0 +1,3 @@ +variable "common" {} +variable "key" {} +variable "storage_account" {} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/container_app/~outputs.tf b/.github/actions/terraform-templates/azure/modules/container_app/~outputs.tf new file mode 100644 index 00000000000..e69de29bb2d diff --git a/.github/actions/terraform-templates/azure/modules/container_instance/main.tf b/.github/actions/terraform-templates/azure/modules/container_instance/main.tf new file mode 100644 index 00000000000..4b4fa15a246 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/container_instance/main.tf @@ -0,0 +1,58 @@ +resource "azurerm_storage_share" "default" { + for_each = var.shares + + name = "cinst-share-${var.key}" + quota = each.value.gb + storage_account_name = var.storage_account.name + access_tier = each.value.tier +} + +resource "azurerm_container_group" "default" { + name = "cinst-${var.key}" + location = var.common.location + resource_group_name = var.common.resource_group.name + ip_address_type = "Public" + os_type = var.os_type + dns_name_label = "cinst-${var.key}" + + container { + name = "cinst-${var.key}" + image = var.image + cpu = var.cpu_cores + memory = var.mem_gb + commands = var.commands + + secure_environment_variables = { + USER_PASSWORD = var.user_password + } + + ports { + port = 2222 + protocol = "TCP" + } + + dynamic "volume" { + for_each = var.shares + content { + name = volume.key + storage_account_name = var.storage_account.name + storage_account_key = var.storage_account.primary_access_key + share_name = azurerm_storage_share.default[volume.key].name + read_only = false + mount_path = volume.value.mount_path + } + } + + dynamic "volume" { + for_each = var.repos + content { + name = volume.key + mount_path = volume.value.mount_path + git_repo { + url = volume.value.url + directory = "/" + } + } + } + } +} diff --git a/.github/actions/terraform-templates/azure/modules/container_instance/~inputs.tf b/.github/actions/terraform-templates/azure/modules/container_instance/~inputs.tf new file mode 100644 index 00000000000..04345384e36 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/container_instance/~inputs.tf @@ -0,0 +1,27 @@ +variable "common" {} +variable "key" {} +variable "storage_account" { + default = null +} +variable "image" {} +variable "cpu_cores" {} +variable "mem_gb" {} +variable "commands" { + default = [] +} +variable "exec" { + default = "" +} +variable "repos" { + default = {} +} +variable "shares" { + default = {} +} +variable "os_type" { + default = "Linux" +} +variable "user_password" { + sensitive = true + default = null +} diff --git a/.github/actions/terraform-templates/azure/modules/container_instance/~outputs.tf b/.github/actions/terraform-templates/azure/modules/container_instance/~outputs.tf new file mode 100644 index 00000000000..fd6df497c9f --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/container_instance/~outputs.tf @@ -0,0 +1,3 @@ +output "exec" { + value = "az container exec --name ${azurerm_container_group.default.name} --resource-group ${var.common.resource_group.name} --container-name ${azurerm_container_group.default.name} ${var.exec != "" ? "--exec-command '${var.exec}'" : ""}" +} diff --git a/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/main.tf b/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/main.tf new file mode 100644 index 00000000000..f3c41207767 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/main.tf @@ -0,0 +1,9 @@ +resource "azurerm_cosmosdb_postgresql_cluster" "default" { + name = "cluster-${var.common.uid}-${var.common.env}-${var.key}" + location = var.common.location + resource_group_name = var.common.resource_group.name + administrator_login_password = var.admin_password + coordinator_storage_quota_in_mb = 262144 + coordinator_vcore_count = 4 + node_count = 0 +} diff --git a/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~inputs.tf b/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~inputs.tf new file mode 100644 index 00000000000..c86953cc18c --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~inputs.tf @@ -0,0 +1,5 @@ +variable "common" {} +variable "key" {} +variable "admin_password" { + sensitive = true +} diff --git a/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~outputs.tf b/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~outputs.tf new file mode 100644 index 00000000000..f15b6a1bc12 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~outputs.tf @@ -0,0 +1,3 @@ +output "meta" { + value = azurerm_cosmosdb_postgresql_cluster.default +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/data_factory/v1/main.tf b/.github/actions/terraform-templates/azure/modules/data_factory/v1/main.tf new file mode 100644 index 00000000000..b4e4badc5f7 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/data_factory/v1/main.tf @@ -0,0 +1,85 @@ +resource "azurerm_data_factory" "default" { + name = "df-${var.common.uid}-${var.common.env}" + location = var.common.location + resource_group_name = var.common.resource_group.name + + identity { + type = "SystemAssigned" + } +} + +resource "azurerm_role_assignment" "df_blob" { + scope = var.common.resource_group.id + role_definition_name = "Storage Blob Data Contributor" + principal_id = azurerm_data_factory.default.identity[0].principal_id +} + +resource "azurerm_role_assignment" "df_kv" { + scope = var.common.resource_group.id + role_definition_name = "Key Vault Reader" + principal_id = azurerm_data_factory.default.identity[0].principal_id +} + +resource "azurerm_role_assignment" "df_kv_secrets" { + scope = var.common.resource_group.id + role_definition_name = "Key Vault Secrets User" + principal_id = azurerm_data_factory.default.identity[0].principal_id +} + +resource "azurerm_data_factory_linked_service_azure_blob_storage" "default" { + name = var.storage_account.name + data_factory_id = azurerm_data_factory.default.id + service_endpoint = var.storage_account.primary_blob_endpoint + use_managed_identity = true +} + +resource "azurerm_data_factory_linked_custom_service" "rest_default" { + name = "${var.common.env}-rest" + data_factory_id = azurerm_data_factory.default.id + description = "Managed by Terraform" + + type = "RestService" + type_properties_json = <<JSON +{ + "url": "${var.secrets["${var.common.env}-API-ENDPOINT"].value}", + "enableServerCertificateValidation": true, + "authenticationType": "Basic", + "userName": "${var.secrets["${var.common.env}-KEY"].value}", + "password": { + "type": "AzureKeyVaultSecret", + "store": { + "referenceName": "${azurerm_data_factory_linked_service_key_vault.default.name}", + "type": "LinkedServiceReference" + }, + "secretName": "${var.common.env}-SECRET" + } +} +JSON + + depends_on = [azurerm_role_assignment.df_kv_secrets] +} + +resource "azurerm_data_factory_linked_service_key_vault" "default" { + name = "${var.common.env}-kv" + data_factory_id = azurerm_data_factory.default.id + key_vault_id = var.key_vault.id +} + + +resource "azurerm_data_factory_custom_dataset" "rest_default" { + name = "${var.common.env}-rest" + data_factory_id = azurerm_data_factory.default.id + type = "RestResource" + + linked_service { + name = azurerm_data_factory_linked_custom_service.rest_default.name + } + + type_properties_json = <<JSON +{ + "relativeUrl": "${var.secrets["${var.common.env}-API-URL"].value}" +} +JSON + + description = "test description" +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/data_factory/v1/~inputs.tf b/.github/actions/terraform-templates/azure/modules/data_factory/v1/~inputs.tf new file mode 100644 index 00000000000..50e95c75e4b --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/data_factory/v1/~inputs.tf @@ -0,0 +1,4 @@ +variable "common" {} +variable "storage_account" {} +variable "key_vault" {} +variable "secrets" {} diff --git a/.github/actions/terraform-templates/azure/modules/data_factory/v1/~outputs.tf b/.github/actions/terraform-templates/azure/modules/data_factory/v1/~outputs.tf new file mode 100644 index 00000000000..e69de29bb2d diff --git a/.github/actions/terraform-templates/azure/modules/data_factory/v2/main.tf b/.github/actions/terraform-templates/azure/modules/data_factory/v2/main.tf new file mode 100644 index 00000000000..6bee82a323b --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/data_factory/v2/main.tf @@ -0,0 +1,17 @@ +resource "azurerm_data_factory" "default" { + name = "df-${var.common.uid}-${var.common.env}-${var.key}" + location = var.common.location + resource_group_name = var.common.resource_group.name + + identity { + type = "SystemAssigned" + } +} + +resource "azurerm_role_assignment" "default" { + for_each = var.roles + + scope = var.common.resource_group.id + role_definition_name = each.value + principal_id = azurerm_data_factory.default.identity[0].principal_id +} diff --git a/.github/actions/terraform-templates/azure/modules/data_factory/v2/~inputs.tf b/.github/actions/terraform-templates/azure/modules/data_factory/v2/~inputs.tf new file mode 100644 index 00000000000..4fedd304f9c --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/data_factory/v2/~inputs.tf @@ -0,0 +1,3 @@ +variable "common" {} +variable "roles" {} +variable "key" {} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/data_factory/v2/~outputs.tf b/.github/actions/terraform-templates/azure/modules/data_factory/v2/~outputs.tf new file mode 100644 index 00000000000..e69de29bb2d diff --git a/.github/actions/terraform-templates/azure/modules/dns_zone/main.tf b/.github/actions/terraform-templates/azure/modules/dns_zone/main.tf new file mode 100644 index 00000000000..c254ac97465 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/dns_zone/main.tf @@ -0,0 +1,14 @@ +resource "azurerm_private_dns_zone" "default" { + name = var.dns_name + resource_group_name = var.common.resource_group.name +} + +resource "azurerm_private_dns_zone_virtual_network_link" "default" { + for_each = var.dns_links + + name = "vnet-link-${each.value.vnet_key}-${var.key}" + resource_group_name = var.common.resource_group.name + private_dns_zone_name = azurerm_private_dns_zone.default.name + virtual_network_id = lookup(var.vnets, "vnet-${each.value.vnet_key}", "") + registration_enabled = each.value.registration_enabled +} diff --git a/.github/actions/terraform-templates/azure/modules/dns_zone/~inputs.tf b/.github/actions/terraform-templates/azure/modules/dns_zone/~inputs.tf new file mode 100644 index 00000000000..9740cc56f82 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/dns_zone/~inputs.tf @@ -0,0 +1,7 @@ +variable "common" {} +variable "key" {} +variable "dns_name" {} +variable "dns_links" {} +variable "vnets" { + //default = "" +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/dns_zone/~outputs.tf b/.github/actions/terraform-templates/azure/modules/dns_zone/~outputs.tf new file mode 100644 index 00000000000..d4d8f388958 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/dns_zone/~outputs.tf @@ -0,0 +1,3 @@ +output "meta" { + value = azurerm_private_dns_zone.default +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/init/v1/main.tf b/.github/actions/terraform-templates/azure/modules/init/v1/main.tf new file mode 100644 index 00000000000..5edb227566f --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/init/v1/main.tf @@ -0,0 +1,18 @@ +resource "azurerm_role_assignment" "dev_roles" { + for_each = toset(var.dev_roles) + + scope = var.common.resource_group.id + role_definition_name = each.value + principal_id = var.common.owner.object_id +} + +resource "azuread_group" "sqladmins" { + display_name = "sqladmins-${var.common.env}" + owners = [var.common.owner.object_id] + security_enabled = true + + members = [ + var.common.owner.object_id, + /* more users */ + ] +} diff --git a/.github/actions/terraform-templates/azure/modules/init/v1/~inputs.tf b/.github/actions/terraform-templates/azure/modules/init/v1/~inputs.tf new file mode 100644 index 00000000000..49d79042088 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/init/v1/~inputs.tf @@ -0,0 +1,2 @@ +variable "common" {} +variable "dev_roles" {} diff --git a/.github/actions/terraform-templates/azure/modules/init/v1/~outputs.tf b/.github/actions/terraform-templates/azure/modules/init/v1/~outputs.tf new file mode 100644 index 00000000000..a59c8b34b56 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/init/v1/~outputs.tf @@ -0,0 +1,3 @@ +output "sqladmins" { + value = azuread_group.sqladmins +} diff --git a/.github/actions/terraform-templates/azure/modules/init/v2/main.tf b/.github/actions/terraform-templates/azure/modules/init/v2/main.tf new file mode 100644 index 00000000000..6db44742e3c --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/init/v2/main.tf @@ -0,0 +1,7 @@ +resource "azurerm_role_assignment" "dev_roles" { + for_each = toset(var.dev_roles) + + scope = var.common.resource_group.id + role_definition_name = each.value + principal_id = var.common.owner.object_id +} diff --git a/.github/actions/terraform-templates/azure/modules/init/v2/~inputs.tf b/.github/actions/terraform-templates/azure/modules/init/v2/~inputs.tf new file mode 100644 index 00000000000..49d79042088 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/init/v2/~inputs.tf @@ -0,0 +1,2 @@ +variable "common" {} +variable "dev_roles" {} diff --git a/.github/actions/terraform-templates/azure/modules/init/v2/~outputs.tf b/.github/actions/terraform-templates/azure/modules/init/v2/~outputs.tf new file mode 100644 index 00000000000..e69de29bb2d diff --git a/.github/actions/terraform-templates/azure/modules/key_vault/main.tf b/.github/actions/terraform-templates/azure/modules/key_vault/main.tf new file mode 100644 index 00000000000..bdc7d9cd964 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/key_vault/main.tf @@ -0,0 +1,23 @@ +resource "azurerm_key_vault" "default" { + name = "kv-${var.common.uid}-${var.common.env}-${var.key}" + location = var.common.location + resource_group_name = var.common.resource_group.name + tenant_id = var.common.tenant_id + sku_name = "standard" + soft_delete_retention_days = 7 + purge_protection_enabled = false + enable_rbac_authorization = true +} + +resource "azurerm_key_vault_secret" "default" { + for_each = var.secrets + + name = each.key + value = each.value.value + key_vault_id = azurerm_key_vault.default.id + + lifecycle { + ignore_changes = [value] + } +} + diff --git a/.github/actions/terraform-templates/azure/modules/key_vault/~inputs.tf b/.github/actions/terraform-templates/azure/modules/key_vault/~inputs.tf new file mode 100644 index 00000000000..451440d1c74 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/key_vault/~inputs.tf @@ -0,0 +1,3 @@ +variable "common" {} +variable "secrets" {} +variable "key" {} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/key_vault/~outputs.tf b/.github/actions/terraform-templates/azure/modules/key_vault/~outputs.tf new file mode 100644 index 00000000000..07ab666933a --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/key_vault/~outputs.tf @@ -0,0 +1,7 @@ +output "meta" { + value = azurerm_key_vault.default +} + +output "secrets" { + value = azurerm_key_vault_secret.default +} diff --git a/.github/actions/terraform-templates/azure/modules/logic_app/main.tf b/.github/actions/terraform-templates/azure/modules/logic_app/main.tf new file mode 100644 index 00000000000..06a73c9fb1a --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/logic_app/main.tf @@ -0,0 +1,110 @@ +resource "azurerm_logic_app_workflow" "default" { + name = "la-${var.common.env}" + location = var.common.location + resource_group_name = var.common.resource_group.name + + identity { + type = "SystemAssigned" + } + + lifecycle { + ignore_changes = [parameters, workflow_parameters] + } +} + +data "azapi_resource" "logicapp_resource" { + depends_on = [azurerm_logic_app_workflow.default, azurerm_resource_group_template_deployment.la_db_workflow] + name = azurerm_logic_app_workflow.default.name + parent_id = var.common.resource_group.id + type = "Microsoft.Logic/workflows@2019-05-01" + + response_export_values = ["*"] +} + +resource "azurerm_logic_app_trigger_http_request" "default" { + name = "${var.common.env}-trigger" + logic_app_id = azurerm_logic_app_workflow.default.id + method = "POST" + + schema = <<SCHEMA +{ + "properties": { + "name": { + "type": "string" + } + }, + "type": "object" +} +SCHEMA + + depends_on = [azurerm_resource_group_template_deployment.la_db_workflow] + lifecycle { + replace_triggered_by = [azurerm_resource_group_template_deployment.la_db_workflow] + } +} + +data "template_file" "la_db_workflow" { + template = file("${path.module}/workflows/${var.workflow_file}") +} + +resource "azurerm_resource_group_template_deployment" "la_db_workflow" { + depends_on = [azurerm_logic_app_workflow.default] + + name = "la-${var.common.env}-workflow" + resource_group_name = var.common.resource_group.name + deployment_mode = "Incremental" + parameters_content = jsonencode({ + "workflows_la_name" = { + value = azurerm_logic_app_workflow.default.name + } + "location" = { + value = var.common.location + } + "sql_api_id" = { + value = data.azurerm_managed_api.sql.id + } + "sql_conn_id" = { + value = azapi_resource.sql.id + } + "sql_conn_name" = { + value = azapi_resource.sql.name + } + "sql_server_fqdn" = { + value = var.sql_server_fqdn + } + "db_name" = { + value = var.db_name + } + "query" = { + value = "SELECT * FROM sys.objects" + } + }) + + template_content = data.template_file.la_db_workflow.template +} + +data "azurerm_managed_api" "sql" { + name = "sql" + location = var.common.location +} + +resource "azapi_resource" "sql" { + type = "Microsoft.Web/connections@2016-06-01" + name = "sql-la-db" + parent_id = var.common.resource_group.id + location = var.common.location + + body = jsonencode({ + properties = { + api = { + id = data.azurerm_managed_api.sql.id + } + displayName = "sql-la-db" + parameterValueSet = { + name = "oauthMI" + values = {} + } + } + }) + schema_validation_enabled = false +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/logic_app/workflows/workflow.json b/.github/actions/terraform-templates/azure/modules/logic_app/workflows/workflow.json new file mode 100644 index 00000000000..1ad66cf6eef --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/logic_app/workflows/workflow.json @@ -0,0 +1,114 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "workflows_la_name": { + "type": "String" + }, + "location":{ + "type": "String" + }, + "sql_api_id":{ + "type": "String" + }, + "sql_conn_id":{ + "type": "String" + }, + "sql_conn_name":{ + "type": "String" + }, + "sql_server_fqdn":{ + "type": "String" + }, + "db_name":{ + "type": "String" + }, + "query":{ + "type": "String" + } + }, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2017-07-01", + "name": "[parameters('workflows_la_name')]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "actions": { + "Execute_a_SQL_query_(V2)": { + "inputs": { + "body": { + "query": "@parameters('sql_server')['query']" + }, + "host": { + "connection": { + "name": "@parameters('$connections')['sql']['connectionId']" + } + }, + "method": "post", + "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('sql_server')['fqdn']))},@{encodeURIComponent(encodeURIComponent(parameters('sql_server')['db']))}/query/sql" + }, + "runAfter": {}, + "type": "ApiConnection" + }, + "Response": { + "inputs": { + "body": "@body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1']", + "statusCode": 200 + }, + "kind": "Http", + "runAfter": { + "Execute_a_SQL_query_(V2)": [ + "Succeeded" + ] + }, + "type": "Response" + } + }, + "contentVersion": "1.0.0.0", + "parameters": { + "$connections": { + "defaultValue": {}, + "type": "Object" + }, + "sql_server": { + "defaultValue": {}, + "type": "Object" + } + }, + "triggers": {} + }, + "parameters": { + "$connections": { + "value": { + "sql": { + "connectionId": "[parameters('sql_conn_id')]", + "connectionName": "[parameters('sql_conn_name')]", + "connectionProperties": { + "authentication": { + "type": "ManagedServiceIdentity" + } + }, + "id": "[parameters('sql_api_id')]" + } + } + }, + "sql_server" : { + "value": { + "fqdn": "[parameters('sql_server_fqdn')]", + "db": "[parameters('db_name')]", + "query": "[parameters('query')]" + } + } + } + } + } + ] +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/logic_app/~inputs.tf b/.github/actions/terraform-templates/azure/modules/logic_app/~inputs.tf new file mode 100644 index 00000000000..1bcccfe1a15 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/logic_app/~inputs.tf @@ -0,0 +1,5 @@ +variable "common" {} +variable "storage_account" {} +variable "workflow_file" {} +variable "sql_server_fqdn" {} +variable "db_name" {} diff --git a/.github/actions/terraform-templates/azure/modules/logic_app/~outputs.tf b/.github/actions/terraform-templates/azure/modules/logic_app/~outputs.tf new file mode 100644 index 00000000000..43a8a35f2e4 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/logic_app/~outputs.tf @@ -0,0 +1,16 @@ +output "ids" { + value = azurerm_logic_app_workflow.default.identity[0] +} + +output "name" { + value = azurerm_logic_app_workflow.default.name +} + +output "meta" { + value = azurerm_logic_app_workflow.default +} + +output "endpoint" { + value = azurerm_logic_app_trigger_http_request.default.callback_url + sensitive = true +} diff --git a/.github/actions/terraform-templates/azure/modules/logic_app/~providers.tf b/.github/actions/terraform-templates/azure/modules/logic_app/~providers.tf new file mode 100644 index 00000000000..bb8af2ed8a1 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/logic_app/~providers.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + azapi = { + source = "Azure/azapi" + version = "1.12.1" + } + } +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/mssql_vm/main.tf b/.github/actions/terraform-templates/azure/modules/mssql_vm/main.tf new file mode 100644 index 00000000000..79d46143ebd --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/mssql_vm/main.tf @@ -0,0 +1,254 @@ +data "http" "icanhazip" { + url = "http://icanhazip.com" +} + +locals { + public_ip = chomp(data.http.icanhazip.response_body) +} + +resource "azurerm_virtual_network" "example" { + name = "${var.key}-VN" + address_space = ["10.1.0.0/16"] + location = var.common.location + resource_group_name = var.common.resource_group.name + + dns_servers = var.ad_dns_ips +} + +resource "azurerm_network_security_group" "example" { + name = "${var.key}-NSG" + location = var.common.location + resource_group_name = var.common.resource_group.name +} + +resource "azurerm_subnet" "example" { + name = "${var.key}-SN" + resource_group_name = var.common.resource_group.name + virtual_network_name = azurerm_virtual_network.example.name + address_prefixes = ["10.1.0.0/24"] +} + +/*resource "azurerm_subnet_network_security_group_association" "example" { + subnet_id = azurerm_subnet.example.id + network_security_group_id = azurerm_network_security_group.example.id +}*/ + +resource "azurerm_public_ip" "vm" { + for_each = toset([ "vm1", "vm2" ]) + + name = "${var.key}-${each.key}-PIP" + location = var.common.location + resource_group_name = var.common.resource_group.name + allocation_method = "Dynamic" +} + +resource "azurerm_public_ip" "lb" { + name = "${var.key}-lb-PIP" + location = var.common.location + resource_group_name = var.common.resource_group.name + allocation_method = "Dynamic" +} + +resource "azurerm_lb" "example" { + name = "TestLoadBalancer" + location = var.common.location + resource_group_name = var.common.resource_group.name + + frontend_ip_configuration { + name = "PublicIPAddress" + public_ip_address_id = azurerm_public_ip.lb.id + } +} + +resource "azurerm_network_security_rule" "RDPRule" { + name = "RDPRule" + resource_group_name = var.common.resource_group.name + priority = 1000 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = 3389 + source_address_prefix = "${local.public_ip}" + destination_address_prefix = "*" + network_security_group_name = azurerm_network_security_group.example.name +} + +resource "azurerm_network_security_rule" "MSSQLRule" { + name = "MSSQLRule" + resource_group_name = var.common.resource_group.name + priority = 1001 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = 1433 + source_address_prefix = "${local.public_ip}" + destination_address_prefix = "*" + network_security_group_name = azurerm_network_security_group.example.name +} + +resource "azurerm_network_interface" "example" { + for_each = toset([ "vm1", "vm2" ]) + + name = "${var.key}-${each.key}-NIC" + location = var.common.location + resource_group_name = var.common.resource_group.name + + ip_configuration { + name = "exampleconfiguration1" + subnet_id = azurerm_subnet.example.id + private_ip_address_allocation = "Dynamic" + public_ip_address_id = azurerm_public_ip.vm[each.key].id + } +} + +resource "azurerm_network_interface_security_group_association" "example" { + for_each = toset([ "vm1", "vm2" ]) + + network_interface_id = azurerm_network_interface.example[each.key].id + network_security_group_id = azurerm_network_security_group.example.id +} + +resource "azurerm_windows_virtual_machine" "example" { + for_each = toset([ "vm1", "vm2" ]) + + name = "${var.key}-${each.key}-VM" + location = var.common.location + resource_group_name = var.common.resource_group.name + network_interface_ids = [azurerm_network_interface.example[each.key].id] + size = "Standard_B2s" + provision_vm_agent = true + enable_automatic_updates = true + + admin_username = "exampleadmin" + admin_password = "Password1234!" + + source_image_reference { + publisher = "MicrosoftSQLServer" + offer = "SQL2022-WS2022" #"SQL2017-WS2016" + sku = "sqldev-gen2" #"SQLDEV" + version = "latest" + } + + os_disk { + name = "${var.key}-${each.key}-OSDisk" + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + +} + +// Waits for up to 1 hour for the Domain to become available. Will return an error 1 if unsuccessful preventing the member attempting to join. +// todo - find out why this is so variable? (approx 40min during testing) + +resource "azurerm_virtual_machine_extension" "wait-for-domain-to-provision" { + for_each = toset([ "vm1", "vm2" ]) + + name = "TestConnectionDomain" + publisher = "Microsoft.Compute" + type = "CustomScriptExtension" + type_handler_version = "1.9" + virtual_machine_id = azurerm_windows_virtual_machine.example[each.key].id + settings = <<SETTINGS + { + "commandToExecute": "powershell.exe -Command \"while (!(Test-Connection -ComputerName ${var.domain_name} -Count 1 -Quiet) -and ($retryCount++ -le 360)) { Start-Sleep 10 } \"" + } +SETTINGS +} + +resource "azurerm_virtual_machine_extension" "join-domain" { + for_each = toset([ "vm1", "vm2" ]) + + name = azurerm_windows_virtual_machine.example[each.key].name + publisher = "Microsoft.Compute" + type = "JsonADDomainExtension" + type_handler_version = "1.3" + virtual_machine_id = azurerm_windows_virtual_machine.example[each.key].id + + settings = <<SETTINGS + { + "Name": "${var.domain_name}", + "OUPath": "", + "User": "${var.ad_username}@${var.domain_name}", + "Restart": "true", + "Options": "3" + } +SETTINGS + + protected_settings = <<SETTINGS + { + "Password": "${var.ad_password}" + } +SETTINGS + + depends_on = [azurerm_virtual_machine_extension.wait-for-domain-to-provision] +} + +resource "azurerm_mssql_virtual_machine_group" "example" { + name = "examplegroup" + resource_group_name = var.common.resource_group.name + location = var.common.location + + sql_image_offer = "SQL2022-WS2022" #"SQL2017-WS2016" + sql_image_sku = "Developer" + + wsfc_domain_profile { + fqdn = var.domain_name + cluster_bootstrap_account_name = "install@${var.domain_name}" + cluster_operator_account_name = "install@${var.domain_name}" + sql_service_account_name = "sqlsvc@${var.domain_name}" + cluster_subnet_type = "SingleSubnet" + } +} + +resource "azurerm_mssql_virtual_machine" "example" { + for_each = toset([ "vm1", "vm2" ]) + + virtual_machine_id = azurerm_windows_virtual_machine.example[each.key].id + sql_license_type = "PAYG" + sql_virtual_machine_group_id = azurerm_mssql_virtual_machine_group.example.id + + wsfc_domain_credential { + cluster_bootstrap_account_password = "P@ssw0rd1234!" + cluster_operator_account_password = "P@ssw0rd1234!" + sql_service_account_password = "P@ssw0rd1234!" + } + + depends_on = [ azurerm_virtual_machine_extension.join-domain ] +} + +resource "azurerm_mssql_virtual_machine_availability_group_listener" "example" { + name = "listener1" + availability_group_name = "availabilitygroup1" + port = 1433 + sql_virtual_machine_group_id = azurerm_mssql_virtual_machine_group.example.id + + load_balancer_configuration { + load_balancer_id = azurerm_lb.example.id + private_ip_address = "10.1.0.11" + probe_port = 51572 + subnet_id = azurerm_subnet.example.id + + sql_virtual_machine_ids = [ + azurerm_mssql_virtual_machine.example["vm1"].id, + azurerm_mssql_virtual_machine.example["vm2"].id + ] + } + + replica { + sql_virtual_machine_id = azurerm_mssql_virtual_machine.example["vm1"].id + role = "Primary" + commit = "Synchronous_Commit" + failover_mode = "Automatic" + readable_secondary = "All" + } + + replica { + sql_virtual_machine_id = azurerm_mssql_virtual_machine.example["vm2"].id + role = "Secondary" + commit = "Asynchronous_Commit" + failover_mode = "Manual" + readable_secondary = "No" + } +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/mssql_vm/~inputs.tf b/.github/actions/terraform-templates/azure/modules/mssql_vm/~inputs.tf new file mode 100644 index 00000000000..df9516d9ec1 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/mssql_vm/~inputs.tf @@ -0,0 +1,12 @@ +variable "common" {} +variable "key" {} +variable "ad_dns_ips" { + default = [] +} +variable "domain_name" {} +variable "ad_username" { + sensitive = true +} +variable "ad_password" { + sensitive = true +} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/private_endpoint/main.tf b/.github/actions/terraform-templates/azure/modules/private_endpoint/main.tf new file mode 100644 index 00000000000..ed8d57322e4 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/private_endpoint/main.tf @@ -0,0 +1,18 @@ +resource "azurerm_private_endpoint" "default" { + name = "pe-${var.key}" + location = var.common.location + resource_group_name = var.common.resource_group.name + subnet_id = var.subnet_id + + private_dns_zone_group { + name = "pe-${var.key}-dns-group" + private_dns_zone_ids = var.dns_zone_ids + } + + private_service_connection { + name = "pe-${var.key}-sc" + private_connection_resource_id = var.resource_id + subresource_names = var.subresource_names + is_manual_connection = var.is_manual_connection + } +} diff --git a/.github/actions/terraform-templates/azure/modules/private_endpoint/~inputs.tf b/.github/actions/terraform-templates/azure/modules/private_endpoint/~inputs.tf new file mode 100644 index 00000000000..a175d0a48e3 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/private_endpoint/~inputs.tf @@ -0,0 +1,7 @@ +variable "common" {} +variable "key" {} +variable "dns_zone_ids" {} +variable "resource_id" {} +variable "subresource_names" {} +variable "is_manual_connection" {} +variable "subnet_id" {} diff --git a/.github/actions/terraform-templates/azure/modules/private_endpoint/~outputs.tf b/.github/actions/terraform-templates/azure/modules/private_endpoint/~outputs.tf new file mode 100644 index 00000000000..e69de29bb2d diff --git a/.github/actions/terraform-templates/azure/modules/storage_account/v1/main.tf b/.github/actions/terraform-templates/azure/modules/storage_account/v1/main.tf new file mode 100644 index 00000000000..638422a982f --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/storage_account/v1/main.tf @@ -0,0 +1,26 @@ +resource "azurerm_storage_account" "default" { + name = "sa${var.common.env}${var.common.uid}data" + location = var.common.location + resource_group_name = var.common.resource_group.name + account_tier = "Standard" + account_replication_type = "LRS" + public_network_access_enabled = true + is_hns_enabled = true + allow_nested_items_to_be_public = false + + identity { + type = "SystemAssigned" + } +} + +resource "azurerm_storage_container" "default" { + name = "container-${var.common.env}" + storage_account_name = azurerm_storage_account.default.name + container_access_type = "private" +} + +resource "azurerm_role_assignment" "la_azure_db" { + scope = azurerm_storage_account.default.id + role_definition_name = "Storage Blob Data Contributor" + principal_id = var.logic_app.principal_id +} diff --git a/.github/actions/terraform-templates/azure/modules/storage_account/v1/~inputs.tf b/.github/actions/terraform-templates/azure/modules/storage_account/v1/~inputs.tf new file mode 100644 index 00000000000..cc82b60e6f5 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/storage_account/v1/~inputs.tf @@ -0,0 +1,2 @@ +variable "common" {} +variable "logic_app" {} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/storage_account/v1/~outputs.tf b/.github/actions/terraform-templates/azure/modules/storage_account/v1/~outputs.tf new file mode 100644 index 00000000000..f2c3c043105 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/storage_account/v1/~outputs.tf @@ -0,0 +1,3 @@ +output "meta" { + value = azurerm_storage_account.default +} diff --git a/.github/actions/terraform-templates/azure/modules/storage_account/v2/main.tf b/.github/actions/terraform-templates/azure/modules/storage_account/v2/main.tf new file mode 100644 index 00000000000..38b853678ef --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/storage_account/v2/main.tf @@ -0,0 +1,15 @@ +resource "azurerm_storage_account" "default" { + name = "sa${var.common.env}${var.common.uid}${var.key}" + location = var.common.location + resource_group_name = var.common.resource_group.name + account_tier = var.account_tier + account_replication_type = "LRS" + public_network_access_enabled = true + is_hns_enabled = false + allow_nested_items_to_be_public = false + account_kind = var.account_kind + + identity { + type = "SystemAssigned" + } +} diff --git a/.github/actions/terraform-templates/azure/modules/storage_account/v2/~inputs.tf b/.github/actions/terraform-templates/azure/modules/storage_account/v2/~inputs.tf new file mode 100644 index 00000000000..2582fbde820 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/storage_account/v2/~inputs.tf @@ -0,0 +1,8 @@ +variable "common" {} +variable "key" {} +variable "account_tier" { + default = "Standard" +} +variable "account_kind" { + default = "StorageV2" +} diff --git a/.github/actions/terraform-templates/azure/modules/storage_account/v2/~outputs.tf b/.github/actions/terraform-templates/azure/modules/storage_account/v2/~outputs.tf new file mode 100644 index 00000000000..f2c3c043105 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/storage_account/v2/~outputs.tf @@ -0,0 +1,3 @@ +output "meta" { + value = azurerm_storage_account.default +} diff --git a/.github/actions/terraform-templates/azure/modules/vnet/main.tf b/.github/actions/terraform-templates/azure/modules/vnet/main.tf new file mode 100644 index 00000000000..c46b0fdc87c --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/vnet/main.tf @@ -0,0 +1,22 @@ +resource "azurerm_virtual_network" "default" { + name = "vnet-${var.key}" + location = var.common.location + resource_group_name = var.common.resource_group.name + address_space = var.address_space.value +} + +resource "azurerm_subnet" "default" { + for_each = var.subnets + + name = each.key + resource_group_name = var.common.resource_group.name + virtual_network_name = azurerm_virtual_network.default.name + address_prefixes = each.value.address_prefixes.value + + private_link_service_network_policies_enabled = each.value.link_service_policies + private_endpoint_network_policies_enabled = each.value.endpoint_policies + + lifecycle { + ignore_changes = [service_endpoints] + } +} diff --git a/.github/actions/terraform-templates/azure/modules/vnet/~inputs.tf b/.github/actions/terraform-templates/azure/modules/vnet/~inputs.tf new file mode 100644 index 00000000000..5968f000891 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/vnet/~inputs.tf @@ -0,0 +1,4 @@ +variable "common" {} +variable "key" {} +variable "address_space" {} +variable "subnets" {} diff --git a/.github/actions/terraform-templates/azure/modules/vnet/~outputs.tf b/.github/actions/terraform-templates/azure/modules/vnet/~outputs.tf new file mode 100644 index 00000000000..eee7523ff33 --- /dev/null +++ b/.github/actions/terraform-templates/azure/modules/vnet/~outputs.tf @@ -0,0 +1,11 @@ +output "name" { + value = azurerm_virtual_network.default.name +} + +output "meta" { + value = azurerm_virtual_network.default +} + +output "subnets" { + value = azurerm_subnet.default +} From 5bd7df75e0b47004665a1cb3a379e8e51faded82 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Wed, 9 Oct 2024 17:09:39 -0700 Subject: [PATCH 22/32] Importing JosiahSiegel's git-secrets GitHub Action --- .github/actions/git-secrets/.gitattributes | 10 + .../.github/PULL_REQUEST_TEMPLATE.md | 6 + .../git-secrets/.pre-commit-hooks.yaml | 5 + .github/actions/git-secrets/.travis.yml | 8 + .github/actions/git-secrets/CHANGELOG.md | 49 + .../actions/git-secrets/CODE_OF_CONDUCT.md | 4 + .github/actions/git-secrets/CONTRIBUTING.md | 61 ++ .github/actions/git-secrets/LICENSE.txt | 208 +++++ .github/actions/git-secrets/Makefile | 25 + .github/actions/git-secrets/NOTICE.txt | 6 + .github/actions/git-secrets/README.rst | 565 ++++++++++++ .github/actions/git-secrets/git-secrets | 409 +++++++++ .github/actions/git-secrets/git-secrets.1 | 843 ++++++++++++++++++ .github/actions/git-secrets/install.ps1 | 48 + .github/actions/git-secrets/test/bats/LICENSE | 20 + .../actions/git-secrets/test/bats/bin/bats | 142 +++ .../git-secrets/test/bats/libexec/bats | 142 +++ .../test/bats/libexec/bats-exec-suite | 55 ++ .../test/bats/libexec/bats-exec-test | 346 +++++++ .../test/bats/libexec/bats-format-tap-stream | 165 ++++ .../test/bats/libexec/bats-preprocess | 52 ++ .../actions/git-secrets/test/commit-msg.bats | 18 + .../actions/git-secrets/test/git-secrets.bats | 361 ++++++++ .../actions/git-secrets/test/pre-commit.bats | 62 ++ .../git-secrets/test/prepare-commit-msg.bats | 33 + .../actions/git-secrets/test/test_helper.bash | 94 ++ 26 files changed, 3737 insertions(+) create mode 100644 .github/actions/git-secrets/.gitattributes create mode 100644 .github/actions/git-secrets/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/actions/git-secrets/.pre-commit-hooks.yaml create mode 100644 .github/actions/git-secrets/.travis.yml create mode 100644 .github/actions/git-secrets/CHANGELOG.md create mode 100644 .github/actions/git-secrets/CODE_OF_CONDUCT.md create mode 100644 .github/actions/git-secrets/CONTRIBUTING.md create mode 100644 .github/actions/git-secrets/LICENSE.txt create mode 100644 .github/actions/git-secrets/Makefile create mode 100644 .github/actions/git-secrets/NOTICE.txt create mode 100644 .github/actions/git-secrets/README.rst create mode 100755 .github/actions/git-secrets/git-secrets create mode 100644 .github/actions/git-secrets/git-secrets.1 create mode 100644 .github/actions/git-secrets/install.ps1 create mode 100644 .github/actions/git-secrets/test/bats/LICENSE create mode 100755 .github/actions/git-secrets/test/bats/bin/bats create mode 100755 .github/actions/git-secrets/test/bats/libexec/bats create mode 100755 .github/actions/git-secrets/test/bats/libexec/bats-exec-suite create mode 100755 .github/actions/git-secrets/test/bats/libexec/bats-exec-test create mode 100755 .github/actions/git-secrets/test/bats/libexec/bats-format-tap-stream create mode 100755 .github/actions/git-secrets/test/bats/libexec/bats-preprocess create mode 100644 .github/actions/git-secrets/test/commit-msg.bats create mode 100644 .github/actions/git-secrets/test/git-secrets.bats create mode 100644 .github/actions/git-secrets/test/pre-commit.bats create mode 100644 .github/actions/git-secrets/test/prepare-commit-msg.bats create mode 100644 .github/actions/git-secrets/test/test_helper.bash diff --git a/.github/actions/git-secrets/.gitattributes b/.github/actions/git-secrets/.gitattributes new file mode 100644 index 00000000000..107aad84b10 --- /dev/null +++ b/.github/actions/git-secrets/.gitattributes @@ -0,0 +1,10 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Force the bash scripts to be checked out with LF line endings. +git-secrets text eol=lf +git-secrets.1 text eol=lf +test/bats/bin/* text eol=lf +test/bats/libexec/* text eol=lf +*.bats text eol=lf +*.bash text eol=lf \ No newline at end of file diff --git a/.github/actions/git-secrets/.github/PULL_REQUEST_TEMPLATE.md b/.github/actions/git-secrets/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..ab40d21d778 --- /dev/null +++ b/.github/actions/git-secrets/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,6 @@ +*Issue #, if available:* + +*Description of changes:* + + +By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. diff --git a/.github/actions/git-secrets/.pre-commit-hooks.yaml b/.github/actions/git-secrets/.pre-commit-hooks.yaml new file mode 100644 index 00000000000..d313836246f --- /dev/null +++ b/.github/actions/git-secrets/.pre-commit-hooks.yaml @@ -0,0 +1,5 @@ +- id: git-secrets + name: Git Secrets + description: git-secrets scans commits, commit messages, and --no-ff merges to prevent adding secrets into your git repositories. + entry: 'git-secrets --pre_commit_hook' + language: script diff --git a/.github/actions/git-secrets/.travis.yml b/.github/actions/git-secrets/.travis.yml new file mode 100644 index 00000000000..259379892db --- /dev/null +++ b/.github/actions/git-secrets/.travis.yml @@ -0,0 +1,8 @@ +language: bash + +before_install: +- git config --global user.email "you@example.com" +- git config --global user.name "Your Name" + +script: +- make test diff --git a/.github/actions/git-secrets/CHANGELOG.md b/.github/actions/git-secrets/CHANGELOG.md new file mode 100644 index 00000000000..cfcae4e818c --- /dev/null +++ b/.github/actions/git-secrets/CHANGELOG.md @@ -0,0 +1,49 @@ +# CHANGELOG + +## 1.3.0 - 2019-02-10 + +* Empty provider output is now excluded + (https://github.com/awslabs/git-secrets/issues/34) +* Spaces are now supported in git exec path, making more Windows + paths execute properly. +* Patterns with newlines and carriage returns are now loaded properly. +* Patterns that contain only "\n" are now ignored. +* Various Bash 4 fixes (https://github.com/awslabs/git-secrets/issues/66). +* Make IAM key scanning much more targeted. + +## 1.2.1 - 2016-06-27 + +* Fixed an issue where secret provider commands were causing "command not + found" errors due to a previously set IFS variable. + https://github.com/awslabs/git-secrets/pull/30 + +## 1.2.0 - 2016-05-23 + +* Fixed an issue where spaces files with spaces in their names were not being + properly scanned in the pre-commit hook. +* Now ignoring empty lines and comments (e.g., `#`) in the .gitallowed file. +* Fixed an issue where numbers were being compared to strings causing failures + on some platforms. + +## 1.1.0 - 2016-04-06 + +* Bug fix: the pre-commit hook previously only scanned the working directory + rather than staged files. This release updates the pre-commit hook to instead + scan staged files so that git-secrets will detect violations if the working + directory drifts from the staging directory. +* Added the `--scan-history` subcommand so that you can scan your entire + git history for violations. +* Added the ability to filter false positives by using a .gitallowed file. +* Added support for `--cached`, `--no-index`, and `--untracked` to the `--scan` + subcommand. + +## 1.0.1 - 2016-01-11 + +* Now works correctly with filenames in a repository that contain spaces when + executing `git secrets --scan` with no provided filename (via `git grep`). +* Now works with git repositories with hundreds of thousands of files when + using `git secrets --scan` with no provided filename (via `git grep`). + +## 1.0.0 - 2015-12-10 + +* Initial release of ``git-secrets``. diff --git a/.github/actions/git-secrets/CODE_OF_CONDUCT.md b/.github/actions/git-secrets/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..3b64466870c --- /dev/null +++ b/.github/actions/git-secrets/CODE_OF_CONDUCT.md @@ -0,0 +1,4 @@ +## Code of Conduct +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. diff --git a/.github/actions/git-secrets/CONTRIBUTING.md b/.github/actions/git-secrets/CONTRIBUTING.md new file mode 100644 index 00000000000..de6d3d38075 --- /dev/null +++ b/.github/actions/git-secrets/CONTRIBUTING.md @@ -0,0 +1,61 @@ +# Contributing Guidelines + +Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional +documentation, we greatly value feedback and contributions from our community. + +Please read through this document before submitting any issues or pull requests to ensure we have all the necessary +information to effectively respond to your bug report or contribution. + + +## Reporting Bugs/Feature Requests + +We welcome you to use the GitHub issue tracker to report bugs or suggest features. + +When filing an issue, please check [existing open](https://github.com/awslabs/git-secrets/issues), or [recently closed](https://github.com/awslabs/git-secrets/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already +reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: + +* A reproducible test case or series of steps +* The version of our code being used +* Any modifications you've made relevant to the bug +* Anything unusual about your environment or deployment + + +## Contributing via Pull Requests +Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: + +1. You are working against the latest source on the *master* branch. +2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. +3. You open an issue to discuss any significant work - we would hate for your time to be wasted. + +To send us a pull request, please: + +1. Fork the repository. +2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. +3. Ensure local tests pass. +4. Commit to your fork using clear commit messages. +5. Send us a pull request, answering any default questions in the pull request interface. +6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. + +GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and +[creating a pull request](https://help.github.com/articles/creating-a-pull-request/). + + +## Finding contributions to work on +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/git-secrets/labels/help%20wanted) issues is a great place to start. + + +## Code of Conduct +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. + + +## Security issue notifications +If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. + + +## Licensing + +See the [LICENSE](https://github.com/awslabs/git-secrets/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. + +We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. diff --git a/.github/actions/git-secrets/LICENSE.txt b/.github/actions/git-secrets/LICENSE.txt new file mode 100644 index 00000000000..de96b9473c9 --- /dev/null +++ b/.github/actions/git-secrets/LICENSE.txt @@ -0,0 +1,208 @@ + + 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 [yyyy] [name of copyright owner] + + 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. + +Note: Other license terms may apply to certain, identified software files +contained within or distributed with the accompanying software if such terms +are included in the directory containing the accompanying software. Such other +license terms will then apply in lieu of the terms of the software license +above. diff --git a/.github/actions/git-secrets/Makefile b/.github/actions/git-secrets/Makefile new file mode 100644 index 00000000000..a67eee2dd23 --- /dev/null +++ b/.github/actions/git-secrets/Makefile @@ -0,0 +1,25 @@ +PREFIX ?= /usr/local +MANPREFIX ?= "${PREFIX}/share/man/man1" + +help: + @echo "Please use \`make <target>' where <target> is one of" + @echo " test to perform unit tests." + @echo " man to build the man file from README.rst" + @echo " install to install. Use PREFIX and MANPREFIX to customize." + +# We use bats for testing: https://github.com/sstephenson/bats +test: + LANG=C test/bats/bin/bats test/ + +# The man page is completely derived from README.rst. Edits to +# README.rst require a rebuild of the man page. +man: + rst2man.py README.rst > git-secrets.1 + +install: + @mkdir -p ${DESTDIR}${MANPREFIX} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f git-secrets ${DESTDIR}${PREFIX}/bin + @cp -f git-secrets.1 ${DESTDIR}${MANPREFIX} + +.PHONY: help test man diff --git a/.github/actions/git-secrets/NOTICE.txt b/.github/actions/git-secrets/NOTICE.txt new file mode 100644 index 00000000000..a5e5da9ba01 --- /dev/null +++ b/.github/actions/git-secrets/NOTICE.txt @@ -0,0 +1,6 @@ +git-secrets +Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +bats +This product bundles bats, which is available under a "MIT" license. +For details, see test/bats. diff --git a/.github/actions/git-secrets/README.rst b/.github/actions/git-secrets/README.rst new file mode 100644 index 00000000000..1be1691ab8a --- /dev/null +++ b/.github/actions/git-secrets/README.rst @@ -0,0 +1,565 @@ +=========== +git-secrets +=========== + +------------------------------------------------------------------------------------------- +Prevent committing AWS , AZURE and GCP sensitive creds to a git repository. +------------------------------------------------------------------------------------------- + +.. contents:: :depth: 2 + +Synopsis +-------- + +:: + + git secrets --scan [-r|--recursive] [--cached] [--no-index] [--untracked] [<files>...] + git secrets --scan-history + git secrets --install [-f|--force] [<target-directory>] + git secrets --list [--global] + git secrets --add [-a|--allowed] [-l|--literal] [--global] <pattern> + git secrets --add-provider [--global] <command> [arguments...] + git secrets --register-aws [--global] + git secrets --register-gcp [--global] + git secrets --register-azure [--global] + git secrets --aws-provider [<credentials-file>] + + +Description +----------- + +``git-secrets`` scans commits, commit messages, and ``--no-ff`` merges to +prevent adding secrets into your git repositories. If a commit, +commit message, or any commit in a ``--no-ff`` merge history matches one of +your configured prohibited regular expression patterns, then the commit is +rejected. + + +Installing git-secrets +---------------------- + +``git-secrets`` must be placed somewhere in your PATH so that it is picked up +by ``git`` when running ``git secrets``. + +\*nix (Linux/macOS) +~~~~~~~~~~~~~~~~~~~ + +You can use the ``install`` target of the provided Makefile to install ``git secrets`` and the man page. +You can customize the install path using the PREFIX and MANPREFIX variables. + +:: + + make install + +Windows +~~~~~~~ + +Run the provided ``install.ps1`` powershell script. This will copy the needed files +to an installation directory (``%USERPROFILE%/.git-secrets`` by default) and add +the directory to the current user ``PATH``. + +:: + + PS > ./install.ps1 + +Homebrew (for macOS users) +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + brew install git-secrets + +.. warning:: + + You're not done yet! You MUST install the git hooks for every repo that + you wish to use with ``git secrets --install``. + +Here's a quick example of how to ensure a git repository is scanned for secrets +on each commit:: + + cd /path/to/my/repo + git secrets --install + git secrets --register-aws + git secrets --register-azure + git secrets --register-gcp + + +Advanced configuration +---------------------- + +Add a configuration template if you want to add hooks to all repositories you +initialize or clone in the future. + +:: + + git secrets --register-(aws/azure/gcp) --global + + +Add hooks to all your local repositories. + +:: + + git secrets --install ~/.git-templates/git-secrets + git config --global init.templateDir ~/.git-templates/git-secrets + + +Add custom providers to scan for security credentials. + +:: + + git secrets --add-provider -- cat /path/to/secret/file/patterns + + +Before making public a repository +--------------------------------- + +With git-secrets is also possible to scan a repository including all revisions: + +:: + + git secrets --scan-history + + +Options +------- + +Operation Modes +~~~~~~~~~~~~~~~ + +Each of these options must appear first on the command line. + +``--install`` + Installs git hooks for a repository. Once the hooks are installed for a git + repository, commits and non-fast-forward merges for that repository will be prevented + from committing secrets. + +``--scan`` + Scans one or more files for secrets. When a file contains a secret, the + matched text from the file being scanned will be written to stdout and the + script will exit with a non-zero status. Each matched line will be written with + the name of the file that matched, a colon, the line number that matched, + a colon, and then the line of text that matched. If no files are provided, + all files returned by ``git ls-files`` are scanned. + +``--scan-history`` + Scans repository including all revisions. When a file contains a secret, the + matched text from the file being scanned will be written to stdout and the + script will exit with a non-zero status. Each matched line will be written with + the name of the file that matched, a colon, the line number that matched, + a colon, and then the line of text that matched. + +``--list`` + Lists the ``git-secrets`` configuration for the current repo or in the global + git config. + +``--add`` + Adds a prohibited or allowed pattern. + +``--add-provider`` + Registers a secret provider. Secret providers are executables that when + invoked output prohibited patterns that ``git-secrets`` should treat as + prohibited. + +``--register-aws`` + Adds common AWS patterns to the git config and ensures that keys present + in ``~/.aws/credentials`` are not found in any commit. The following + checks are added: + + - AWS Access Key IDs via ``(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}`` + - AWS Secret Access Key assignments via ":" or "=" surrounded by optional + quotes + - AWS account ID assignments via ":" or "=" surrounded by optional quotes + - Allowed patterns for example AWS keys (``AKIAIOSFODNN7EXAMPLE`` and + ``wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY``) + - Known credentials from ``~/.aws/credentials`` + + .. note:: + + While the patterns registered by this command should catch most + instances of AWS credentials, these patterns are **not** guaranteed to + catch them **all**. ``git-secrets`` should be used as an extra means of + insurance -- you still need to do your due diligence to ensure that you + do not commit credentials to a repository. + +``--register-gcp`` + Secret provider which scans files for Google Cloud Platform's (GCP's) crentials JSON files. + +``--register-azure`` + Secret provider which scans files for AZURE credentials + + +``--aws-provider`` + Secret provider that outputs credentials found in an INI file. You can + optionally provide the path to an INI file. + + +Options for ``--install`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +``-f, --force`` + Overwrites existing hooks if present. + +``<target-directory>`` + When provided, installs git hooks to the given directory. The current + directory is assumed if ``<target-directory>`` is not provided. + + If the provided ``<target-directory>`` is not in a git repository, the + directory will be created and hooks will be placed in + ``<target-directory>/hooks``. This can be useful for creating git template + directories using with ``git init --template <target-directory>``. + + You can run ``git init`` on a repository that has already been initialized. + From the `git init documentation <https://git-scm.com/docs/git-init>`_: + + From the git documentation: Running ``git init`` in an existing repository + is safe. It will not overwrite things that are already there. The + primary reason for rerunning ``git init`` is to pick up newly added + templates (or to move the repository to another place if + ``--separate-git-dir`` is given). + + The following git hooks are installed: + + 1. ``pre-commit``: Used to check if any of the files changed in the commit + use prohibited patterns. + 2. ``commit-msg``: Used to determine if a commit message contains a + prohibited patterns. + 3. ``prepare-commit-msg``: Used to determine if a merge commit will + introduce a history that contains a prohibited pattern at any point. + Please note that this hook is only invoked for non fast-forward merges. + + .. note:: + + Git only allows a single script to be executed per hook. If the + repository contains Debian-style subdirectories like ``pre-commit.d`` + and ``commit-msg.d``, then the git hooks will be installed into these + directories, which assumes that you've configured the corresponding + hooks to execute all of the scripts found in these directories. If + these git subdirectories are not present, then the git hooks will be + installed to the git repo's ``.git/hooks`` directory. + + +Examples +^^^^^^^^ + +Install git hooks to the current directory:: + + cd /path/to/my/repository + git secrets --install + +Install git hooks to a repository other than the current directory:: + + git secrets --install /path/to/my/repository + +Create a git template that has ``git-secrets`` installed, and then copy that +template into a git repository:: + + git secrets --install ~/.git-templates/git-secrets + git init --template ~/.git-templates/git-secrets + +Overwrite existing hooks if present:: + + git secrets --install -f + + +Options for ``--scan`` +~~~~~~~~~~~~~~~~~~~~~~ + +``-r, --recursive`` + Scans the given files recursively. If a directory is encountered, the + directory will be scanned. If ``-r`` is not provided, directories will be + ignored. + + ``-r`` cannot be used alongside ``--cached``, ``--no-index``, or + ``--untracked``. + +``--cached`` + Searches blobs registered in the index file. + +``--no-index`` + Searches files in the current directory that is not managed by git. + +``--untracked`` + In addition to searching in the tracked files in the working tree, + ``--scan`` also in untracked files. + +``<files>...`` + The path to one or more files on disk to scan for secrets. + + If no files are provided, all files returned by ``git ls-files`` are + scanned. + + +Examples +^^^^^^^^ + +Scan all files in the repo:: + + git secrets --scan + +Scans a single file for secrets:: + + git secrets --scan /path/to/file + +Scans a directory recursively for secrets:: + + git secrets --scan -r /path/to/directory + +Scans multiple files for secrets:: + + git secrets --scan /path/to/file /path/to/other/file + +You can scan by globbing:: + + git secrets --scan /path/to/directory/* + +Scan from stdin:: + + echo 'hello!' | git secrets --scan - + + +Options for ``--list`` +~~~~~~~~~~~~~~~~~~~~~~ + +``--global`` + Lists only git-secrets configuration in the global git config. + + +Options for ``--add`` +~~~~~~~~~~~~~~~~~~~~~ + +``--global`` + Adds patterns to the global git config + +``-l, --literal`` + Escapes special regular expression characters in the provided pattern so + that the pattern is searched for literally. + +``-a, --allowed`` + Mark the pattern as allowed instead of prohibited. Allowed patterns are + used to filter our false positives. + +``<pattern>`` + The regex pattern to search. + + +Examples +^^^^^^^^ + +Adds a prohibited pattern to the current repo:: + + git secrets --add '[A-Z0-9]{20}' + +Adds a prohibited pattern to the global git config:: + + git secrets --add --global '[A-Z0-9]{20}' + +Adds a string that is scanned for literally (``+`` is escaped):: + + git secrets --add --literal 'foo+bar' + +Add an allowed pattern:: + + git secrets --add -a 'allowed pattern' + + +Options for ``--register-aws`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``--global`` + Adds AWS specific configuration variables to the global git config. + +Options for ``--register-gcp`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``--global`` + Adds GCP specific configuration variables to the global git config. + +Options for ``--register-azure`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``--global`` + Adds AZURE specific configuration variables to the global git config. + +Options for ``--aws-provider`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``[<credentials-file>]`` + If provided, specifies the custom path to an INI file to scan. If not + provided, ``~/.aws/credentials`` is assumed. + + +Options for ``--add-provider`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``--global`` + Adds the provider to the global git config. + +``<command>`` + Provider command to invoke. When invoked the command is expected to write + prohibited patterns separated by new lines to stdout. Any extra arguments + provided are passed on to the command. + + +Examples +^^^^^^^^ + +Registers a secret provider with arguments:: + + git secrets --add-provider -- git secrets --aws-provider + +Cats secrets out of a file:: + + git secrets --add-provider -- cat /path/to/secret/file/patterns + + +Defining prohibited patterns +---------------------------- + +``egrep``-compatible regular expressions are used to determine if a commit or +commit message contains any prohibited patterns. These regular expressions are +defined using the ``git config`` command. It is important to note that +different systems use different versions of egrep. For example, when running on +macOS, you will use a different version of ``egrep`` than when running on something +like Ubuntu (BSD vs GNU). + +You can add prohibited regular expression patterns to your git config using +``git secrets --add <pattern>``. + + +Ignoring false positives +------------------------ + +Sometimes a regular expression might match false positives. For example, git +commit SHAs look a lot like AWS access keys. You can specify many different +regular expression patterns as false positives using the following command: + +:: + + git secrets --add --allowed 'my regex pattern' + +You can also add regular expressions patterns to filter false positives to a +``.gitallowed`` file located in the repository's root directory. Lines starting +with ``#`` are skipped (comment line) and empty lines are also skipped. + +First, git-secrets will extract all lines from a file that contain a prohibited +match. Included in the matched results will be the full path to the name of +the file that was matched, followed by ':', followed by the line number that was +matched, followed by the entire line from the file that was matched by a secret +pattern. Then, if you've defined allowed regular expressions, git-secrets will +check to see if all of the matched lines match at least one of your registered +allowed regular expressions. If all of the lines that were flagged as secret +are canceled out by an allowed match, then the subject text does not contain +any secrets. If any of the matched lines are not matched by an allowed regular +expression, then git-secrets will fail the commit/merge/message. + +.. important:: + + Just as it is a bad practice to add prohibited patterns that are too + greedy, it is also a bad practice to add allowed patterns that are too + forgiving. Be sure to test out your patterns using ad-hoc calls to + ``git secrets --scan $filename`` to ensure they are working as intended. + + +Secret providers +---------------- + +Sometimes you want to check for an exact pattern match against a set of known +secrets. For example, you might want to ensure that no credentials present in +``~/.aws/credentials`` ever show up in a commit. In these cases, it's better to +leave these secrets in one location rather than spread them out across git +repositories in git configs. You can use "secret providers" to fetch these +types of credentials. A secret provider is an executable that when invoked +outputs prohibited patterns separated by new lines. + +You can add secret providers using the ``--add-provider`` command:: + + git secrets --add-provider -- git secrets --aws-provider + +Notice the use of ``--``. This ensures that any arguments associated with the +provider are passed to the provider each time it is invoked when scanning +for secrets. + + +Example walkthrough +------------------- + +Let's take a look at an example. Given the following subject text (stored in +``/tmp/example``):: + + This is a test! + password=ex@mplepassword + password=****** + More test... + +And the following registered patterns: + +:: + + git secrets --add 'password\s*=\s*.+' + git secrets --add --allowed --literal 'ex@mplepassword' + +Running ``git secrets --scan /tmp/example``, the result will +result in the following error output:: + + /tmp/example:3:password=****** + + [ERROR] Matched prohibited pattern + + Possible mitigations: + - Mark false positives as allowed using: git config --add secrets.allowed ... + - List your configured patterns: git config --get-all secrets.patterns + - List your configured allowed patterns: git config --get-all secrets.allowed + - Use --no-verify if this is a one-time false positive + +Breaking this down, the prohibited pattern value of ``password\s*=\s*.+`` will +match the following lines:: + + /tmp/example:2:password=ex@mplepassword + /tmp/example:3:password=****** + +...But the first match will be filtered out due to the fact that it matches the +allowed regular expression of ``ex@mplepassword``. Because there is still a +remaining line that did not match, it is considered a secret. + +Because that matching lines are placed on lines that start with the filename +and line number (e.g., ``/tmp/example:3:...``), you can create allowed +patterns that take filenames and line numbers into account in the regular +expression. For example, you could whitelist an entire file using something +like:: + + git secrets --add --allowed '/tmp/example:.*' + git secrets --scan /tmp/example && echo $? + # Outputs: 0 + +Alternatively, you could allow a specific line number of a file if that +line is unlikely to change using something like the following: + +:: + + git secrets --add --allowed '/tmp/example:3:.*' + git secrets --scan /tmp/example && echo $? + # Outputs: 0 + +Keep this in mind when creating allowed patterns to ensure that your allowed +patterns are not inadvertently matched due to the fact that the filename is +included in the subject text that allowed patterns are matched against. + + +Skipping validation +------------------- + +Use the ``--no-verify`` option in the event of a false positive match in a +commit, merge, or commit message. This will skip the execution of the +git hook and allow you to make the commit or merge. + + +About +------ + +- Author: `Michael Dowling <https://github.com/mtdowling>`_ +- Issue tracker: This project's source code and issue tracker can be found at + `https://github.com/awslabs/git-secrets <https://github.com/awslabs/git-secrets>`_ +- Special thanks to Adrian Vatchinsky and Ari Juels of Cornell University for + providing suggestions and feedback. + +Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/.github/actions/git-secrets/git-secrets b/.github/actions/git-secrets/git-secrets new file mode 100755 index 00000000000..236d03b5cd1 --- /dev/null +++ b/.github/actions/git-secrets/git-secrets @@ -0,0 +1,409 @@ +#!/usr/bin/env bash +# Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file 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. + +NONGIT_OK=1 OPTIONS_SPEC="\ +git secrets --scan [-r|--recursive] [--cached] [--no-index] [--untracked] [<files>...] +git secrets --scan-history +git secrets --install [-f|--force] [<target-directory>] +git secrets --list [--global] +git secrets --add [-a|--allowed] [-l|--literal] [--global] <pattern> +git secrets --add-provider [--global] <command> [arguments...] +git secrets --register-aws [--global] +git secrets --register-azure [--global] +git secrets --register-gcp [--global] +git secrets --aws-provider [<credentials-file>] +-- +scan Scans <files> for prohibited patterns +scan-history Scans repo for prohibited patterns +install Installs git hooks for Git repository or Git template directory +list Lists secret patterns +add Adds a prohibited or allowed pattern, ensuring to de-dupe with existing patterns +add-provider Adds a secret provider that when called outputs secret patterns on new lines +aws-provider Secret provider that outputs credentials found in an ini file +register-aws Adds common AWS patterns to the git config and scans for ~/.aws/credentials +register-azure Adds common Azure patterns to the git config +register-gcp Adds common GCP patterns to the git config +r,recursive --scan scans directories recursively +cached --scan scans searches blobs registered in the index file +no-index --scan searches files in the current directory that is not managed by Git +untracked In addition to searching in the tracked files in the working tree, --scan also in untracked files +f,force --install overwrites hooks if the hook already exists +l,literal --add and --add-allowed patterns are escaped so that they are literal +a,allowed --add adds an allowed pattern instead of a prohibited pattern +global Uses the --global git config +commit_msg_hook* commit-msg hook (internal only) +pre_commit_hook* pre-commit hook (internal only) +prepare_commit_msg_hook* prepare-commit-msg hook (internal only)" + +# Include the git setup script. This parses and normalized CLI arguments. +. "$(git --exec-path)"/git-sh-setup + +load_patterns() { + git config --get-all secrets.patterns + # Execute each provider and use their output to build up patterns + git config --get-all secrets.providers | while read -r cmd; do + # Only split words on '\n\t ' and strip "\r" from the output to account + # for carriage returns being added on Windows systems. Note that this + # trimming is done before the test to ensure that the string is not empty. + local result="$(export IFS=$'\n\t '; $cmd | tr -d $'\r')" + # Do not add empty lines from providers as that would match everything. + if [ -n "${result}" ]; then + echo "${result}" + fi + done +} + +load_allowed() { + git config --get-all secrets.allowed + local gitallowed="$(git rev-parse --show-toplevel)/.gitallowed" + if [ -e "$gitallowed" ]; then + cat $gitallowed | awk 'NF && $1!~/^#/' + fi +} + +# load patterns and combine them with | +load_combined_patterns() { + local patterns=$(load_patterns) + local combined_patterns='' + for pattern in $patterns; do + combined_patterns=${combined_patterns}${pattern}"|" + done + combined_patterns=${combined_patterns%?} + echo $combined_patterns +} + +# Scans files or a repo using patterns. +scan() { + local files=("${@}") options="" + [ "${SCAN_CACHED}" == 1 ] && options+="--cached" + [ "${SCAN_UNTRACKED}" == 1 ] && options+=" --untracked" + [ "${SCAN_NO_INDEX}" == 1 ] && options+=" --no-index" + # Scan using git-grep if there are no files or if git options are applied. + if [ ${#files[@]} -eq 0 ] || [ ! -z "${options}" ]; then + output=$(git_grep $options "${files[@]}") + else + output=$(regular_grep "${files[@]}") + fi + process_output $? "${output}" +} + +# Scans through history using patterns +scan_history() { + # git log does not support multiple patterns, so we need to combine them + local combined_patterns=$(load_combined_patterns) + [ -z "${combined_patterns}" ] && return 0 + # Looks for differences matching the patterns, reduces the number of revisions to scan + local to_scan=$(git log --all -G"${combined_patterns}" --pretty=%H) + # Scan through revisions with findings to normalize output + output=$(GREP_OPTIONS= LC_ALL=C git grep -nwHEI "${combined_patterns}" $to_scan) + process_output $? "${output}" +} + +# Performs a git-grep, taking into account patterns and options. +# Note: this function returns 1 on success, 0 on error. +git_grep() { + local options="$1"; shift + local files=("${@}") combined_patterns=$(load_combined_patterns) + + [ -z "${combined_patterns}" ] && return 1 + GREP_OPTIONS= LC_ALL=C git grep -nwHEI ${options} "${combined_patterns}" -- "${files[@]}" +} + +# Performs a regular grep, taking into account patterns and recursion. +# Note: this function returns 1 on success, 0 on error. +regular_grep() { + local files=("${@}") patterns=$(load_patterns) action='skip' + [ -z "${patterns}" ] && return 1 + [ ${RECURSIVE} -eq 1 ] && action="recurse" + GREP_OPTIONS= LC_ALL=C grep -d "${action}" -nwHEI "${patterns}" "${files[@]}" +} + +# Process the given status ($1) and output variables ($2). +# Takes into account allowed patterns, and if a bad match is found, +# prints an error message and exits 1. +process_output() { + local status="$1" output="$2" + local allowed=$(load_allowed) + case "$status" in + 0) + [ -z "${allowed}" ] && echo "${output}" >&2 && return 1 + # Determine with a negative grep if the found matches are allowed + echo "${output}" | GREP_OPTIONS= LC_ALL=C grep -Ev "${allowed}" >&2 \ + && return 1 || return 0 + ;; + 1) return 0 ;; + *) exit $status + esac +} + +# Calls the given scanning function at $1, shifts, and passes to it $@. +# Exit 0 if success, otherwise exit 1 with error message. +scan_with_fn_or_die() { + local fn="$1"; shift + $fn "$@" && exit 0 + echo >&2 + echo "[ERROR] Matched one or more prohibited patterns" >&2 + echo >&2 + echo "Possible mitigations:" >&2 + echo "- Mark false positives as allowed using: git config --add secrets.allowed ..." >&2 + echo "- Mark false positives as allowed by adding regular expressions to .gitallowed at repository's root directory" >&2 + echo "- List your configured patterns: git config --get-all secrets.patterns" >&2 + echo "- List your configured allowed patterns: git config --get-all secrets.allowed" >&2 + echo "- List your configured allowed patterns in .gitallowed at repository's root directory" >&2 + echo "- Use --no-verify if this is a one-time false positive" >&2 + exit 1 +} + +# Scans a commit message, passed in the path to a file. +commit_msg_hook() { + scan_with_fn_or_die "scan" "$1" +} + +# Scans all files that are about to be committed. +pre_commit_hook() { + SCAN_CACHED=1 + local files=() file found_match=0 rev="4b825dc642cb6eb9a060e54bf8d69288fbee4904" + # Diff against HEAD if this is not the first commit in the repo. + git rev-parse --verify HEAD >/dev/null 2>&1 && rev="HEAD" + # Filter out deleted files using --diff-filter + while IFS= read -r file; do + [ -n "$file" ] && files+=("$file") + done <<< "$(git diff-index --diff-filter 'ACMU' --name-only --cached $rev --)" + scan_with_fn_or_die "scan" "${files[@]}" +} + +# Determines if merging in a commit will introduce tainted history. +prepare_commit_msg_hook() { + case "$2,$3" in + merge,) + local git_head=$(env | grep GITHEAD) # e.g. GITHEAD_<sha>=release/1.43 + local sha="${git_head##*=}" # Get just the SHA + local branch=$(git symbolic-ref HEAD) # e.g. refs/heads/master + local dest="${branch#refs/heads/}" # cut out "refs/heads" + git log "${dest}".."${sha}" -p | scan_with_fn_or_die "scan" - + ;; + esac +} + +install_hook() { + local path="$1" hook="$2" cmd="$3" dest + # Determines the approriate path for a hook to be installed + if [ -d "${path}/hooks/${hook}.d" ]; then + dest="${path}/hooks/${hook}.d/git-secrets" + else + dest="${path}/hooks/${hook}" + fi + [ -f "${dest}" ] && [ "${FORCE}" -ne 1 ] \ + && die "${dest} already exists. Use -f to force" + echo "#!/usr/bin/env bash" > "${dest}" + echo "git secrets --${cmd} -- \"\$@\"" >> "${dest}" + chmod +x "${dest}" + say "$(tput setaf 2)✓$(tput sgr 0) Installed ${hook} hook to ${dest}" +} + +install_all_hooks() { + install_hook "$1" "commit-msg" "commit_msg_hook" + install_hook "$1" "pre-commit" "pre_commit_hook" + install_hook "$1" "prepare-commit-msg" "prepare_commit_msg_hook" +} + +# Adds a git config pattern, ensuring to de-dupe +add_config() { + local key="$1"; shift + local value="$@" + if [ ${LITERAL} -eq 1 ]; then + value=$(sed 's/[\.|$(){}?+*^]/\\&/g' <<< "${value}") + fi + if [ ${GLOBAL} -eq 1 ]; then + git config --global --get-all $key | grep -Fq "${value}" && return 1 + git config --global --add "${key}" "${value}" + else + git config --get-all $key | grep -Fq "${value}" && return 1 + git config --add "${key}" "${value}" + fi +} + +register_aws() { + # Reusable regex patterns + local aws="(AWS|aws|Aws)?_?" quote="(\"|')" connect="\s*(:|=>|=)\s*" + local opt_quote="${quote}?" + add_config 'secrets.providers' 'git secrets --aws-provider' + add_config 'secrets.patterns' '(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}' + add_config 'secrets.patterns' "${opt_quote}${aws}(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)${opt_quote}${connect}${opt_quote}[A-Za-z0-9/\+=]{40}${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}${aws}(ACCOUNT|account|Account)_?(ID|id|Id)?${opt_quote}${connect}${opt_quote}[0-9]{4}\-?[0-9]{4}\-?[0-9]{4}${opt_quote}" + add_config 'secrets.allowed' 'AKIAIOSFODNN7EXAMPLE' + add_config 'secrets.allowed' "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + + if [[ $? == 0 ]]; then + echo 'OK' + fi + + exit $? +} + +register_azure() { + # Reusable regex patterns + local azure="(AZURE|azure|Azure)?_?" quote="(\"|')" connect="\s*(:|=>|=)\s*" + local opt_quote="${quote}" + add_config 'secrets.patterns' "${opt_quote}[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][o|O][n|N][m|M][i|I][c|C][r|R][o|O][s|S][o|O][f|F][t|T][.][c|C][o|O][m|M]${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][b|B][l|L][o|O][b|B][.][c|C][o|O][r|R][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][q|Q][u|U][e|E][u|U][e|E][.][c|C][o|O][r|R][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][t|T][a|A][b|B][l|L][e|E][.][c|C][o|O][r|R][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][d|D][a|A][t|T][a|A][b|B][a|A][s|S][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][s|S][e|E][r|R][v|V][i|I][c|C][e|E][b|B][u|U][s|S][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][t|T][i|I][m|M][e|E][s|S][e|E][r|R][i|I][e|E][s|S][.][a|A][z|Z][u|U][r|R][e|E][.][c|C][o|O][m|M]${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][a|T][c|C][c|C][e|E][s|S][s|S][c|C][o|O][n|N][t|T][r|R][o|O][l|L][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][a|A][z|Z][u|U][r|R][e|E][h|H][d|D][i|I][n|N][s|S][i|I][g|G][h|H][t|T][.][n|N][e|E][t|T]${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][c|C][l|L][o|O][u|U][d|D][a|A][p|P][p|P][.][a|A][z|Z][u|U][r|R][e|E][.][c|C][o|O][m|M]${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][c|C][l|L][o|O][u|U][d|D][a|A][p|P][p|P][.][n|N][e|E][t|T]${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][d|D][o|O][c|C][u|U][m|M][e|E][n|N][t|T][s|S][.][a|A][z|Z][u|U][r|R][e|E][.][c|C][o|O][m|M]${opt_quote}" + add_config 'secrets.patterns' "^-----BEGIN (RSA|EC|DSA|GPP) PRIVATE KEY-----$" + add_config 'secrets.patterns' "(\"|')[A-Z0-9a-z[:punct:]]{32}(\"|')$" + add_config 'secrets.patterns' "(\"|')[A-Z0-9a-z[:punct:]]{88}(\"|')$" + + + + + if [[ $? == 0 ]]; then + echo 'OK' + fi + + exit $? +} + + + + +register_gcp() { + # Reusable regex patterns + add_config 'secrets.patterns' '\bprivate_key.*\b' + + + if [[ $? == 0 ]]; then + echo 'OK' + fi + + exit $? +} + +aws_provider() { + local fi="$1" + [ -z "$fi" ] && fi=~/.aws/credentials + # Find keys and ensure that special characters are escaped. + if [ -f $fi ]; then + awk -F "=" '/aws_access_key_id|aws_secret_access_key/ {print $2}' $fi \ + | tr -d ' "' \ + | sed 's/[]\.|$(){}?+*^]/\\&/g' + fi +} + +# Ensures that the command is what was expected for an option. +assert_option_for_command() { + local expected_command="$1" + local option_name="$2" + if [ "${COMMAND}" != "${expected_command}" ]; then + die "${option_name} can only be supplied with the ${expected_command} subcommand" + fi +} + +declare COMMAND="$1" FORCE=0 RECURSIVE=0 LITERAL=0 GLOBAL=0 ALLOWED=0 +declare SCAN_CACHED=0 SCAN_NO_INDEX=0 SCAN_UNTRACKED=0 + +# Shift off the command name +shift 1 +while [ "$#" -ne 0 ]; do + case "$1" in + -f) + assert_option_for_command "--install" "-f|--force" + FORCE=1 + ;; + -r) + assert_option_for_command "--scan" "-r|--recursive" + RECURSIVE=1 + ;; + -a) + assert_option_for_command "--add" "-a|--allowed" + ALLOWED=1 + ;; + -l) + assert_option_for_command "--add" "-l|--literal" + LITERAL=1 + ;; + --cached) + assert_option_for_command "--scan" "--cached" + SCAN_CACHED=1 + ;; + --no-index) + assert_option_for_command "--scan" "--no-index" + SCAN_NO_INDEX=1 + ;; + --untracked) + assert_option_for_command "--scan" "--untracked" + SCAN_UNTRACKED=1 + ;; + --global) GLOBAL=1 ;; + --) shift; break ;; + esac + shift +done + +# Ensure that recursive is not applied with mutually exclusive options. +if [ ${RECURSIVE} -eq 1 ]; then + if [ ${SCAN_CACHED} -eq 1 ] \ + || [ ${SCAN_NO_INDEX} -eq 1 ] \ + || [ ${SCAN_UNTRACKED} -eq 1 ]; + then + die "-r|--recursive cannot be supplied with --cached, --no-index, or --untracked" + fi +fi + +case "${COMMAND}" in + -h|--help|--) "$0" -h; exit 0 ;; + --add-provider) add_config "secrets.providers" "$@" ;; + --register-aws) register_aws ;; + --register-azure) register_azure ;; + --register-gcp) register_gcp ;; + --aws-provider) aws_provider "$1" ;; + --commit_msg_hook|--pre_commit_hook|--prepare_commit_msg_hook) + ${COMMAND:2} "$@" + ;; + --add) + if [ ${ALLOWED} -eq 1 ]; then + add_config "secrets.allowed" "$1" + else + add_config "secrets.patterns" "$1" + fi + ;; + --scan) scan_with_fn_or_die "scan" "$@" ;; + --scan-history) scan_with_fn_or_die "scan_history" "$@" ;; + --list) + if [ ${GLOBAL} -eq 1 ]; then + git config --global --get-regex secrets.* + else + git config --get-regex secrets.* + fi + ;; + --install) + DIRECTORY="$1" + if [ -z "${DIRECTORY}" ]; then + DIRECTORY=$(git rev-parse --git-dir) || die "Not in a Git repository" + elif [ -d "${DIRECTORY}"/.git ]; then + DIRECTORY="${DIRECTORY}/.git" + fi + mkdir -p "${DIRECTORY}/hooks" || die "Could not create dir: ${DIRECTORY}" + install_all_hooks "${DIRECTORY}" + ;; + *) echo "Unknown option: ${COMMAND}" && "$0" -h ;; +esac diff --git a/.github/actions/git-secrets/git-secrets.1 b/.github/actions/git-secrets/git-secrets.1 new file mode 100644 index 00000000000..1c6d25c3413 --- /dev/null +++ b/.github/actions/git-secrets/git-secrets.1 @@ -0,0 +1,843 @@ +.\" Man page generated from reStructuredText. +. +.TH GIT-SECRETS "" "" "" +.SH NAME +git-secrets \- Prevents you from committing passwords and other sensitive information to a git repository. +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SS Contents +.INDENT 0.0 +.IP \(bu 2 +\fI\%Synopsis\fP +.IP \(bu 2 +\fI\%Description\fP +.IP \(bu 2 +\fI\%Installing git\-secrets\fP +.INDENT 2.0 +.IP \(bu 2 +\fI\%*nix (Linux/macOS)\fP +.IP \(bu 2 +\fI\%Windows\fP +.IP \(bu 2 +\fI\%Homebrew (for macOS users)\fP +.UNINDENT +.IP \(bu 2 +\fI\%Advanced configuration\fP +.IP \(bu 2 +\fI\%Before making public a repository\fP +.IP \(bu 2 +\fI\%Options\fP +.INDENT 2.0 +.IP \(bu 2 +\fI\%Operation Modes\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-install\fP\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-scan\fP\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-list\fP\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-add\fP\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-register\-aws\fP\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-aws\-provider\fP\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-add\-provider\fP\fP +.UNINDENT +.IP \(bu 2 +\fI\%Defining prohibited patterns\fP +.IP \(bu 2 +\fI\%Ignoring false positives\fP +.IP \(bu 2 +\fI\%Secret providers\fP +.IP \(bu 2 +\fI\%Example walkthrough\fP +.IP \(bu 2 +\fI\%Skipping validation\fP +.IP \(bu 2 +\fI\%About\fP +.UNINDENT +.SH SYNOPSIS +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan [\-r|\-\-recursive] [\-\-cached] [\-\-no\-index] [\-\-untracked] [<files>...] +git secrets \-\-scan\-history +git secrets \-\-install [\-f|\-\-force] [<target\-directory>] +git secrets \-\-list [\-\-global] +git secrets \-\-add [\-a|\-\-allowed] [\-l|\-\-literal] [\-\-global] <pattern> +git secrets \-\-add\-provider [\-\-global] <command> [arguments...] +git secrets \-\-register\-aws [\-\-global] +git secrets \-\-aws\-provider [<credentials\-file>] +.ft P +.fi +.UNINDENT +.UNINDENT +.SH DESCRIPTION +.sp +\fBgit\-secrets\fP scans commits, commit messages, and \fB\-\-no\-ff\fP merges to +prevent adding secrets into your git repositories. If a commit, +commit message, or any commit in a \fB\-\-no\-ff\fP merge history matches one of +your configured prohibited regular expression patterns, then the commit is +rejected. +.SH INSTALLING GIT-SECRETS +.sp +\fBgit\-secrets\fP must be placed somewhere in your PATH so that it is picked up +by \fBgit\fP when running \fBgit secrets\fP\&. +.SS *nix (Linux/macOS) +.IP "System Message: WARNING/2 (README.rst:, line 43)" +Title underline too short. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +\e*nix (Linux/macOS) +~~~~~~~~~~~~~~~~~ +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +You can use the \fBinstall\fP target of the provided Makefile to install \fBgit secrets\fP and the man page. +You can customize the install path using the PREFIX and MANPREFIX variables. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +make install +.ft P +.fi +.UNINDENT +.UNINDENT +.SS Windows +.sp +Run the provided \fBinstall.ps1\fP powershell script. This will copy the needed files +to an installation directory (\fB%USERPROFILE%/.git\-secrets\fP by default) and add +the directory to the current user \fBPATH\fP\&. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +PS > ./install.ps1 +.ft P +.fi +.UNINDENT +.UNINDENT +.SS Homebrew (for macOS users) +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +brew install git\-secrets +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +\fBWARNING:\fP +.INDENT 0.0 +.INDENT 3.5 +You\(aqre not done yet! You MUST install the git hooks for every repo that +you wish to use with \fBgit secrets \-\-install\fP\&. +.UNINDENT +.UNINDENT +.sp +Here\(aqs a quick example of how to ensure a git repository is scanned for secrets +on each commit: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +cd /path/to/my/repo +git secrets \-\-install +git secrets \-\-register\-aws +.ft P +.fi +.UNINDENT +.UNINDENT +.SH ADVANCED CONFIGURATION +.sp +Add a configuration template if you want to add hooks to all repositories you +initialize or clone in the future. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-register\-aws \-\-global +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Add hooks to all your local repositories. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-install ~/.git\-templates/git\-secrets +git config \-\-global init.templateDir ~/.git\-templates/git\-secrets +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Add custom providers to scan for security credentials. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add\-provider \-\- cat /path/to/secret/file/patterns +.ft P +.fi +.UNINDENT +.UNINDENT +.SH BEFORE MAKING PUBLIC A REPOSITORY +.sp +With git\-secrets is also possible to scan a repository including all revisions: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan\-history +.ft P +.fi +.UNINDENT +.UNINDENT +.SH OPTIONS +.SS Operation Modes +.sp +Each of these options must appear first on the command line. +.INDENT 0.0 +.TP +.B \fB\-\-install\fP +Installs git hooks for a repository. Once the hooks are installed for a git +repository, commits and non\-fast\-forward merges for that repository will be prevented +from committing secrets. +.TP +.B \fB\-\-scan\fP +Scans one or more files for secrets. When a file contains a secret, the +matched text from the file being scanned will be written to stdout and the +script will exit with a non\-zero status. Each matched line will be written with +the name of the file that matched, a colon, the line number that matched, +a colon, and then the line of text that matched. If no files are provided, +all files returned by \fBgit ls\-files\fP are scanned. +.TP +.B \fB\-\-scan\-history\fP +Scans repository including all revisions. When a file contains a secret, the +matched text from the file being scanned will be written to stdout and the +script will exit with a non\-zero status. Each matched line will be written with +the name of the file that matched, a colon, the line number that matched, +a colon, and then the line of text that matched. +.TP +.B \fB\-\-list\fP +Lists the \fBgit\-secrets\fP configuration for the current repo or in the global +git config. +.TP +.B \fB\-\-add\fP +Adds a prohibited or allowed pattern. +.TP +.B \fB\-\-add\-provider\fP +Registers a secret provider. Secret providers are executables that when +invoked output prohibited patterns that \fBgit\-secrets\fP should treat as +prohibited. +.TP +.B \fB\-\-register\-aws\fP +Adds common AWS patterns to the git config and ensures that keys present +in \fB~/.aws/credentials\fP are not found in any commit. The following +checks are added: +.INDENT 7.0 +.IP \(bu 2 +AWS Access Key IDs via \fB(A3T[A\-Z0\-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A\-Z0\-9]{16}\fP +.IP \(bu 2 +AWS Secret Access Key assignments via ":" or "=" surrounded by optional +quotes +.IP \(bu 2 +AWS account ID assignments via ":" or "=" surrounded by optional quotes +.IP \(bu 2 +Allowed patterns for example AWS keys (\fBAKIAIOSFODNN7EXAMPLE\fP and +\fBwJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\fP) +.IP \(bu 2 +Known credentials from \fB~/.aws/credentials\fP +.UNINDENT +.sp +\fBNOTE:\fP +.INDENT 7.0 +.INDENT 3.5 +While the patterns registered by this command should catch most +instances of AWS credentials, these patterns are \fBnot\fP guaranteed to +catch them \fBall\fP\&. \fBgit\-secrets\fP should be used as an extra means of +insurance \-\- you still need to do your due diligence to ensure that you +do not commit credentials to a repository. +.UNINDENT +.UNINDENT +.TP +.B \fB\-\-aws\-provider\fP +Secret provider that outputs credentials found in an INI file. You can +optionally provide the path to an INI file. +.UNINDENT +.SS Options for \fB\-\-install\fP +.INDENT 0.0 +.TP +.B \fB\-f, \-\-force\fP +Overwrites existing hooks if present. +.TP +.B \fB<target\-directory>\fP +When provided, installs git hooks to the given directory. The current +directory is assumed if \fB<target\-directory>\fP is not provided. +.sp +If the provided \fB<target\-directory>\fP is not in a git repository, the +directory will be created and hooks will be placed in +\fB<target\-directory>/hooks\fP\&. This can be useful for creating git template +directories using with \fBgit init \-\-template <target\-directory>\fP\&. +.sp +You can run \fBgit init\fP on a repository that has already been initialized. +From the \fI\%git init documentation\fP: +.INDENT 7.0 +.INDENT 3.5 +From the git documentation: Running \fBgit init\fP in an existing repository +is safe. It will not overwrite things that are already there. The +primary reason for rerunning \fBgit init\fP is to pick up newly added +templates (or to move the repository to another place if +\fB\-\-separate\-git\-dir\fP is given). +.UNINDENT +.UNINDENT +.sp +The following git hooks are installed: +.INDENT 7.0 +.IP 1. 3 +\fBpre\-commit\fP: Used to check if any of the files changed in the commit +use prohibited patterns. +.IP 2. 3 +\fBcommit\-msg\fP: Used to determine if a commit message contains a +prohibited patterns. +.IP 3. 3 +\fBprepare\-commit\-msg\fP: Used to determine if a merge commit will +introduce a history that contains a prohibited pattern at any point. +Please note that this hook is only invoked for non fast\-forward merges. +.UNINDENT +.sp +\fBNOTE:\fP +.INDENT 7.0 +.INDENT 3.5 +Git only allows a single script to be executed per hook. If the +repository contains Debian\-style subdirectories like \fBpre\-commit.d\fP +and \fBcommit\-msg.d\fP, then the git hooks will be installed into these +directories, which assumes that you\(aqve configured the corresponding +hooks to execute all of the scripts found in these directories. If +these git subdirectories are not present, then the git hooks will be +installed to the git repo\(aqs \fB\&.git/hooks\fP directory. +.UNINDENT +.UNINDENT +.UNINDENT +.SS Examples +.sp +Install git hooks to the current directory: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +cd /path/to/my/repository +git secrets \-\-install +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Install git hooks to a repository other than the current directory: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-install /path/to/my/repository +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Create a git template that has \fBgit\-secrets\fP installed, and then copy that +template into a git repository: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-install ~/.git\-templates/git\-secrets +git init \-\-template ~/.git\-templates/git\-secrets +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Overwrite existing hooks if present: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-install \-f +.ft P +.fi +.UNINDENT +.UNINDENT +.SS Options for \fB\-\-scan\fP +.INDENT 0.0 +.TP +.B \fB\-r, \-\-recursive\fP +Scans the given files recursively. If a directory is encountered, the +directory will be scanned. If \fB\-r\fP is not provided, directories will be +ignored. +.sp +\fB\-r\fP cannot be used alongside \fB\-\-cached\fP, \fB\-\-no\-index\fP, or +\fB\-\-untracked\fP\&. +.TP +.B \fB\-\-cached\fP +Searches blobs registered in the index file. +.TP +.B \fB\-\-no\-index\fP +Searches files in the current directory that is not managed by git. +.TP +.B \fB\-\-untracked\fP +In addition to searching in the tracked files in the working tree, +\fB\-\-scan\fP also in untracked files. +.TP +.B \fB<files>...\fP +The path to one or more files on disk to scan for secrets. +.sp +If no files are provided, all files returned by \fBgit ls\-files\fP are +scanned. +.UNINDENT +.SS Examples +.sp +Scan all files in the repo: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Scans a single file for secrets: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan /path/to/file +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Scans a directory recursively for secrets: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan \-r /path/to/directory +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Scans multiple files for secrets: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan /path/to/file /path/to/other/file +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +You can scan by globbing: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan /path/to/directory/* +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Scan from stdin: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +echo \(aqhello!\(aq | git secrets \-\-scan \- +.ft P +.fi +.UNINDENT +.UNINDENT +.SS Options for \fB\-\-list\fP +.INDENT 0.0 +.TP +.B \fB\-\-global\fP +Lists only git\-secrets configuration in the global git config. +.UNINDENT +.SS Options for \fB\-\-add\fP +.INDENT 0.0 +.TP +.B \fB\-\-global\fP +Adds patterns to the global git config +.TP +.B \fB\-l, \-\-literal\fP +Escapes special regular expression characters in the provided pattern so +that the pattern is searched for literally. +.TP +.B \fB\-a, \-\-allowed\fP +Mark the pattern as allowed instead of prohibited. Allowed patterns are +used to filter our false positives. +.TP +.B \fB<pattern>\fP +The regex pattern to search. +.UNINDENT +.SS Examples +.sp +Adds a prohibited pattern to the current repo: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \(aq[A\-Z0\-9]{20}\(aq +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Adds a prohibited pattern to the global git config: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \-\-global \(aq[A\-Z0\-9]{20}\(aq +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Adds a string that is scanned for literally (\fB+\fP is escaped): +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \-\-literal \(aqfoo+bar\(aq +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Add an allowed pattern: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \-a \(aqallowed pattern\(aq +.ft P +.fi +.UNINDENT +.UNINDENT +.SS Options for \fB\-\-register\-aws\fP +.INDENT 0.0 +.TP +.B \fB\-\-global\fP +Adds AWS specific configuration variables to the global git config. +.UNINDENT +.SS Options for \fB\-\-aws\-provider\fP +.INDENT 0.0 +.TP +.B \fB[<credentials\-file>]\fP +If provided, specifies the custom path to an INI file to scan. If not +provided, \fB~/.aws/credentials\fP is assumed. +.UNINDENT +.SS Options for \fB\-\-add\-provider\fP +.INDENT 0.0 +.TP +.B \fB\-\-global\fP +Adds the provider to the global git config. +.TP +.B \fB<command>\fP +Provider command to invoke. When invoked the command is expected to write +prohibited patterns separated by new lines to stdout. Any extra arguments +provided are passed on to the command. +.UNINDENT +.SS Examples +.sp +Registers a secret provider with arguments: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add\-provider \-\- git secrets \-\-aws\-provider +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Cats secrets out of a file: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add\-provider \-\- cat /path/to/secret/file/patterns +.ft P +.fi +.UNINDENT +.UNINDENT +.SH DEFINING PROHIBITED PATTERNS +.sp +\fBegrep\fP\-compatible regular expressions are used to determine if a commit or +commit message contains any prohibited patterns. These regular expressions are +defined using the \fBgit config\fP command. It is important to note that +different systems use different versions of egrep. For example, when running on +macOS, you will use a different version of \fBegrep\fP than when running on something +like Ubuntu (BSD vs GNU). +.sp +You can add prohibited regular expression patterns to your git config using +\fBgit secrets \-\-add <pattern>\fP\&. +.SH IGNORING FALSE POSITIVES +.sp +Sometimes a regular expression might match false positives. For example, git +commit SHAs look a lot like AWS access keys. You can specify many different +regular expression patterns as false positives using the following command: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \-\-allowed \(aqmy regex pattern\(aq +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +You can also add regular expressions patterns to filter false positives to a +\fB\&.gitallowed\fP file located in the repository\(aqs root directory. Lines starting +with \fB#\fP are skipped (comment line) and empty lines are also skipped. +.sp +First, git\-secrets will extract all lines from a file that contain a prohibited +match. Included in the matched results will be the full path to the name of +the file that was matched, followed by \(aq:\(aq, followed by the line number that was +matched, followed by the entire line from the file that was matched by a secret +pattern. Then, if you\(aqve defined allowed regular expressions, git\-secrets will +check to see if all of the matched lines match at least one of your registered +allowed regular expressions. If all of the lines that were flagged as secret +are canceled out by an allowed match, then the subject text does not contain +any secrets. If any of the matched lines are not matched by an allowed regular +expression, then git\-secrets will fail the commit/merge/message. +.sp +\fBIMPORTANT:\fP +.INDENT 0.0 +.INDENT 3.5 +Just as it is a bad practice to add prohibited patterns that are too +greedy, it is also a bad practice to add allowed patterns that are too +forgiving. Be sure to test out your patterns using ad\-hoc calls to +\fBgit secrets \-\-scan $filename\fP to ensure they are working as intended. +.UNINDENT +.UNINDENT +.SH SECRET PROVIDERS +.sp +Sometimes you want to check for an exact pattern match against a set of known +secrets. For example, you might want to ensure that no credentials present in +\fB~/.aws/credentials\fP ever show up in a commit. In these cases, it\(aqs better to +leave these secrets in one location rather than spread them out across git +repositories in git configs. You can use "secret providers" to fetch these +types of credentials. A secret provider is an executable that when invoked +outputs prohibited patterns separated by new lines. +.sp +You can add secret providers using the \fB\-\-add\-provider\fP command: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add\-provider \-\- git secrets \-\-aws\-provider +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Notice the use of \fB\-\-\fP\&. This ensures that any arguments associated with the +provider are passed to the provider each time it is invoked when scanning +for secrets. +.SH EXAMPLE WALKTHROUGH +.sp +Let\(aqs take a look at an example. Given the following subject text (stored in +\fB/tmp/example\fP): +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +This is a test! +password=ex@mplepassword +password=****** +More test... +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +And the following registered patterns: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \(aqpassword\es*=\es*.+\(aq +git secrets \-\-add \-\-allowed \-\-literal \(aqex@mplepassword\(aq +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Running \fBgit secrets \-\-scan /tmp/example\fP, the result will +result in the following error output: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +/tmp/example:3:password=****** + +[ERROR] Matched prohibited pattern + +Possible mitigations: +\- Mark false positives as allowed using: git config \-\-add secrets.allowed ... +\- List your configured patterns: git config \-\-get\-all secrets.patterns +\- List your configured allowed patterns: git config \-\-get\-all secrets.allowed +\- Use \-\-no\-verify if this is a one\-time false positive +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Breaking this down, the prohibited pattern value of \fBpassword\es*=\es*.+\fP will +match the following lines: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +/tmp/example:2:password=ex@mplepassword +/tmp/example:3:password=****** +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +\&...But the first match will be filtered out due to the fact that it matches the +allowed regular expression of \fBex@mplepassword\fP\&. Because there is still a +remaining line that did not match, it is considered a secret. +.sp +Because that matching lines are placed on lines that start with the filename +and line number (e.g., \fB/tmp/example:3:...\fP), you can create allowed +patterns that take filenames and line numbers into account in the regular +expression. For example, you could whitelist an entire file using something +like: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \-\-allowed \(aq/tmp/example:.*\(aq +git secrets \-\-scan /tmp/example && echo $? +# Outputs: 0 +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Alternatively, you could allow a specific line number of a file if that +line is unlikely to change using something like the following: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \-\-allowed \(aq/tmp/example:3:.*\(aq +git secrets \-\-scan /tmp/example && echo $? +# Outputs: 0 +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Keep this in mind when creating allowed patterns to ensure that your allowed +patterns are not inadvertently matched due to the fact that the filename is +included in the subject text that allowed patterns are matched against. +.SH SKIPPING VALIDATION +.sp +Use the \fB\-\-no\-verify\fP option in the event of a false positive match in a +commit, merge, or commit message. This will skip the execution of the +git hook and allow you to make the commit or merge. +.SH ABOUT +.INDENT 0.0 +.IP \(bu 2 +Author: \fI\%Michael Dowling\fP +.IP \(bu 2 +Issue tracker: This project\(aqs source code and issue tracker can be found at +\fI\%https://github.com/awslabs/git\-secrets\fP +.IP \(bu 2 +Special thanks to Adrian Vatchinsky and Ari Juels of Cornell University for +providing suggestions and feedback. +.UNINDENT +.sp +Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +.\" Generated by docutils manpage writer. +. diff --git a/.github/actions/git-secrets/install.ps1 b/.github/actions/git-secrets/install.ps1 new file mode 100644 index 00000000000..fbffbfa9906 --- /dev/null +++ b/.github/actions/git-secrets/install.ps1 @@ -0,0 +1,48 @@ +Param([string]$InstallationDirectory = $($Env:USERPROFILE + "\.git-secrets")) + +Write-Host "Checking to see if installation directory already exists..." +if (-not (Test-Path $InstallationDirectory)) +{ + Write-Host "Creating installation directory." + New-Item -ItemType Directory -Path $InstallationDirectory | Out-Null +} +else +{ + Write-Host "Installation directory already exists." +} + +Write-Host "Copying files." +Copy-Item ./git-secrets -Destination $InstallationDirectory -Force +Copy-Item ./git-secrets.1 -Destination $InstallationDirectory -Force + +Write-Host "Checking if directory already exists in Path..." +$currentPath = [Environment]::GetEnvironmentVariable("PATH", "User") +if ($currentPath -notlike "*$InstallationDirectory*") +{ + Write-Host "Adding to path." + $newPath = $currentPath + if(-not ($newPath.EndsWith(";"))) + { + $newPath = $newPath + ";" + } + $newPath = $newPath + $InstallationDirectory + [Environment]::SetEnvironmentVariable("PATH", $newPath, "User") +} +else +{ + Write-Host "Already in Path." +} + +# Adding to Session +Write-Host "Adding to user session." +$currentSessionPath = $Env:Path +if ($currentSessionPath -notlike "*$InstallationDirectory*") +{ + if(-not ($currentSessionPath.EndsWith(";"))) + { + $currentSessionPath = $currentSessionPath + ";" + } + $Env:Path = $currentSessionPath + $InstallationDirectory +} + +Write-Host "Done." \ No newline at end of file diff --git a/.github/actions/git-secrets/test/bats/LICENSE b/.github/actions/git-secrets/test/bats/LICENSE new file mode 100644 index 00000000000..bac4eb29ccf --- /dev/null +++ b/.github/actions/git-secrets/test/bats/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2014 Sam Stephenson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.github/actions/git-secrets/test/bats/bin/bats b/.github/actions/git-secrets/test/bats/bin/bats new file mode 100755 index 00000000000..71f392f757e --- /dev/null +++ b/.github/actions/git-secrets/test/bats/bin/bats @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +set -e + +version() { + echo "Bats 0.4.0" +} + +usage() { + version + echo "Usage: bats [-c] [-p | -t] <test> [<test> ...]" +} + +help() { + usage + echo + echo " <test> is the path to a Bats test file, or the path to a directory" + echo " containing Bats test files." + echo + echo " -c, --count Count the number of test cases without running any tests" + echo " -h, --help Display this help message" + echo " -p, --pretty Show results in pretty format (default for terminals)" + echo " -t, --tap Show results in TAP format" + echo " -v, --version Display the version number" + echo + echo " For more information, see https://github.com/sstephenson/bats" + echo +} + +resolve_link() { + $(type -p greadlink readlink | head -1) "$1" +} + +abs_dirname() { + local cwd="$(pwd)" + local path="$1" + + while [ -n "$path" ]; do + cd "${path%/*}" + local name="${path##*/}" + path="$(resolve_link "$name" || true)" + done + + pwd + cd "$cwd" +} + +expand_path() { + { cd "$(dirname "$1")" 2>/dev/null + local dirname="$PWD" + cd "$OLDPWD" + echo "$dirname/$(basename "$1")" + } || echo "$1" +} + +BATS_LIBEXEC="$(abs_dirname "$0")" +export BATS_PREFIX="$(abs_dirname "$BATS_LIBEXEC")" +export BATS_CWD="$(abs_dirname .)" +export PATH="$BATS_LIBEXEC:$PATH" + +options=() +arguments=() +for arg in "$@"; do + if [ "${arg:0:1}" = "-" ]; then + if [ "${arg:1:1}" = "-" ]; then + options[${#options[*]}]="${arg:2}" + else + index=1 + while option="${arg:$index:1}"; do + [ -n "$option" ] || break + options[${#options[*]}]="$option" + let index+=1 + done + fi + else + arguments[${#arguments[*]}]="$arg" + fi +done + +unset count_flag pretty +[ -t 0 ] && [ -t 1 ] && pretty="1" +[ -n "$CI" ] && pretty="" + +for option in "${options[@]}"; do + case "$option" in + "h" | "help" ) + help + exit 0 + ;; + "v" | "version" ) + version + exit 0 + ;; + "c" | "count" ) + count_flag="-c" + ;; + "t" | "tap" ) + pretty="" + ;; + "p" | "pretty" ) + pretty="1" + ;; + * ) + usage >&2 + exit 1 + ;; + esac +done + +if [ "${#arguments[@]}" -eq 0 ]; then + usage >&2 + exit 1 +fi + +filenames=() +for filename in "${arguments[@]}"; do + if [ -d "$filename" ]; then + shopt -s nullglob + for suite_filename in "$(expand_path "$filename")"/*.bats; do + filenames["${#filenames[@]}"]="$suite_filename" + done + shopt -u nullglob + else + filenames["${#filenames[@]}"]="$(expand_path "$filename")" + fi +done + +if [ "${#filenames[@]}" -eq 1 ]; then + command="bats-exec-test" +else + command="bats-exec-suite" +fi + +if [ -n "$pretty" ]; then + extended_syntax_flag="-x" + formatter="bats-format-tap-stream" +else + extended_syntax_flag="" + formatter="cat" +fi + +set -o pipefail execfail +exec "$command" $count_flag $extended_syntax_flag "${filenames[@]}" | "$formatter" diff --git a/.github/actions/git-secrets/test/bats/libexec/bats b/.github/actions/git-secrets/test/bats/libexec/bats new file mode 100755 index 00000000000..71f392f757e --- /dev/null +++ b/.github/actions/git-secrets/test/bats/libexec/bats @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +set -e + +version() { + echo "Bats 0.4.0" +} + +usage() { + version + echo "Usage: bats [-c] [-p | -t] <test> [<test> ...]" +} + +help() { + usage + echo + echo " <test> is the path to a Bats test file, or the path to a directory" + echo " containing Bats test files." + echo + echo " -c, --count Count the number of test cases without running any tests" + echo " -h, --help Display this help message" + echo " -p, --pretty Show results in pretty format (default for terminals)" + echo " -t, --tap Show results in TAP format" + echo " -v, --version Display the version number" + echo + echo " For more information, see https://github.com/sstephenson/bats" + echo +} + +resolve_link() { + $(type -p greadlink readlink | head -1) "$1" +} + +abs_dirname() { + local cwd="$(pwd)" + local path="$1" + + while [ -n "$path" ]; do + cd "${path%/*}" + local name="${path##*/}" + path="$(resolve_link "$name" || true)" + done + + pwd + cd "$cwd" +} + +expand_path() { + { cd "$(dirname "$1")" 2>/dev/null + local dirname="$PWD" + cd "$OLDPWD" + echo "$dirname/$(basename "$1")" + } || echo "$1" +} + +BATS_LIBEXEC="$(abs_dirname "$0")" +export BATS_PREFIX="$(abs_dirname "$BATS_LIBEXEC")" +export BATS_CWD="$(abs_dirname .)" +export PATH="$BATS_LIBEXEC:$PATH" + +options=() +arguments=() +for arg in "$@"; do + if [ "${arg:0:1}" = "-" ]; then + if [ "${arg:1:1}" = "-" ]; then + options[${#options[*]}]="${arg:2}" + else + index=1 + while option="${arg:$index:1}"; do + [ -n "$option" ] || break + options[${#options[*]}]="$option" + let index+=1 + done + fi + else + arguments[${#arguments[*]}]="$arg" + fi +done + +unset count_flag pretty +[ -t 0 ] && [ -t 1 ] && pretty="1" +[ -n "$CI" ] && pretty="" + +for option in "${options[@]}"; do + case "$option" in + "h" | "help" ) + help + exit 0 + ;; + "v" | "version" ) + version + exit 0 + ;; + "c" | "count" ) + count_flag="-c" + ;; + "t" | "tap" ) + pretty="" + ;; + "p" | "pretty" ) + pretty="1" + ;; + * ) + usage >&2 + exit 1 + ;; + esac +done + +if [ "${#arguments[@]}" -eq 0 ]; then + usage >&2 + exit 1 +fi + +filenames=() +for filename in "${arguments[@]}"; do + if [ -d "$filename" ]; then + shopt -s nullglob + for suite_filename in "$(expand_path "$filename")"/*.bats; do + filenames["${#filenames[@]}"]="$suite_filename" + done + shopt -u nullglob + else + filenames["${#filenames[@]}"]="$(expand_path "$filename")" + fi +done + +if [ "${#filenames[@]}" -eq 1 ]; then + command="bats-exec-test" +else + command="bats-exec-suite" +fi + +if [ -n "$pretty" ]; then + extended_syntax_flag="-x" + formatter="bats-format-tap-stream" +else + extended_syntax_flag="" + formatter="cat" +fi + +set -o pipefail execfail +exec "$command" $count_flag $extended_syntax_flag "${filenames[@]}" | "$formatter" diff --git a/.github/actions/git-secrets/test/bats/libexec/bats-exec-suite b/.github/actions/git-secrets/test/bats/libexec/bats-exec-suite new file mode 100755 index 00000000000..29ab255d062 --- /dev/null +++ b/.github/actions/git-secrets/test/bats/libexec/bats-exec-suite @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -e + +count_only_flag="" +if [ "$1" = "-c" ]; then + count_only_flag=1 + shift +fi + +extended_syntax_flag="" +if [ "$1" = "-x" ]; then + extended_syntax_flag="-x" + shift +fi + +trap "kill 0; exit 1" int + +count=0 +for filename in "$@"; do + let count+="$(bats-exec-test -c "$filename")" +done + +if [ -n "$count_only_flag" ]; then + echo "$count" + exit +fi + +echo "1..$count" +status=0 +offset=0 +for filename in "$@"; do + index=0 + { + IFS= read -r # 1..n + while IFS= read -r line; do + case "$line" in + "begin "* ) + let index+=1 + echo "${line/ $index / $(($offset + $index)) }" + ;; + "ok "* | "not ok "* ) + [ -n "$extended_syntax_flag" ] || let index+=1 + echo "${line/ $index / $(($offset + $index)) }" + [ "${line:0:6}" != "not ok" ] || status=1 + ;; + * ) + echo "$line" + ;; + esac + done + } < <( bats-exec-test $extended_syntax_flag "$filename" ) + offset=$(($offset + $index)) +done + +exit "$status" diff --git a/.github/actions/git-secrets/test/bats/libexec/bats-exec-test b/.github/actions/git-secrets/test/bats/libexec/bats-exec-test new file mode 100755 index 00000000000..8f3bd5102e4 --- /dev/null +++ b/.github/actions/git-secrets/test/bats/libexec/bats-exec-test @@ -0,0 +1,346 @@ +#!/usr/bin/env bash +set -e +set -E +set -T + +BATS_COUNT_ONLY="" +if [ "$1" = "-c" ]; then + BATS_COUNT_ONLY=1 + shift +fi + +BATS_EXTENDED_SYNTAX="" +if [ "$1" = "-x" ]; then + BATS_EXTENDED_SYNTAX="$1" + shift +fi + +BATS_TEST_FILENAME="$1" +if [ -z "$BATS_TEST_FILENAME" ]; then + echo "usage: bats-exec <filename>" >&2 + exit 1 +elif [ ! -f "$BATS_TEST_FILENAME" ]; then + echo "bats: $BATS_TEST_FILENAME does not exist" >&2 + exit 1 +else + shift +fi + +BATS_TEST_DIRNAME="$(dirname "$BATS_TEST_FILENAME")" +BATS_TEST_NAMES=() + +load() { + local name="$1" + local filename + + if [ "${name:0:1}" = "/" ]; then + filename="${name}" + else + filename="$BATS_TEST_DIRNAME/${name}.bash" + fi + + [ -f "$filename" ] || { + echo "bats: $filename does not exist" >&2 + exit 1 + } + + source "${filename}" +} + +run() { + local e E T oldIFS + [[ ! "$-" =~ e ]] || e=1 + [[ ! "$-" =~ E ]] || E=1 + [[ ! "$-" =~ T ]] || T=1 + set +e + set +E + set +T + output="$("$@" 2>&1)" + status="$?" + oldIFS=$IFS + IFS=$'\n' lines=($output) + [ -z "$e" ] || set -e + [ -z "$E" ] || set -E + [ -z "$T" ] || set -T + IFS=$oldIFS +} + +setup() { + true +} + +teardown() { + true +} + +skip() { + BATS_TEST_SKIPPED=${1:-1} + BATS_TEST_COMPLETED=1 + exit 0 +} + +bats_test_begin() { + BATS_TEST_DESCRIPTION="$1" + if [ -n "$BATS_EXTENDED_SYNTAX" ]; then + echo "begin $BATS_TEST_NUMBER $BATS_TEST_DESCRIPTION" >&3 + fi + setup +} + +bats_test_function() { + local test_name="$1" + BATS_TEST_NAMES["${#BATS_TEST_NAMES[@]}"]="$test_name" +} + +bats_capture_stack_trace() { + BATS_PREVIOUS_STACK_TRACE=( "${BATS_CURRENT_STACK_TRACE[@]}" ) + BATS_CURRENT_STACK_TRACE=() + + local test_pattern=" $BATS_TEST_NAME $BATS_TEST_SOURCE" + local setup_pattern=" setup $BATS_TEST_SOURCE" + local teardown_pattern=" teardown $BATS_TEST_SOURCE" + + local frame + local index=1 + + while frame="$(caller "$index")"; do + BATS_CURRENT_STACK_TRACE["${#BATS_CURRENT_STACK_TRACE[@]}"]="$frame" + if [[ "$frame" = *"$test_pattern" || \ + "$frame" = *"$setup_pattern" || \ + "$frame" = *"$teardown_pattern" ]]; then + break + else + let index+=1 + fi + done + + BATS_SOURCE="$(bats_frame_filename "${BATS_CURRENT_STACK_TRACE[0]}")" + BATS_LINENO="$(bats_frame_lineno "${BATS_CURRENT_STACK_TRACE[0]}")" +} + +bats_print_stack_trace() { + local frame + local index=1 + local count="${#@}" + + for frame in "$@"; do + local filename="$(bats_trim_filename "$(bats_frame_filename "$frame")")" + local lineno="$(bats_frame_lineno "$frame")" + + if [ $index -eq 1 ]; then + echo -n "# (" + else + echo -n "# " + fi + + local fn="$(bats_frame_function "$frame")" + if [ "$fn" != "$BATS_TEST_NAME" ]; then + echo -n "from function \`$fn' " + fi + + if [ $index -eq $count ]; then + echo "in test file $filename, line $lineno)" + else + echo "in file $filename, line $lineno," + fi + + let index+=1 + done +} + +bats_print_failed_command() { + local frame="$1" + local status="$2" + local filename="$(bats_frame_filename "$frame")" + local lineno="$(bats_frame_lineno "$frame")" + + local failed_line="$(bats_extract_line "$filename" "$lineno")" + local failed_command="$(bats_strip_string "$failed_line")" + echo -n "# \`${failed_command}' " + + if [ $status -eq 1 ]; then + echo "failed" + else + echo "failed with status $status" + fi +} + +bats_frame_lineno() { + local frame="$1" + local lineno="${frame%% *}" + echo "$lineno" +} + +bats_frame_function() { + local frame="$1" + local rest="${frame#* }" + local fn="${rest%% *}" + echo "$fn" +} + +bats_frame_filename() { + local frame="$1" + local rest="${frame#* }" + local filename="${rest#* }" + + if [ "$filename" = "$BATS_TEST_SOURCE" ]; then + echo "$BATS_TEST_FILENAME" + else + echo "$filename" + fi +} + +bats_extract_line() { + local filename="$1" + local lineno="$2" + sed -n "${lineno}p" "$filename" +} + +bats_strip_string() { + local string="$1" + printf "%s" "$string" | sed -e "s/^[ "$'\t'"]*//" -e "s/[ "$'\t'"]*$//" +} + +bats_trim_filename() { + local filename="$1" + local length="${#BATS_CWD}" + + if [ "${filename:0:length+1}" = "${BATS_CWD}/" ]; then + echo "${filename:length+1}" + else + echo "$filename" + fi +} + +bats_debug_trap() { + if [ "$BASH_SOURCE" != "$1" ]; then + bats_capture_stack_trace + fi +} + +bats_error_trap() { + BATS_ERROR_STATUS="$?" + BATS_ERROR_STACK_TRACE=( "${BATS_PREVIOUS_STACK_TRACE[@]}" ) + trap - debug +} + +bats_teardown_trap() { + trap "bats_exit_trap" exit + local status=0 + teardown >>"$BATS_OUT" 2>&1 || status="$?" + + if [ $status -eq 0 ]; then + BATS_TEARDOWN_COMPLETED=1 + elif [ -n "$BATS_TEST_COMPLETED" ]; then + BATS_ERROR_STATUS="$status" + BATS_ERROR_STACK_TRACE=( "${BATS_CURRENT_STACK_TRACE[@]}" ) + fi + + bats_exit_trap +} + +bats_exit_trap() { + local status + local skipped + trap - err exit + + skipped="" + if [ -n "$BATS_TEST_SKIPPED" ]; then + skipped=" # skip" + if [ "1" != "$BATS_TEST_SKIPPED" ]; then + skipped+=" ($BATS_TEST_SKIPPED)" + fi + fi + + if [ -z "$BATS_TEST_COMPLETED" ] || [ -z "$BATS_TEARDOWN_COMPLETED" ]; then + echo "not ok $BATS_TEST_NUMBER $BATS_TEST_DESCRIPTION" >&3 + bats_print_stack_trace "${BATS_ERROR_STACK_TRACE[@]}" >&3 + bats_print_failed_command "${BATS_ERROR_STACK_TRACE[${#BATS_ERROR_STACK_TRACE[@]}-1]}" "$BATS_ERROR_STATUS" >&3 + sed -e "s/^/# /" < "$BATS_OUT" >&3 + status=1 + else + echo "ok ${BATS_TEST_NUMBER}${skipped} ${BATS_TEST_DESCRIPTION}" >&3 + status=0 + fi + + rm -f "$BATS_OUT" + exit "$status" +} + +bats_perform_tests() { + echo "1..$#" + test_number=1 + status=0 + for test_name in "$@"; do + "$0" $BATS_EXTENDED_SYNTAX "$BATS_TEST_FILENAME" "$test_name" "$test_number" || status=1 + let test_number+=1 + done + exit "$status" +} + +bats_perform_test() { + BATS_TEST_NAME="$1" + if [ "$(type -t "$BATS_TEST_NAME" || true)" = "function" ]; then + BATS_TEST_NUMBER="$2" + if [ -z "$BATS_TEST_NUMBER" ]; then + echo "1..1" + BATS_TEST_NUMBER="1" + fi + + BATS_TEST_COMPLETED="" + BATS_TEARDOWN_COMPLETED="" + trap "bats_debug_trap \"\$BASH_SOURCE\"" debug + trap "bats_error_trap" err + trap "bats_teardown_trap" exit + "$BATS_TEST_NAME" >>"$BATS_OUT" 2>&1 + BATS_TEST_COMPLETED=1 + + else + echo "bats: unknown test name \`$BATS_TEST_NAME'" >&2 + exit 1 + fi +} + +if [ -z "$TMPDIR" ]; then + BATS_TMPDIR="/tmp" +else + BATS_TMPDIR="${TMPDIR%/}" +fi + +BATS_TMPNAME="$BATS_TMPDIR/bats.$$" +BATS_PARENT_TMPNAME="$BATS_TMPDIR/bats.$PPID" +BATS_OUT="${BATS_TMPNAME}.out" + +bats_preprocess_source() { + BATS_TEST_SOURCE="${BATS_TMPNAME}.src" + { tr -d '\r' < "$BATS_TEST_FILENAME"; echo; } | bats-preprocess > "$BATS_TEST_SOURCE" + trap "bats_cleanup_preprocessed_source" err exit + trap "bats_cleanup_preprocessed_source; exit 1" int +} + +bats_cleanup_preprocessed_source() { + rm -f "$BATS_TEST_SOURCE" +} + +bats_evaluate_preprocessed_source() { + if [ -z "$BATS_TEST_SOURCE" ]; then + BATS_TEST_SOURCE="${BATS_PARENT_TMPNAME}.src" + fi + source "$BATS_TEST_SOURCE" +} + +exec 3<&1 + +if [ "$#" -eq 0 ]; then + bats_preprocess_source + bats_evaluate_preprocessed_source + + if [ -n "$BATS_COUNT_ONLY" ]; then + echo "${#BATS_TEST_NAMES[@]}" + else + bats_perform_tests "${BATS_TEST_NAMES[@]}" + fi +else + bats_evaluate_preprocessed_source + bats_perform_test "$@" +fi diff --git a/.github/actions/git-secrets/test/bats/libexec/bats-format-tap-stream b/.github/actions/git-secrets/test/bats/libexec/bats-format-tap-stream new file mode 100755 index 00000000000..614768f4d9e --- /dev/null +++ b/.github/actions/git-secrets/test/bats/libexec/bats-format-tap-stream @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +set -e + +# Just stream the TAP output (sans extended syntax) if tput is missing +command -v tput >/dev/null || exec grep -v "^begin " + +header_pattern='[0-9]+\.\.[0-9]+' +IFS= read -r header + +if [[ "$header" =~ $header_pattern ]]; then + count="${header:3}" + index=0 + failures=0 + skipped=0 + name="" + count_column_width=$(( ${#count} * 2 + 2 )) +else + # If the first line isn't a TAP plan, print it and pass the rest through + printf "%s\n" "$header" + exec cat +fi + +update_screen_width() { + screen_width="$(tput cols)" + count_column_left=$(( $screen_width - $count_column_width )) +} + +trap update_screen_width WINCH +update_screen_width + +begin() { + go_to_column 0 + printf_with_truncation $(( $count_column_left - 1 )) " %s" "$name" + clear_to_end_of_line + go_to_column $count_column_left + printf "%${#count}s/${count}" "$index" + go_to_column 1 +} + +pass() { + go_to_column 0 + printf " ✓ %s" "$name" + advance +} + +skip() { + local reason="$1" + [ -z "$reason" ] || reason=": $reason" + go_to_column 0 + printf " - %s (skipped%s)" "$name" "$reason" + advance +} + +fail() { + go_to_column 0 + set_color 1 bold + printf " ✗ %s" "$name" + advance +} + +log() { + set_color 1 + printf " %s\n" "$1" + clear_color +} + +summary() { + printf "\n%d test%s" "$count" "$(plural "$count")" + + printf ", %d failure%s" "$failures" "$(plural "$failures")" + + if [ "$skipped" -gt 0 ]; then + printf ", %d skipped" "$skipped" + fi + + printf "\n" +} + +printf_with_truncation() { + local width="$1" + shift + local string="$(printf "$@")" + + if [ "${#string}" -gt "$width" ]; then + printf "%s..." "${string:0:$(( $width - 4 ))}" + else + printf "%s" "$string" + fi +} + +go_to_column() { + local column="$1" + printf "\x1B[%dG" $(( $column + 1 )) +} + +clear_to_end_of_line() { + printf "\x1B[K" +} + +advance() { + clear_to_end_of_line + echo + clear_color +} + +set_color() { + local color="$1" + local weight="$2" + printf "\x1B[%d;%dm" $(( 30 + $color )) "$( [ "$weight" = "bold" ] && echo 1 || echo 22 )" +} + +clear_color() { + printf "\x1B[0m" +} + +plural() { + [ "$1" -eq 1 ] || echo "s" +} + +_buffer="" + +buffer() { + _buffer="${_buffer}$("$@")" +} + +flush() { + printf "%s" "$_buffer" + _buffer="" +} + +finish() { + flush + printf "\n" +} + +trap finish EXIT + +while IFS= read -r line; do + case "$line" in + "begin "* ) + let index+=1 + name="${line#* $index }" + buffer begin + flush + ;; + "ok "* ) + skip_expr="ok $index # skip (\(([^)]*)\))?" + if [[ "$line" =~ $skip_expr ]]; then + let skipped+=1 + buffer skip "${BASH_REMATCH[2]}" + else + buffer pass + fi + ;; + "not ok "* ) + let failures+=1 + buffer fail + ;; + "# "* ) + buffer log "${line:2}" + ;; + esac +done + +buffer summary diff --git a/.github/actions/git-secrets/test/bats/libexec/bats-preprocess b/.github/actions/git-secrets/test/bats/libexec/bats-preprocess new file mode 100755 index 00000000000..04297ed019b --- /dev/null +++ b/.github/actions/git-secrets/test/bats/libexec/bats-preprocess @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -e + +encode_name() { + local name="$1" + local result="test_" + + if [[ ! "$name" =~ [^[:alnum:]\ _-] ]]; then + name="${name//_/-5f}" + name="${name//-/-2d}" + name="${name// /_}" + result+="$name" + else + local length="${#name}" + local char i + + for ((i=0; i<length; i++)); do + char="${name:$i:1}" + if [ "$char" = " " ]; then + result+="_" + elif [[ "$char" =~ [[:alnum:]] ]]; then + result+="$char" + else + result+="$(printf -- "-%02x" \'"$char")" + fi + done + fi + + echo "$result" +} + +tests=() +index=0 +pattern='^ *@test *([^ ].*) *\{ *(.*)$' + +while IFS= read -r line; do + let index+=1 + if [[ "$line" =~ $pattern ]]; then + quoted_name="${BASH_REMATCH[1]}" + body="${BASH_REMATCH[2]}" + name="$(eval echo "$quoted_name")" + encoded_name="$(encode_name "$name")" + tests["${#tests[@]}"]="$encoded_name" + echo "${encoded_name}() { bats_test_begin ${quoted_name} ${index}; ${body}" + else + printf "%s\n" "$line" + fi +done + +for test_name in "${tests[@]}"; do + echo "bats_test_function ${test_name}" +done diff --git a/.github/actions/git-secrets/test/commit-msg.bats b/.github/actions/git-secrets/test/commit-msg.bats new file mode 100644 index 00000000000..009121952c9 --- /dev/null +++ b/.github/actions/git-secrets/test/commit-msg.bats @@ -0,0 +1,18 @@ +#!/usr/bin/env bats +load test_helper + +@test "Rejects commit messages with prohibited patterns" { + setup_good_repo + repo_run git-secrets --install $TEST_REPO + run git commit -m '@todo in the message??' + [ $status -eq 1 ] + [ "${lines[0]}" == ".git/COMMIT_EDITMSG:1:@todo in the message??" ] +} + +@test "Allows commit messages that do not match a prohibited pattern" { + setup_good_repo + repo_run git-secrets --install $TEST_REPO + cd $TEST_REPO + run git commit -m 'This is OK' + [ $status -eq 0 ] +} diff --git a/.github/actions/git-secrets/test/git-secrets.bats b/.github/actions/git-secrets/test/git-secrets.bats new file mode 100644 index 00000000000..b7a5b1c0903 --- /dev/null +++ b/.github/actions/git-secrets/test/git-secrets.bats @@ -0,0 +1,361 @@ +#!/usr/bin/env bats +load test_helper + +@test "no arguments prints usage instructions" { + repo_run git-secrets + [ $status -eq 0 ] + [ $(expr "${lines[0]}" : "usage: git secrets") -ne 0 ] +} + +@test "-h prints help" { + repo_run git-secrets -h + [ $(expr "${lines[0]}" : "usage: git secrets") -ne 0 ] +} + +@test "Invalid scan filename fails" { + repo_run git-secrets --scan /path/to/not/there + [ $status -eq 2 ] + echo "$output" | grep "No such file" +} + +@test "Does not require secrets" { + git config --unset-all secrets.patterns || true + repo_run git-secrets --scan $BATS_TEST_FILENAME + [ $status -eq 0 ] +} + +@test "No prohibited matches exits 0" { + echo 'it is ok' > "$BATS_TMPDIR/test.txt" + repo_run git-secrets --scan "$BATS_TMPDIR/test.txt" + [ $status -eq 0 ] +} + +@test "Scans all files when no file provided" { + setup_bad_repo + repo_run git-secrets --scan + [ $status -eq 1 ] +} + +@test "Scans all files including history" { + setup_bad_repo + repo_run git-secrets --scan-history + [ $status -eq 1 ] +} + +@test "Scans all files when no file provided with secret in history" { + setup_bad_repo_history + repo_run git-secrets --scan + [ $status -eq 0 ] +} + +@test "Scans all files including history with secret in history" { + setup_bad_repo_history + repo_run git-secrets --scan-history + [ $status -eq 1 ] +} + +@test "Scans history with secrets distributed among branches in history" { + cd $TEST_REPO + echo '@todo' > $TEST_REPO/history_failure.txt + git add -A + git commit -m "Testing history" + echo 'todo' > $TEST_REPO/history_failure.txt + git add -A + git commit -m "Testing history" + git checkout -b testbranch + echo '@todo' > $TEST_REPO/history_failure.txt + git add -A + git commit -m "Testing history" + git checkout master + cd - + repo_run git-secrets --scan-history + [ $status -eq 1 ] +} + +@test "Scans recursively" { + setup_bad_repo + mkdir -p $TEST_REPO/foo/bar/baz + echo '@todo more stuff' > $TEST_REPO/foo/bar/baz/data.txt + repo_run git-secrets --scan -r $TEST_REPO/foo + [ $status -eq 1 ] +} + +@test "Scans recursively only if -r is given" { + setup_bad_repo + mkdir -p $TEST_REPO/foo/bar/baz + echo '@todo more stuff' > $TEST_REPO/foo/bar/baz/data.txt + repo_run git-secrets --scan $TEST_REPO/foo + [ $status -eq 0 ] +} + +@test "Excludes allowed patterns from failures" { + git config --add secrets.patterns 'foo="baz{1,5}"' + git config --add secrets.allowed 'foo="bazzz"' + echo 'foo="bazzz" is ok because 3 "z"s' > "$BATS_TMPDIR/test.txt" + repo_run git-secrets --scan "$BATS_TMPDIR/test.txt" + [ $status -eq 0 ] + echo 'This is NOT: ok foo="bazzzz"' > "$BATS_TMPDIR/test.txt" + repo_run git-secrets --scan "$BATS_TMPDIR/test.txt" + [ $status -eq 1 ] +} + +@test "Prohibited matches exits 1" { + file="$TEST_REPO/test.txt" + echo '@todo stuff' > $file + echo 'this is forbidden right?' >> $file + repo_run git-secrets --scan $file + [ $status -eq 1 ] + [ "${lines[0]}" == "$file:1:@todo stuff" ] + [ "${lines[1]}" == "$file:2:this is forbidden right?" ] +} + +@test "Only matches on word boundaries" { + file="$TEST_REPO/test.txt" + # Note that the following does not match as it is not a word. + echo 'mesa Jar Jar Binks' > $file + # The following do match because they are in word boundaries. + echo 'foo.me' >> $file + echo '"me"' >> $file + repo_run git-secrets --scan $file + [ $status -eq 1 ] + [ "${lines[0]}" == "$file:2:foo.me" ] + [ "${lines[1]}" == "$file:3:\"me\"" ] +} + +@test "Can scan from stdin using -" { + echo "foo" | "${BATS_TEST_DIRNAME}/../git-secrets" --scan - + echo "me" | "${BATS_TEST_DIRNAME}/../git-secrets" --scan - && exit 1 || true +} + +@test "installs hooks for repo" { + setup_bad_repo + repo_run git-secrets --install $TEST_REPO + [ -f $TEST_REPO/.git/hooks/pre-commit ] + [ -f $TEST_REPO/.git/hooks/prepare-commit-msg ] + [ -f $TEST_REPO/.git/hooks/commit-msg ] +} + +@test "fails if hook exists and no -f" { + repo_run git-secrets --install $TEST_REPO + repo_run git-secrets --install $TEST_REPO + [ $status -eq 1 ] +} + +@test "Overwrites hooks if -f is given" { + repo_run git-secrets --install $TEST_REPO + repo_run git-secrets --install -f $TEST_REPO + [ $status -eq 0 ] +} + +@test "installs hooks for repo with Debian style directories" { + setup_bad_repo + mkdir $TEST_REPO/.git/hooks/pre-commit.d + mkdir $TEST_REPO/.git/hooks/prepare-commit-msg.d + mkdir $TEST_REPO/.git/hooks/commit-msg.d + run git-secrets --install $TEST_REPO + [ -f $TEST_REPO/.git/hooks/pre-commit.d/git-secrets ] + [ -f $TEST_REPO/.git/hooks/prepare-commit-msg.d/git-secrets ] + [ -f $TEST_REPO/.git/hooks/commit-msg.d/git-secrets ] +} + +@test "installs hooks to template directory" { + setup_bad_repo + run git-secrets --install $TEMPLATE_DIR + [ $status -eq 0 ] + run git init --template $TEMPLATE_DIR + [ $status -eq 0 ] + [ -f "${TEST_REPO}/.git/hooks/pre-commit" ] + [ -f "${TEST_REPO}/.git/hooks/prepare-commit-msg" ] + [ -f "${TEST_REPO}/.git/hooks/commit-msg" ] +} + +@test "Scans using keys from credentials file" { + echo 'aws_access_key_id = abc123' > $BATS_TMPDIR/test.ini + echo 'aws_secret_access_key=foobaz' >> $BATS_TMPDIR/test.ini + echo 'aws_access_key_id = "Bernard"' >> $BATS_TMPDIR/test.ini + echo 'aws_secret_access_key= "Laverne"' >> $BATS_TMPDIR/test.ini + echo 'aws_access_key_id= Hoagie+man' >> $BATS_TMPDIR/test.ini + cd $TEST_REPO + run git secrets --aws-provider $BATS_TMPDIR/test.ini + [ $status -eq 0 ] + echo "$output" | grep -F "foobaz" + echo "$output" | grep -F "abc123" + echo "$output" | grep -F "Bernard" + echo "$output" | grep -F "Laverne" + echo "$output" | grep -F 'Hoagie\+man' + run git secrets --add-provider -- git secrets --aws-provider $BATS_TMPDIR/test.ini + [ $status -eq 0 ] + echo '(foobaz) test' > $TEST_REPO/bad_file + echo "abc123 test" >> $TEST_REPO/bad_file + echo 'Bernard test' >> $TEST_REPO/bad_file + echo 'Laverne test' >> $TEST_REPO/bad_file + echo 'Hoagie+man test' >> $TEST_REPO/bad_file + repo_run git-secrets --scan $TEST_REPO/bad_file + [ $status -eq 1 ] + echo "$output" | grep "foobaz" + echo "$output" | grep "abc123" + echo "$output" | grep "Bernard" + echo "$output" | grep "Laverne" + echo "$output" | grep -F 'Hoagie+man' +} + +@test "Lists secrets for a repo" { + repo_run git-secrets --list + [ $status -eq 0 ] + echo "$output" | grep -F 'secrets.patterns @todo' + echo "$output" | grep -F 'secrets.patterns forbidden|me' +} + +@test "Adds secrets to a repo and de-dedupes" { + repo_run git-secrets --add 'testing+123' + [ $status -eq 0 ] + repo_run git-secrets --add 'testing+123' + [ $status -eq 1 ] + repo_run git-secrets --add --literal 'testing+abc' + [ $status -eq 0 ] + repo_run git-secrets --add -l 'testing+abc' + [ $status -eq 1 ] + repo_run git-secrets --list + echo "$output" | grep -F 'secrets.patterns @todo' + echo "$output" | grep -F 'secrets.patterns forbidden|me' + echo "$output" | grep -F 'secrets.patterns testing+123' + echo "$output" | grep -F 'secrets.patterns testing\+abc' +} + +@test "Adds allowed patterns to a repo and de-dedupes" { + repo_run git-secrets --add -a 'testing+123' + [ $status -eq 0 ] + repo_run git-secrets --add --allowed 'testing+123' + [ $status -eq 1 ] + repo_run git-secrets --add -a -l 'testing+abc' + [ $status -eq 0 ] + repo_run git-secrets --add -a -l 'testing+abc' + [ $status -eq 1 ] + repo_run git-secrets --list + echo "$output" | grep -F 'secrets.patterns @todo' + echo "$output" | grep -F 'secrets.patterns forbidden|me' + echo "$output" | grep -F 'secrets.allowed testing+123' + echo "$output" | grep -F 'secrets.allowed testing\+abc' +} + +@test "Empty lines must be ignored in .gitallowed files" { + setup_bad_repo + echo '' >> $TEST_REPO/.gitallowed + repo_run git-secrets --scan + [ $status -eq 1 ] +} + +@test "Comment lines must be ignored in .gitallowed files" { + setup_bad_repo_with_hash + repo_run git-secrets --scan + [ $status -eq 1 ] + echo '#hash' > $TEST_REPO/.gitallowed + repo_run git-secrets --scan + [ $status -eq 1 ] + echo 'hash' > $TEST_REPO/.gitallowed + repo_run git-secrets --scan + [ $status -eq 0 ] +} + +@test "Scans all files and allowing none of the bad patterns in .gitallowed" { + setup_bad_repo + echo 'hello' > $TEST_REPO/.gitallowed + repo_run git-secrets --scan + [ $status -eq 1 ] +} + +@test "Scans all files and allowing all bad patterns in .gitallowed" { + setup_bad_repo + echo '@todo' > $TEST_REPO/.gitallowed + echo 'forbidden' >> $TEST_REPO/.gitallowed + echo 'me' >> $TEST_REPO/.gitallowed + repo_run git-secrets --scan + [ $status -eq 0 ] +} + +@test "Adds common AWS patterns" { + repo_run git config --unset-all secrets + repo_run git-secrets --register-aws + git config --local --get secrets.providers + repo_run git-secrets --list + echo "$output" | grep -F '(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}' + echo "$output" | grep "AKIAIOSFODNN7EXAMPLE" + echo "$output" | grep "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" +} + +@test "Adds providers" { + repo_run git-secrets --add-provider -- echo foo baz bar + [ $status -eq 0 ] + repo_run git-secrets --add-provider -- echo bam + [ $status -eq 0 ] + repo_run git-secrets --list + echo "$output" | grep -F 'echo foo baz bar' + echo "$output" | grep -F 'echo bam' + echo 'foo baz bar' > $TEST_REPO/bad_file + echo 'bam' >> $TEST_REPO/bad_file + repo_run git-secrets --scan $TEST_REPO/bad_file + [ $status -eq 1 ] + echo "$output" | grep -F 'foo baz bar' + echo "$output" | grep -F 'bam' +} + +@test "Strips providers that return nothing" { + repo_run git-secrets --add-provider -- 'echo' + [ $status -eq 0 ] + repo_run git-secrets --add-provider -- 'echo 123' + [ $status -eq 0 ] + repo_run git-secrets --list + echo "$output" | grep -F 'echo 123' + echo 'foo' > $TEST_REPO/bad_file + repo_run git-secrets --scan $TEST_REPO/bad_file + [ $status -eq 0 ] +} + +@test "--recursive cannot be used with SCAN_*" { + repo_run git-secrets --scan -r --cached + [ $status -eq 1 ] + repo_run git-secrets --scan -r --no-index + [ $status -eq 1 ] + repo_run git-secrets --scan -r --untracked + [ $status -eq 1 ] +} + +@test "--recursive can be used with --scan" { + repo_run git-secrets --scan -r + [ $status -eq 0 ] +} + +@test "--recursive can't be used with --list" { + repo_run git-secrets --list -r + [ $status -eq 1 ] +} + +@test "-f can only be used with --install" { + repo_run git-secrets --scan -f + [ $status -eq 1 ] +} + +@test "-a can only be used with --add" { + repo_run git-secrets --scan -a + [ $status -eq 1 ] +} + +@test "-l can only be used with --add" { + repo_run git-secrets --scan -l + [ $status -eq 1 ] +} + +@test "--cached can only be used with --scan" { + repo_run git-secrets --list --cached + [ $status -eq 1 ] +} + +@test "--no-index can only be used with --scan" { + repo_run git-secrets --list --no-index + [ $status -eq 1 ] +} + +@test "--untracked can only be used with --scan" { + repo_run git-secrets --list --untracked + [ $status -eq 1 ] +} diff --git a/.github/actions/git-secrets/test/pre-commit.bats b/.github/actions/git-secrets/test/pre-commit.bats new file mode 100644 index 00000000000..5ace267cbb3 --- /dev/null +++ b/.github/actions/git-secrets/test/pre-commit.bats @@ -0,0 +1,62 @@ +#!/usr/bin/env bats +load test_helper + +@test "Rejects commits with prohibited patterns in changeset" { + setup_bad_repo + repo_run git-secrets --install $TEST_REPO + cd $TEST_REPO + run git commit -m 'Contents are bad not the message' + [ $status -eq 1 ] + [ "${lines[0]}" == "data.txt:1:@todo more stuff" ] + [ "${lines[1]}" == "failure1.txt:1:another line... forbidden" ] + [ "${lines[2]}" == "failure2.txt:1:me" ] +} + +@test "Rejects commits with prohibited patterns in changeset with filename that contain spaces" { + setup_bad_repo_with_spaces + repo_run git-secrets --install $TEST_REPO + cd $TEST_REPO + run git commit -m 'Contents are bad not the message' + [ $status -eq 1 ] + [ "${lines[0]}" == "da ta.txt:1:@todo more stuff" ] +} + +@test "Scans staged files" { + cd $TEST_REPO + repo_run git-secrets --install $TEST_REPO + echo '@todo more stuff' > $TEST_REPO/data.txt + echo 'hi there' > $TEST_REPO/ok.txt + git add -A + echo 'fixed the working directory, but not staged' > $TEST_REPO/data.txt + run git commit -m 'Contents are bad not the message' + [ $status -eq 1 ] + [ "${lines[0]}" == "data.txt:1:@todo more stuff" ] +} + +@test "Allows commits that do not match prohibited patterns" { + setup_good_repo + repo_run git-secrets --install $TEST_REPO + cd $TEST_REPO + run git commit -m 'This is fine' + [ $status -eq 0 ] + # Ensure deleted files are filtered out of the grep + rm $TEST_REPO/data.txt + echo 'aaa' > $TEST_REPO/data_2.txt + run git add -A + run git commit -m 'This is also fine' + [ $status -eq 0 ] +} + +@test "Rejects commits with prohibited patterns in changeset when AWS provider is enabled" { + setup_bad_repo + repo_run git-secrets --install $TEST_REPO + repo_run git-secrets --register-aws $TEST_REPO + cd $TEST_REPO + run git commit -m 'Contents are bad not the message' + [ $status -eq 1 ] + echo "${lines}" | grep -vq 'git secrets --aws-provider: command not found' + + [ "${lines[0]}" == "data.txt:1:@todo more stuff" ] + [ "${lines[1]}" == "failure1.txt:1:another line... forbidden" ] + [ "${lines[2]}" == "failure2.txt:1:me" ] +} diff --git a/.github/actions/git-secrets/test/prepare-commit-msg.bats b/.github/actions/git-secrets/test/prepare-commit-msg.bats new file mode 100644 index 00000000000..a211c1318a2 --- /dev/null +++ b/.github/actions/git-secrets/test/prepare-commit-msg.bats @@ -0,0 +1,33 @@ +#!/usr/bin/env bats +load test_helper + +@test "Rejects merges with prohibited patterns in history" { + setup_good_repo + repo_run git-secrets --install $TEST_REPO + cd $TEST_REPO + git commit -m 'OK' + git checkout -b feature + echo '@todo' > data.txt + git add -A + git commit -m 'Bad commit' --no-verify + echo 'Fixing!' > data.txt + git add -A + git commit -m 'Fixing commit' + git checkout master + run git merge --no-ff feature + [ $status -eq 1 ] +} + +@test "Allows merges that do not match prohibited patterns" { + setup_good_repo + cd $TEST_REPO + repo_run git-secrets --install + git commit -m 'OK' + git checkout -b feature + echo 'Not bad' > data.txt + git add -A + git commit -m 'Good commit' + git checkout master + run git merge --no-ff feature + [ $status -eq 0 ] +} diff --git a/.github/actions/git-secrets/test/test_helper.bash b/.github/actions/git-secrets/test/test_helper.bash new file mode 100644 index 00000000000..9133e5162ec --- /dev/null +++ b/.github/actions/git-secrets/test/test_helper.bash @@ -0,0 +1,94 @@ +#!/bin/bash +export TEST_REPO="$BATS_TMPDIR/test-repo" +export TEMP_HOME="$BATS_TMPDIR/home" +export TEMPLATE_DIR="${BATS_TMPDIR}/template" +INITIAL_PATH="${PATH}" +INITIAL_HOME=${HOME} + +setup() { + setup_repo + [ -d "${TEMPLATE_DIR}" ] && rm -rf "${TEMPLATE_DIR}" + [ -d "${TEMP_HOME}" ] && rm -rf "${TEMP_HOME}" + mkdir -p $TEMP_HOME + export HOME=$TEMP_HOME + export PATH="${BATS_TEST_DIRNAME}/..:${INITIAL_PATH}" + cd $TEST_REPO +} + +teardown() { + delete_repo + export PATH="${INITIAL_PATH}" + export HOME="${INITIAL_HOME}" + [ -d "${TEMP_HOME}" ] && rm -rf "${TEMP_HOME}" +} + +delete_repo() { + [ -d $TEST_REPO ] && rm -rf $TEST_REPO || true +} + +setup_repo() { + delete_repo + mkdir -p $TEST_REPO + cd $TEST_REPO + git init + git config --local --add secrets.patterns '@todo' + git config --local --add secrets.patterns 'forbidden|me' + git config --local --add secrets.patterns '#hash' + git config --local user.email "you@example.com" + git config --local user.name "Your Name" + cd - +} + +repo_run() { + cmd="$1" + shift + cd "${TEST_REPO}" + run "${BATS_TEST_DIRNAME}/../${cmd}" $@ + cd - +} + +# Creates a repo that should fail +setup_bad_repo() { + cd $TEST_REPO + echo '@todo more stuff' > $TEST_REPO/data.txt + echo 'hi there' > $TEST_REPO/ok.txt + echo 'another line... forbidden' > $TEST_REPO/failure1.txt + echo 'me' > $TEST_REPO/failure2.txt + git add -A + cd - +} + +# Creates a repo that should fail +setup_bad_repo_with_spaces() { + cd $TEST_REPO + echo '@todo more stuff' > "$TEST_REPO/da ta.txt" + git add -A + cd - +} + +# Creates a repo that should fail +setup_bad_repo_with_hash() { + cd $TEST_REPO + echo '#hash' > "$TEST_REPO/data.txt" + git add -A + cd - +} + +# Creates a repo that should fail +setup_bad_repo_history() { + cd $TEST_REPO + echo '@todo' > $TEST_REPO/history_failure.txt + git add -A + git commit -m "Testing history" + echo 'todo' > $TEST_REPO/history_failure.txt + git add -A + cd - +} + +# Creates a repo that does not fail +setup_good_repo() { + cd $TEST_REPO + echo 'hello!' > $TEST_REPO/data.txt + git add -A + cd - +} From fb486a700a89ae09d64f3584b6adea86d66d7c23 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <1084551+emvaldes@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:33:35 -0700 Subject: [PATCH 23/32] Update dependabot.yml Adding these GitHub Actions (Josiah Siegel's external/remote repositories) to the Dependabot configurations. These repositories are in the process of getting imported/integrated into the repository to eliminate any external dependencies. --- .github/dependabot.yml | 58 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8bc1de5d19f..ea193a27f3a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -120,6 +120,64 @@ updates: schedule: interval: "daily" + ## Importing Josiah Siegel's external/remote GitHub Actions + - package-ecosystem: "github-actions" + directory: "/.github/actions/azviz-action" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/action-connect-ovpn" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/checksum-validate-action" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/git-secrets" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/randomrepo" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/rapid-wsl" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/reliable-pull-request-action" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/remote-branch-action" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/runleaks" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/slack-boltjs-app" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/stackoverflow_in_pg" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/terraform-stats" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/terraform-templates" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/workflow-housekeeper" + schedule: + interval: "daily" + # Frontend - package-ecosystem: "npm" directory: "/frontend-react" From ae9f58a04098296984b15dca1f33e16da4b5b6c8 Mon Sep 17 00:00:00 2001 From: Eduardo Valdes <eduardo.valdes@aquia.io> Date: Mon, 21 Oct 2024 20:01:48 -0700 Subject: [PATCH 24/32] Restructuring the scope for target GitHub Actions to be imported --- .../workflows/sample_full_workflow.yml | 70 - .../.github/workflows/sample_min_workflow.yml | 35 - .../.github/workflows/test_linux_runner.yml | 31 - .../.github/workflows/test_windows_runner.yml | 31 - .github/actions/azviz-action/README.md | 93 - .github/actions/azviz-action/action.yml | 83 - .github/actions/azviz-action/viz_run.ps1 | 57 - .../.github/dependabot.yml | 13 - .../.github/workflows/test_action.yml | 119 - .../checksum-validate-action/README.md | 94 - .../checksum-validate-action/action.yml | 111 - .github/actions/git-secrets/.gitattributes | 10 - .../.github/PULL_REQUEST_TEMPLATE.md | 6 - .../git-secrets/.pre-commit-hooks.yaml | 5 - .github/actions/git-secrets/.travis.yml | 8 - .github/actions/git-secrets/CHANGELOG.md | 49 - .../actions/git-secrets/CODE_OF_CONDUCT.md | 4 - .github/actions/git-secrets/CONTRIBUTING.md | 61 - .github/actions/git-secrets/LICENSE.txt | 208 - .github/actions/git-secrets/Makefile | 25 - .github/actions/git-secrets/NOTICE.txt | 6 - .github/actions/git-secrets/README.rst | 565 --- .github/actions/git-secrets/git-secrets | 409 -- .github/actions/git-secrets/git-secrets.1 | 843 ---- .github/actions/git-secrets/install.ps1 | 48 - .github/actions/git-secrets/test/bats/LICENSE | 20 - .../actions/git-secrets/test/bats/bin/bats | 142 - .../git-secrets/test/bats/libexec/bats | 142 - .../test/bats/libexec/bats-exec-suite | 55 - .../test/bats/libexec/bats-exec-test | 346 -- .../test/bats/libexec/bats-format-tap-stream | 165 - .../test/bats/libexec/bats-preprocess | 52 - .../actions/git-secrets/test/commit-msg.bats | 18 - .../actions/git-secrets/test/git-secrets.bats | 361 -- .../actions/git-secrets/test/pre-commit.bats | 62 - .../git-secrets/test/prepare-commit-msg.bats | 33 - .../actions/git-secrets/test/test_helper.bash | 94 - .../randomrepo/.github/workflows/main.yml | 19 - .github/actions/randomrepo/Dockerfile | 13 - .github/actions/randomrepo/README.md | 15 - .github/actions/randomrepo/action.yml | 18 - .github/actions/randomrepo/entrypoint.sh | 24 - .github/actions/randomrepo/lib/install.sh | 8 - .github/actions/rapid-wsl/.gitignore | 8 - .github/actions/rapid-wsl/README.md | 105 - .github/actions/rapid-wsl/defaults/demo.sh | 1 - .github/actions/rapid-wsl/defaults/generic.sh | 1 - .github/actions/rapid-wsl/defaults/macos.sh | 1 - .../defaults/prime-data-ingestion.sh | 1 - .../rapid-wsl/defaults/prime-reportstream.sh | 1 - .../actions/rapid-wsl/etc/sample.gitconfig | 9 - .github/actions/rapid-wsl/lib/args.sh | 163 - .../rapid-wsl/lib/commands/backup_distro.sh | 5 - .../rapid-wsl/lib/commands/destroy_distro.sh | 9 - .../rapid-wsl/lib/commands/docker_distro.sh | 30 - .../rapid-wsl/lib/commands/down_distro.sh | 8 - .../rapid-wsl/lib/commands/enter_distro.sh | 17 - .../rapid-wsl/lib/commands/fix_distro.sh | 8 - .../rapid-wsl/lib/commands/git_setup.sh | 28 - .../rapid-wsl/lib/commands/gpg_import.sh | 42 - .../rapid-wsl/lib/commands/init_distro.sh | 50 - .../rapid-wsl/lib/commands/install_distro.sh | 61 - .../rapid-wsl/lib/commands/restore_distro.sh | 9 - .../rapid-wsl/lib/commands/setup_distro.sh | 27 - .../rapid-wsl/lib/commands/status_distro.sh | 39 - .../lib/commands/test-pipe_distro.sh | 8 - .../rapid-wsl/lib/commands/test_distro.sh | 8 - .../rapid-wsl/lib/commands/tips_distro.sh | 37 - .../rapid-wsl/lib/commands/up_distro.sh | 12 - .github/actions/rapid-wsl/lib/controller.sh | 66 - .github/actions/rapid-wsl/lib/waiting.sh | 17 - .github/actions/rapid-wsl/modules/README.md | 4 - .../rapid-wsl/modules/demo/install_distro.sh | 28 - .../rapid-wsl/modules/generic/README.md | 17 - .../rapid-wsl/modules/macos/install_distro.sh | 28 - .../rapid-wsl/modules/macos/setup_distro.sh | 24 - .../modules/prime-data-ingestion/README.md | 18 - .../prime-data-ingestion/install_distro.sh | 44 - .../prime-data-ingestion/setup_distro.sh | 16 - .../modules/prime-reportstream/README.md | 18 - .../modules/prime-reportstream/down_distro.sh | 11 - .../modules/prime-reportstream/fix_distro.sh | 7 - .../prime-reportstream/install_distro.sh | 45 - .../prime-reportstream/setup_distro.sh | 44 - .../prime-reportstream/status_distro.sh | 7 - .../prime-reportstream/test-pipe_distro.sh | 8 - .../modules/prime-reportstream/test_distro.sh | 23 - .../modules/prime-reportstream/tips_distro.sh | 34 - .../modules/prime-reportstream/up_distro.sh | 9 - .github/actions/rapid-wsl/rwsl | 4 - .../.github/workflows/test-action.yml | 35 - .../reliable-pull-request-action/LICENSE | 21 - .../reliable-pull-request-action/README.md | 83 - .../reliable-pull-request-action/action.yml | 42 - .../reliable-pull-request-action/create-pr.sh | 16 - .../.github/workflows/test-action.yml | 49 - .github/actions/remote-branch-action/LICENSE | 21 - .../actions/remote-branch-action/README.md | 108 - .../actions/remote-branch-action/action.yml | 61 - .../actions/runleaks/.github/dependabot.yml | 16 - .../runleaks/.github/runleaks/exclusions.txt | 26 - .../runleaks/.github/runleaks/patterns.txt | 20 - .../runleaks/patterns_force_failure.txt | 21 - .../runleaks/.github/workflows/main.yml | 49 - .../.github/workflows/scan_public.yml | 61 - .github/actions/runleaks/Dockerfile | 9 - .github/actions/runleaks/LICENSE | 21 - .github/actions/runleaks/README.md | 163 - .github/actions/runleaks/action.yml | 55 - .github/actions/runleaks/lib/scan.sh | 126 - .../actions/slack-boltjs-app/.dockerignore | 1 - .github/actions/slack-boltjs-app/.env.example | 10 - .../actions/slack-boltjs-app/.eslintignore | 1 - .../actions/slack-boltjs-app/.eslintrc.cjs | 15 - .../slack-boltjs-app/.github/dependabot.yml | 17 - .../.github/workflows/codacy.yml | 61 - .../.github/workflows/codeql.yml | 74 - .../.github/workflows/confirm_push.yml | 15 - .../.github/workflows/confirm_run.yml | 19 - .../.github/workflows/dependency-review.yml | 20 - .../.github/workflows/njsscan.yml | 40 - .../.github/workflows/validate.yml | 64 - .github/actions/slack-boltjs-app/.gitignore | 3 - .../actions/slack-boltjs-app/.glitch-assets | 6 - .../actions/slack-boltjs-app/.help.example | 32 - .../slack-boltjs-app/Dockerfile.example | 11 - .github/actions/slack-boltjs-app/Makefile | 10 - .github/actions/slack-boltjs-app/README.md | 83 - .../slack-boltjs-app/npm-shrinkwrap.json | 4263 ----------------- .github/actions/slack-boltjs-app/package.json | 27 - .github/actions/slack-boltjs-app/src/index.js | 47 - .../src/utils/github/default_branch.js | 11 - .../src/utils/github/list_targets.js | 13 - .../src/utils/github/lock_target.js | 61 - .../slack-boltjs-app/src/utils/github/push.js | 182 - .../src/utils/github/request.js | 44 - .../src/utils/github/router.js | 65 - .../src/utils/github/run_workflow.js | 43 - .../slack-boltjs-app/src/views/app_home.js | 33 - .../actions/stackoverflow_in_pg/.gitignore | 145 - .github/actions/stackoverflow_in_pg/README.md | 87 - .../install_pg/install_centos.md | 105 - .../install_pg/install_ubuntu.md | 98 - .../python_src/sample_xml/Badges.xml | 11 - .../python_src/sample_xml/Comments.xml | 11 - .../python_src/sample_xml/PostHistory.xml | 11 - .../python_src/sample_xml/PostLinks.xml | 11 - .../python_src/sample_xml/Posts.xml | 12 - .../python_src/sample_xml/Tags.xml | 11 - .../python_src/sample_xml/Users.xml | 11 - .../python_src/sample_xml/Votes.xml | 11 - .../python_src/so2pg-badges.py | 59 - .../python_src/so2pg-comments.py | 62 - .../python_src/so2pg-posthistory.py | 77 - .../python_src/so2pg-postlinks.py | 62 - .../python_src/so2pg-posts.py | 113 - .../python_src/so2pg-tags.py | 63 - .../python_src/so2pg-users.py | 104 - .../python_src/so2pg-votes.py | 52 - .../scripts/backup-restore.sh | 20 - .../scripts/convert-xml-2-sql.sh | 25 - .../stackoverflow_in_pg/scripts/push-2-pg.sh | 22 - .../actions/stackoverflow_in_pg/so-create.sql | 94 - .../.github/ISSUE_TEMPLATE/bug_report.md | 38 - .../.github/ISSUE_TEMPLATE/feature_request.md | 20 - .../terraform-stats/.github/dependabot.yml | 10 - .../.github/workflows/test_action.yml | 49 - .github/actions/terraform-stats/.gitignore | 1 - .../terraform-stats/CODE_OF_CONDUCT.md | 128 - .github/actions/terraform-stats/LICENSE | 21 - .github/actions/terraform-stats/README.md | 110 - .github/actions/terraform-stats/action.yml | 108 - .../actions/terraform-stats/lib/tf_stats.sh | 79 - .../.devcontainer/Dockerfile | 4 - .../.devcontainer/devcontainer.json | 24 - .../.devcontainer/docker-compose.yml | 19 - .../.devops/adf-build-and-release.yml | 83 - .../.devops/adf-release-tasks.yml | 57 - .../.devops/db-restore.yml | 73 - .../.github/dependabot.yml | 12 - .../actions/terraform-templates/.gitignore | 36 - .../.scripts/data/psql_table_massive.sh | 35 - .../.scripts/data/psql_table_tiny.sh | 32 - .../.scripts/destroy_template.sh | 79 - .../.scripts/provision_template.sh | 203 - .../.scripts/utils/psql_install_16.sh | 8 - .../.scripts/utils/ssh_key_auth.sh | 32 - .../.scripts/utils/whats_running.sh | 1 - .github/actions/terraform-templates/README.md | 176 - .../azure/env/01/config.tf | 35 - .../terraform-templates/azure/env/01/data.tf | 10 - .../terraform-templates/azure/env/01/main.tf | 62 - .../azure/env/01/~locals.tf | 39 - .../azure/env/02/config.tf | 35 - .../terraform-templates/azure/env/02/data.tf | 15 - .../terraform-templates/azure/env/02/main.tf | 42 - .../azure/env/02/~locals.tf | 64 - .../azure/env/03/README.md | 142 - .../azure/env/03/config.tf | 35 - .../terraform-templates/azure/env/03/data.tf | 18 - .../terraform-templates/azure/env/03/main.tf | 104 - .../azure/env/03/~locals.tf | 102 - .../azure/env/05/config.tf | 31 - .../terraform-templates/azure/env/05/data.tf | 18 - .../terraform-templates/azure/env/05/main.tf | 41 - .../azure/env/05/~locals.tf | 41 - .../azure/env/06/config.tf | 27 - .../terraform-templates/azure/env/06/data.tf | 10 - .../terraform-templates/azure/env/06/main.tf | 27 - .../azure/env/06/~locals.tf | 35 - .../azure/env/07/config.tf | 27 - .../terraform-templates/azure/env/07/data.tf | 10 - .../terraform-templates/azure/env/07/main.tf | 31 - .../azure/env/07/~locals.tf | 42 - .../modules/api_management/api_default.tf | 83 - .../azure/modules/api_management/main.tf | 13 - .../azure/modules/api_management/~inputs.tf | 4 - .../azure/modules/api_management/~outputs.tf | 0 .../azure/modules/azure_ad/v1/main.tf | 131 - .../azure/modules/azure_ad/v1/~inputs.tf | 3 - .../azure/modules/azure_ad/v1/~outputs.tf | 3 - .../azure_ad/v2/files/FirstLogonCommands.xml | 22 - .../azure/modules/azure_ad/v2/files/winrm.ps1 | 21 - .../azure/modules/azure_ad/v2/main.tf | 93 - .../azure/modules/azure_ad/v2/~inputs.tf | 31 - .../azure/modules/azure_ad/v2/~outputs.tf | 12 - .../azure/modules/azure_mssql/v1/main.tf | 84 - .../azure/modules/azure_mssql/v1/~inputs.tf | 3 - .../azure/modules/azure_mssql/v1/~outputs.tf | 11 - .../modules/azure_mssql/v1/~providers.tf | 8 - .../azure/modules/azure_mssql/v2/databases.tf | 22 - .../modules/azure_mssql/v2/elastic_pool.tf | 22 - .../azure/modules/azure_mssql/v2/main.tf | 43 - .../azure/modules/azure_mssql/v2/~inputs.tf | 11 - .../azure/modules/azure_mssql/v2/~outputs.tf | 7 - .../azure/modules/cloudshell/main.tf | 162 - .../azure/modules/cloudshell/outputs.tf | 7 - .../azure/modules/cloudshell/variables.tf | 65 - .../azure/modules/container_app/main.tf | 59 - .../azure/modules/container_app/~inputs.tf | 3 - .../azure/modules/container_app/~outputs.tf | 0 .../azure/modules/container_instance/main.tf | 58 - .../modules/container_instance/~inputs.tf | 27 - .../modules/container_instance/~outputs.tf | 3 - .../modules/cosmosdb_postgresql/v1/main.tf | 9 - .../modules/cosmosdb_postgresql/v1/~inputs.tf | 5 - .../cosmosdb_postgresql/v1/~outputs.tf | 3 - .../azure/modules/data_factory/v1/main.tf | 85 - .../azure/modules/data_factory/v1/~inputs.tf | 4 - .../azure/modules/data_factory/v1/~outputs.tf | 0 .../azure/modules/data_factory/v2/main.tf | 17 - .../azure/modules/data_factory/v2/~inputs.tf | 3 - .../azure/modules/data_factory/v2/~outputs.tf | 0 .../azure/modules/dns_zone/main.tf | 14 - .../azure/modules/dns_zone/~inputs.tf | 7 - .../azure/modules/dns_zone/~outputs.tf | 3 - .../azure/modules/init/v1/main.tf | 18 - .../azure/modules/init/v1/~inputs.tf | 2 - .../azure/modules/init/v1/~outputs.tf | 3 - .../azure/modules/init/v2/main.tf | 7 - .../azure/modules/init/v2/~inputs.tf | 2 - .../azure/modules/init/v2/~outputs.tf | 0 .../azure/modules/key_vault/main.tf | 23 - .../azure/modules/key_vault/~inputs.tf | 3 - .../azure/modules/key_vault/~outputs.tf | 7 - .../azure/modules/logic_app/main.tf | 110 - .../modules/logic_app/workflows/workflow.json | 114 - .../azure/modules/logic_app/~inputs.tf | 5 - .../azure/modules/logic_app/~outputs.tf | 16 - .../azure/modules/logic_app/~providers.tf | 8 - .../azure/modules/mssql_vm/main.tf | 254 - .../azure/modules/mssql_vm/~inputs.tf | 12 - .../azure/modules/private_endpoint/main.tf | 18 - .../azure/modules/private_endpoint/~inputs.tf | 7 - .../modules/private_endpoint/~outputs.tf | 0 .../azure/modules/storage_account/v1/main.tf | 26 - .../modules/storage_account/v1/~inputs.tf | 2 - .../modules/storage_account/v1/~outputs.tf | 3 - .../azure/modules/storage_account/v2/main.tf | 15 - .../modules/storage_account/v2/~inputs.tf | 8 - .../modules/storage_account/v2/~outputs.tf | 3 - .../azure/modules/vnet/main.tf | 22 - .../azure/modules/vnet/~inputs.tf | 4 - .../azure/modules/vnet/~outputs.tf | 11 - .../.github/dependabot.yml | 11 - .../.github/workflows/test_action.yml | 27 - .../actions/workflow-housekeeper/README.md | 55 - .../actions/workflow-housekeeper/action.yml | 41 - .../workflow-housekeeper/lib/housekeeper.sh | 72 - 289 files changed, 17833 deletions(-) delete mode 100644 .github/actions/azviz-action/.github/workflows/sample_full_workflow.yml delete mode 100644 .github/actions/azviz-action/.github/workflows/sample_min_workflow.yml delete mode 100644 .github/actions/azviz-action/.github/workflows/test_linux_runner.yml delete mode 100644 .github/actions/azviz-action/.github/workflows/test_windows_runner.yml delete mode 100644 .github/actions/azviz-action/README.md delete mode 100644 .github/actions/azviz-action/action.yml delete mode 100644 .github/actions/azviz-action/viz_run.ps1 delete mode 100644 .github/actions/checksum-validate-action/.github/dependabot.yml delete mode 100644 .github/actions/checksum-validate-action/.github/workflows/test_action.yml delete mode 100644 .github/actions/checksum-validate-action/README.md delete mode 100644 .github/actions/checksum-validate-action/action.yml delete mode 100644 .github/actions/git-secrets/.gitattributes delete mode 100644 .github/actions/git-secrets/.github/PULL_REQUEST_TEMPLATE.md delete mode 100644 .github/actions/git-secrets/.pre-commit-hooks.yaml delete mode 100644 .github/actions/git-secrets/.travis.yml delete mode 100644 .github/actions/git-secrets/CHANGELOG.md delete mode 100644 .github/actions/git-secrets/CODE_OF_CONDUCT.md delete mode 100644 .github/actions/git-secrets/CONTRIBUTING.md delete mode 100644 .github/actions/git-secrets/LICENSE.txt delete mode 100644 .github/actions/git-secrets/Makefile delete mode 100644 .github/actions/git-secrets/NOTICE.txt delete mode 100644 .github/actions/git-secrets/README.rst delete mode 100755 .github/actions/git-secrets/git-secrets delete mode 100644 .github/actions/git-secrets/git-secrets.1 delete mode 100644 .github/actions/git-secrets/install.ps1 delete mode 100644 .github/actions/git-secrets/test/bats/LICENSE delete mode 100755 .github/actions/git-secrets/test/bats/bin/bats delete mode 100755 .github/actions/git-secrets/test/bats/libexec/bats delete mode 100755 .github/actions/git-secrets/test/bats/libexec/bats-exec-suite delete mode 100755 .github/actions/git-secrets/test/bats/libexec/bats-exec-test delete mode 100755 .github/actions/git-secrets/test/bats/libexec/bats-format-tap-stream delete mode 100755 .github/actions/git-secrets/test/bats/libexec/bats-preprocess delete mode 100644 .github/actions/git-secrets/test/commit-msg.bats delete mode 100644 .github/actions/git-secrets/test/git-secrets.bats delete mode 100644 .github/actions/git-secrets/test/pre-commit.bats delete mode 100644 .github/actions/git-secrets/test/prepare-commit-msg.bats delete mode 100644 .github/actions/git-secrets/test/test_helper.bash delete mode 100644 .github/actions/randomrepo/.github/workflows/main.yml delete mode 100644 .github/actions/randomrepo/Dockerfile delete mode 100644 .github/actions/randomrepo/README.md delete mode 100644 .github/actions/randomrepo/action.yml delete mode 100644 .github/actions/randomrepo/entrypoint.sh delete mode 100644 .github/actions/randomrepo/lib/install.sh delete mode 100644 .github/actions/rapid-wsl/.gitignore delete mode 100644 .github/actions/rapid-wsl/README.md delete mode 100644 .github/actions/rapid-wsl/defaults/demo.sh delete mode 100644 .github/actions/rapid-wsl/defaults/generic.sh delete mode 100644 .github/actions/rapid-wsl/defaults/macos.sh delete mode 100644 .github/actions/rapid-wsl/defaults/prime-data-ingestion.sh delete mode 100644 .github/actions/rapid-wsl/defaults/prime-reportstream.sh delete mode 100644 .github/actions/rapid-wsl/etc/sample.gitconfig delete mode 100644 .github/actions/rapid-wsl/lib/args.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/backup_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/destroy_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/docker_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/down_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/enter_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/fix_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/git_setup.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/gpg_import.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/init_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/install_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/restore_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/setup_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/status_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/test-pipe_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/test_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/tips_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/commands/up_distro.sh delete mode 100644 .github/actions/rapid-wsl/lib/controller.sh delete mode 100644 .github/actions/rapid-wsl/lib/waiting.sh delete mode 100644 .github/actions/rapid-wsl/modules/README.md delete mode 100644 .github/actions/rapid-wsl/modules/demo/install_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/generic/README.md delete mode 100644 .github/actions/rapid-wsl/modules/macos/install_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/macos/setup_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/prime-data-ingestion/README.md delete mode 100644 .github/actions/rapid-wsl/modules/prime-data-ingestion/install_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/prime-data-ingestion/setup_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/README.md delete mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/down_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/fix_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/install_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/setup_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/status_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/test-pipe_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/test_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/tips_distro.sh delete mode 100644 .github/actions/rapid-wsl/modules/prime-reportstream/up_distro.sh delete mode 100644 .github/actions/rapid-wsl/rwsl delete mode 100644 .github/actions/reliable-pull-request-action/.github/workflows/test-action.yml delete mode 100644 .github/actions/reliable-pull-request-action/LICENSE delete mode 100644 .github/actions/reliable-pull-request-action/README.md delete mode 100644 .github/actions/reliable-pull-request-action/action.yml delete mode 100644 .github/actions/reliable-pull-request-action/create-pr.sh delete mode 100644 .github/actions/remote-branch-action/.github/workflows/test-action.yml delete mode 100644 .github/actions/remote-branch-action/LICENSE delete mode 100644 .github/actions/remote-branch-action/README.md delete mode 100644 .github/actions/remote-branch-action/action.yml delete mode 100644 .github/actions/runleaks/.github/dependabot.yml delete mode 100644 .github/actions/runleaks/.github/runleaks/exclusions.txt delete mode 100644 .github/actions/runleaks/.github/runleaks/patterns.txt delete mode 100644 .github/actions/runleaks/.github/runleaks/patterns_force_failure.txt delete mode 100644 .github/actions/runleaks/.github/workflows/main.yml delete mode 100644 .github/actions/runleaks/.github/workflows/scan_public.yml delete mode 100644 .github/actions/runleaks/Dockerfile delete mode 100644 .github/actions/runleaks/LICENSE delete mode 100644 .github/actions/runleaks/README.md delete mode 100644 .github/actions/runleaks/action.yml delete mode 100644 .github/actions/runleaks/lib/scan.sh delete mode 100644 .github/actions/slack-boltjs-app/.dockerignore delete mode 100644 .github/actions/slack-boltjs-app/.env.example delete mode 100644 .github/actions/slack-boltjs-app/.eslintignore delete mode 100644 .github/actions/slack-boltjs-app/.eslintrc.cjs delete mode 100644 .github/actions/slack-boltjs-app/.github/dependabot.yml delete mode 100644 .github/actions/slack-boltjs-app/.github/workflows/codacy.yml delete mode 100644 .github/actions/slack-boltjs-app/.github/workflows/codeql.yml delete mode 100644 .github/actions/slack-boltjs-app/.github/workflows/confirm_push.yml delete mode 100644 .github/actions/slack-boltjs-app/.github/workflows/confirm_run.yml delete mode 100644 .github/actions/slack-boltjs-app/.github/workflows/dependency-review.yml delete mode 100644 .github/actions/slack-boltjs-app/.github/workflows/njsscan.yml delete mode 100644 .github/actions/slack-boltjs-app/.github/workflows/validate.yml delete mode 100644 .github/actions/slack-boltjs-app/.gitignore delete mode 100644 .github/actions/slack-boltjs-app/.glitch-assets delete mode 100644 .github/actions/slack-boltjs-app/.help.example delete mode 100644 .github/actions/slack-boltjs-app/Dockerfile.example delete mode 100644 .github/actions/slack-boltjs-app/Makefile delete mode 100644 .github/actions/slack-boltjs-app/README.md delete mode 100644 .github/actions/slack-boltjs-app/npm-shrinkwrap.json delete mode 100644 .github/actions/slack-boltjs-app/package.json delete mode 100644 .github/actions/slack-boltjs-app/src/index.js delete mode 100644 .github/actions/slack-boltjs-app/src/utils/github/default_branch.js delete mode 100644 .github/actions/slack-boltjs-app/src/utils/github/list_targets.js delete mode 100644 .github/actions/slack-boltjs-app/src/utils/github/lock_target.js delete mode 100644 .github/actions/slack-boltjs-app/src/utils/github/push.js delete mode 100644 .github/actions/slack-boltjs-app/src/utils/github/request.js delete mode 100644 .github/actions/slack-boltjs-app/src/utils/github/router.js delete mode 100644 .github/actions/slack-boltjs-app/src/utils/github/run_workflow.js delete mode 100644 .github/actions/slack-boltjs-app/src/views/app_home.js delete mode 100755 .github/actions/stackoverflow_in_pg/.gitignore delete mode 100644 .github/actions/stackoverflow_in_pg/README.md delete mode 100644 .github/actions/stackoverflow_in_pg/install_pg/install_centos.md delete mode 100644 .github/actions/stackoverflow_in_pg/install_pg/install_ubuntu.md delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/Badges.xml delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/Comments.xml delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/PostHistory.xml delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/PostLinks.xml delete mode 100755 .github/actions/stackoverflow_in_pg/python_src/sample_xml/Posts.xml delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/Tags.xml delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/Users.xml delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/sample_xml/Votes.xml delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-badges.py delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-comments.py delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-posthistory.py delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-postlinks.py delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-posts.py delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-tags.py delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-users.py delete mode 100644 .github/actions/stackoverflow_in_pg/python_src/so2pg-votes.py delete mode 100644 .github/actions/stackoverflow_in_pg/scripts/backup-restore.sh delete mode 100644 .github/actions/stackoverflow_in_pg/scripts/convert-xml-2-sql.sh delete mode 100644 .github/actions/stackoverflow_in_pg/scripts/push-2-pg.sh delete mode 100755 .github/actions/stackoverflow_in_pg/so-create.sql delete mode 100644 .github/actions/terraform-stats/.github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/actions/terraform-stats/.github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 .github/actions/terraform-stats/.github/dependabot.yml delete mode 100644 .github/actions/terraform-stats/.github/workflows/test_action.yml delete mode 100644 .github/actions/terraform-stats/.gitignore delete mode 100644 .github/actions/terraform-stats/CODE_OF_CONDUCT.md delete mode 100644 .github/actions/terraform-stats/LICENSE delete mode 100644 .github/actions/terraform-stats/README.md delete mode 100644 .github/actions/terraform-stats/action.yml delete mode 100644 .github/actions/terraform-stats/lib/tf_stats.sh delete mode 100644 .github/actions/terraform-templates/.devcontainer/Dockerfile delete mode 100644 .github/actions/terraform-templates/.devcontainer/devcontainer.json delete mode 100644 .github/actions/terraform-templates/.devcontainer/docker-compose.yml delete mode 100644 .github/actions/terraform-templates/.devops/adf-build-and-release.yml delete mode 100644 .github/actions/terraform-templates/.devops/adf-release-tasks.yml delete mode 100644 .github/actions/terraform-templates/.devops/db-restore.yml delete mode 100644 .github/actions/terraform-templates/.github/dependabot.yml delete mode 100644 .github/actions/terraform-templates/.gitignore delete mode 100755 .github/actions/terraform-templates/.scripts/data/psql_table_massive.sh delete mode 100755 .github/actions/terraform-templates/.scripts/data/psql_table_tiny.sh delete mode 100755 .github/actions/terraform-templates/.scripts/destroy_template.sh delete mode 100755 .github/actions/terraform-templates/.scripts/provision_template.sh delete mode 100755 .github/actions/terraform-templates/.scripts/utils/psql_install_16.sh delete mode 100755 .github/actions/terraform-templates/.scripts/utils/ssh_key_auth.sh delete mode 100755 .github/actions/terraform-templates/.scripts/utils/whats_running.sh delete mode 100644 .github/actions/terraform-templates/README.md delete mode 100644 .github/actions/terraform-templates/azure/env/01/config.tf delete mode 100644 .github/actions/terraform-templates/azure/env/01/data.tf delete mode 100644 .github/actions/terraform-templates/azure/env/01/main.tf delete mode 100644 .github/actions/terraform-templates/azure/env/01/~locals.tf delete mode 100644 .github/actions/terraform-templates/azure/env/02/config.tf delete mode 100644 .github/actions/terraform-templates/azure/env/02/data.tf delete mode 100644 .github/actions/terraform-templates/azure/env/02/main.tf delete mode 100644 .github/actions/terraform-templates/azure/env/02/~locals.tf delete mode 100644 .github/actions/terraform-templates/azure/env/03/README.md delete mode 100644 .github/actions/terraform-templates/azure/env/03/config.tf delete mode 100644 .github/actions/terraform-templates/azure/env/03/data.tf delete mode 100644 .github/actions/terraform-templates/azure/env/03/main.tf delete mode 100644 .github/actions/terraform-templates/azure/env/03/~locals.tf delete mode 100644 .github/actions/terraform-templates/azure/env/05/config.tf delete mode 100644 .github/actions/terraform-templates/azure/env/05/data.tf delete mode 100644 .github/actions/terraform-templates/azure/env/05/main.tf delete mode 100644 .github/actions/terraform-templates/azure/env/05/~locals.tf delete mode 100644 .github/actions/terraform-templates/azure/env/06/config.tf delete mode 100644 .github/actions/terraform-templates/azure/env/06/data.tf delete mode 100644 .github/actions/terraform-templates/azure/env/06/main.tf delete mode 100644 .github/actions/terraform-templates/azure/env/06/~locals.tf delete mode 100644 .github/actions/terraform-templates/azure/env/07/config.tf delete mode 100644 .github/actions/terraform-templates/azure/env/07/data.tf delete mode 100644 .github/actions/terraform-templates/azure/env/07/main.tf delete mode 100644 .github/actions/terraform-templates/azure/env/07/~locals.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/api_management/api_default.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/api_management/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/api_management/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/api_management/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v1/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v1/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v1/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v2/files/FirstLogonCommands.xml delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v2/files/winrm.ps1 delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v2/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v2/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_ad/v2/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v1/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v1/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v1/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v1/~providers.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v2/databases.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v2/elastic_pool.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v2/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v2/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/azure_mssql/v2/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/cloudshell/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/cloudshell/outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/cloudshell/variables.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/container_app/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/container_app/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/container_app/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/container_instance/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/container_instance/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/container_instance/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/data_factory/v1/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/data_factory/v1/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/data_factory/v1/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/data_factory/v2/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/data_factory/v2/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/data_factory/v2/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/dns_zone/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/dns_zone/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/dns_zone/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/init/v1/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/init/v1/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/init/v1/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/init/v2/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/init/v2/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/init/v2/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/key_vault/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/key_vault/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/key_vault/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/logic_app/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/logic_app/workflows/workflow.json delete mode 100644 .github/actions/terraform-templates/azure/modules/logic_app/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/logic_app/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/logic_app/~providers.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/mssql_vm/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/mssql_vm/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/private_endpoint/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/private_endpoint/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/private_endpoint/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/storage_account/v1/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/storage_account/v1/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/storage_account/v1/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/storage_account/v2/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/storage_account/v2/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/storage_account/v2/~outputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/vnet/main.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/vnet/~inputs.tf delete mode 100644 .github/actions/terraform-templates/azure/modules/vnet/~outputs.tf delete mode 100644 .github/actions/workflow-housekeeper/.github/dependabot.yml delete mode 100644 .github/actions/workflow-housekeeper/.github/workflows/test_action.yml delete mode 100644 .github/actions/workflow-housekeeper/README.md delete mode 100644 .github/actions/workflow-housekeeper/action.yml delete mode 100644 .github/actions/workflow-housekeeper/lib/housekeeper.sh diff --git a/.github/actions/azviz-action/.github/workflows/sample_full_workflow.yml b/.github/actions/azviz-action/.github/workflows/sample_full_workflow.yml deleted file mode 100644 index ef1d8d8d1a9..00000000000 --- a/.github/actions/azviz-action/.github/workflows/sample_full_workflow.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: Manually triggered full workflow -on: - workflow_dispatch: - inputs: - resource-group: - description: Comma-seperated resource group list - required: true - default: test1-rg,test2-rg - out-file: - description: Graph export path - required: true - default: output/viz.svg - sub-name: - description: Azure subscription name - required: true - default: Pay-As-You-Go - theme: - description: Graph theme (dark, light, neon) - required: true - default: neon - depth: - description: Level of Azure Resource Sub-category to be included in vizualization (1 or 2) - required: true - default: '1' - verbosity: - description: Level of information to included in vizualization (1 or 2) - required: true - default: '1' - format: - description: Graph format (png or svg) - required: true - default: svg - direction: - description: Direction in which resource groups are plotted on the visualization (left-to-right or top-to-bottom) - required: true - default: top-to-bottom - exclude-types: - description: Exclude resources via string search - required: true - default: '*excludethisthing1,excludethisthing2*' - splines: - description: Controls how edges appear in visualization. ('spline', 'polyline', 'curved', 'ortho', 'line') - required: true - default: spline - -jobs: - generate-viz: - runs-on: ubuntu-latest - steps: - - name: Login to Azure - uses: azure/login@v1 - with: - creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} - enable-AzPSSession: true - - uses: .github/actions/azviz-action - with: - resource-group: ${{ github.event.inputs.resource-group }} - out-file: ${{ github.event.inputs.out-file }} - sub-name: ${{ github.event.inputs.sub-name }} - theme: ${{ github.event.inputs.theme }} - depth: ${{ github.event.inputs.depth }} - verbosity: ${{ github.event.inputs.verbosity }} - format: ${{ github.event.inputs.format }} - direction: ${{ github.event.inputs.direction }} - exclude-types: ${{ github.event.inputs.exclude-types }} - splines: ${{ github.event.inputs.splines }} - - uses: actions/upload-artifact@v2 - with: - name: viz - path: output/* diff --git a/.github/actions/azviz-action/.github/workflows/sample_min_workflow.yml b/.github/actions/azviz-action/.github/workflows/sample_min_workflow.yml deleted file mode 100644 index 676388ff265..00000000000 --- a/.github/actions/azviz-action/.github/workflows/sample_min_workflow.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Manually triggered min workflow -on: - workflow_dispatch: - inputs: - resource-group: - description: Comma-seperated resource group list - required: true - default: test1-rg,test2-rg - out-file: - description: Graph export path - required: true - default: output/viz.svg - sub-name: - description: Azure subscription name - required: true - default: Pay-As-You-Go - -jobs: - generate-viz: - runs-on: ubuntu-latest - steps: - - name: Login to Azure - uses: azure/login@v1 - with: - creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} - enable-AzPSSession: true - - uses: .github/actions/azviz-action - with: - resource-group: ${{ github.event.inputs.resource-group }} - out-file: ${{ github.event.inputs.out-file }} - sub-name: ${{ github.event.inputs.sub-name }} - - uses: actions/upload-artifact@v2 - with: - name: viz - path: output/* diff --git a/.github/actions/azviz-action/.github/workflows/test_linux_runner.yml b/.github/actions/azviz-action/.github/workflows/test_linux_runner.yml deleted file mode 100644 index 3300a863551..00000000000 --- a/.github/actions/azviz-action/.github/workflows/test_linux_runner.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Linux runner -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - generate-viz: - runs-on: ubuntu-latest - steps: - - name: Login to Azure - uses: azure/login@v1 - with: - creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} - enable-AzPSSession: true - - name: Check out repository - uses: actions/checkout@v2 - - name: Use local my-action - uses: ./ - with: - resource-group: ${{ secrets.TEST_RG }} - out-file: output/viz.svg - sub-name: Pay-As-You-Go - format: svg - - uses: actions/upload-artifact@v2 - with: - name: viz - path: output/* diff --git a/.github/actions/azviz-action/.github/workflows/test_windows_runner.yml b/.github/actions/azviz-action/.github/workflows/test_windows_runner.yml deleted file mode 100644 index 865ac2fbfd8..00000000000 --- a/.github/actions/azviz-action/.github/workflows/test_windows_runner.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Windows runner -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - generate-viz: - runs-on: windows-latest - steps: - - name: Login to Azure - uses: azure/login@v1 - with: - creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} - enable-AzPSSession: true - - name: Check out repository - uses: actions/checkout@v2 - - name: Use local my-action - uses: ./ - with: - resource-group: ${{ secrets.TEST_RG }} - out-file: output/viz.svg - sub-name: Pay-As-You-Go - format: svg - - uses: actions/upload-artifact@v2 - with: - name: viz - path: output/* diff --git a/.github/actions/azviz-action/README.md b/.github/actions/azviz-action/README.md deleted file mode 100644 index c55796133a6..00000000000 --- a/.github/actions/azviz-action/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# AzViz (Azure Visualizer) action - - -[](https://github.com/CDCgov/azviz-action/actions/workflows/test_linux_runner.yml) -[](https://github.com/CDCgov/azviz-action/actions/workflows/test_windows_runner.yml) - -## ☕ Please donate to [AzViz Developer](https://github.com/PrateekKumarSingh/AzViz#readme) - - - -## Synopsis - -[AzViz](https://github.com/PrateekKumarSingh/AzViz) for [GitHub actions](https://github.com/marketplace?type=actions)! - -## Inputs - -### Required - -```yml -inputs: - resource-group: - description: Comma-seperated resource group list - required: true - out-file: - description: Graph export path - required: true - default: output/viz.svg - sub-name: - description: Azure subscription name - required: true - default: Pay-As-You-Go -``` - -### Optional - -```yml - theme: - description: Graph theme (dark, light, neon) - required: false - default: neon - depth: - description: Level of Azure Resource Sub-category to be included in vizualization (1 or 2) - required: false - default: '1' - verbosity: - description: Level of information to included in vizualization (1 or 2) - required: false - default: '1' - format: - description: Graph format (png or svg) - required: false - default: svg - direction: - description: Direction in which resource groups are plotted on the visualization (left-to-right or top-to-bottom) - required: false - default: top-to-bottom - exclude-types: - description: Exclude resources via string search - required: false - default: '*excludethisthing1,excludethisthing2*' - splines: - description: Controls how edges appear in visualization. ('spline', 'polyline', 'curved', 'ortho', 'line') - required: false - default: spline -``` - -## Quick start - -`sample_min_workflow.yml` -```yml -jobs: - generate-viz: - runs-on: ubuntu-latest - steps: - - name: Login to Azure - uses: azure/login@v1 - with: - creds: ${{ secrets.SERVICE_PRINCIPAL_CREDS }} - enable-AzPSSession: true - - uses: .github/actions/azviz-action - with: - resource-group: ${{ github.event.inputs.resource-group }} - out-file: ${{ github.event.inputs.out-file }} - sub-name: ${{ github.event.inputs.sub-name }} - - uses: actions/upload-artifact@v2 - with: - name: viz - path: output/* -``` - -## Dependencies - - * [azure/login](https://github.com/marketplace/actions/azure-login) with `enable-AzPSSession: true` diff --git a/.github/actions/azviz-action/action.yml b/.github/actions/azviz-action/action.yml deleted file mode 100644 index 0178f26fcaf..00000000000 --- a/.github/actions/azviz-action/action.yml +++ /dev/null @@ -1,83 +0,0 @@ -# action.yml -name: 'Generate Azure resource topology diagrams with AzViz (Azure Visualizer)' -description: 'Run AzViz against one or more Azure Resource Groups' -branding: - icon: 'download-cloud' - color: 'blue' -inputs: - resource-group: - description: Comma-seperated resource group list - required: true - out-file: - description: Graph export path - required: true - default: viz.svg - sub-name: - description: Azure subscription name - required: true - default: Pay-As-You-Go - theme: - description: Graph theme (dark, light, neon) - required: false - default: neon - depth: - description: Level of Azure Resource Sub-category to be included in vizualization (1 or 2) - required: false - default: '1' - verbosity: - description: Level of information to included in vizualization (1 or 2) - required: false - default: '1' - format: - description: Graph format (png or svg) - required: true - default: svg - direction: - description: Direction in which resource groups are plotted on the visualization (left-to-right or top-to-bottom) - required: false - default: top-to-bottom - exclude-types: - description: Exclude resources via string search - required: false - default: '*excludethisthing1,excludethisthing2*' - splines: - description: Controls how edges appear in visualization. ('spline', 'polyline', 'curved', 'ortho', 'line') - required: false - default: spline - -runs: - using: "composite" - steps: - - name: Choco install graphviz - if: runner.os == 'Windows' - uses: crazy-max/ghaction-chocolatey@v1 - with: - args: install graphviz - - name: Apt-get install graphviz - if: runner.os != 'Windows' - run: | - sudo apt-get update; - sudo apt-get install graphviz -y; - shell: bash - - name: 'Install AzViz module' - shell: pwsh - run: | - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; - Install-Module -Name AzViz -AllowClobber -Confirm:$False -Force; - Import-Module AzViz; - - name: Run AzViz - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - ${{ github.action_path }}/viz_run.ps1 ` - -RESOURCE_GROUP '${{ inputs.resource-group }}' ` - -OUT_FILE '${{ inputs.out-file }}' ` - -SUB_NAME '${{ inputs.sub-name }}' ` - -THEME '${{ inputs.theme }}' ` - -DEPTH ${{ inputs.depth }} ` - -VERBOSITY ${{ inputs.verbosity }} ` - -FORMAT '${{ inputs.format }}' ` - -DIRECTION '${{ inputs.direction }}' ` - -EXCLUDE_TYPES '${{ inputs.exclude-types }}' ` - -SPLINES '${{ inputs.splines }}' diff --git a/.github/actions/azviz-action/viz_run.ps1 b/.github/actions/azviz-action/viz_run.ps1 deleted file mode 100644 index 87071186628..00000000000 --- a/.github/actions/azviz-action/viz_run.ps1 +++ /dev/null @@ -1,57 +0,0 @@ -Param( - [Parameter(Mandatory)] - [String]$RESOURCE_GROUP, - [Parameter(Mandatory)] - [String]$OUT_FILE = 'viz.svg', - [Parameter(Mandatory)] - [String]$SUB_NAME = 'Pay-As-You-Go', - [Parameter(Mandatory)] - [String]$THEME = 'neon', - [Parameter(Mandatory)] - [String]$DEPTH = '1', - [Parameter(Mandatory)] - [String]$VERBOSITY = '1', - [Parameter(Mandatory)] - [String]$FORMAT = 'svg', - [Parameter(Mandatory)] - [String]$DIRECTION = 'top-to-bottom', - [String]$EXCLUDE_TYPES = '*excludethisthing1,excludethisthing2*', - [Parameter(Mandatory)] - [String]$SPLINES = 'spline' -) - -# Create missing directory paths for output -New-Item -ItemType File -Force -Path ${OUT_FILE} - -# Get current Azure context -$currentAzureContext = Get-AzContext; - -# Check If Azure context exists -if ($currentAzureContext.Tenant.TenantId) { - - # Set Azure subscription to match SUB_NAME - Set-AzContext -SubscriptionName ${SUB_NAME}; -}; - -# Run AzViz and export Azure diagram to location OUT_FILE -Export-AzViz ` - -ResourceGroup ${RESOURCE_GROUP}.Split(",") ` - -Theme ${THEME} ` - -OutputFormat ${FORMAT} ` - -CategoryDepth ${DEPTH} ` - -LabelVerbosity ${VERBOSITY} ` - -ExcludeTypes ${EXCLUDE_TYPES}.Split(",") ` - -Splines ${SPLINES} ` - -Direction ${DIRECTION} ` - -OutputFilePath ${OUT_FILE}; - -if (${FORMAT} -eq 'svg') { - - # Move svg embedded png to output directory - ((Get-Content -path ${OUT_FILE} -Raw) -replace '(?<=xlink:href\=").+?(?=icons)','') | Set-Content -Path ${OUT_FILE} - $ICON_PATH=$(Split-Path -Path ${OUT_FILE})+'/icons/' - Write-Host "Moving ${HOME}/*/AzViz/* icons to ${ICON_PATH}" - New-Item -ItemType Directory -Force -Path ${ICON_PATH} - Get-Childitem -Path ${HOME} -Force -recurse -include *.png -ErrorAction SilentlyContinue | Move-Item -dest ${ICON_PATH} -Force - -}; \ No newline at end of file diff --git a/.github/actions/checksum-validate-action/.github/dependabot.yml b/.github/actions/checksum-validate-action/.github/dependabot.yml deleted file mode 100644 index b2f16c9faef..00000000000 --- a/.github/actions/checksum-validate-action/.github/dependabot.yml +++ /dev/null @@ -1,13 +0,0 @@ -# Dependabot general documentation: -# https://docs.github.com/en/code-security/dependabot -# Please see the documentation for all configuration options: -# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file - -version: 2 -updates: - # GitHub Actions workflows - - package-ecosystem: "github-actions" - # Workflow files stored in `.github/workflows` - directory: "/" - schedule: - interval: "daily" diff --git a/.github/actions/checksum-validate-action/.github/workflows/test_action.yml b/.github/actions/checksum-validate-action/.github/workflows/test_action.yml deleted file mode 100644 index 078b0964c09..00000000000 --- a/.github/actions/checksum-validate-action/.github/workflows/test_action.yml +++ /dev/null @@ -1,119 +0,0 @@ -name: Test Action - -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - generate-checksums: - name: Test generate checksum on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest] - steps: - - uses: actions/checkout@v4.1.1 - - - name: Generate checksum of string - uses: ./ - with: - key: test string - input: hello world - - - name: Generate checksum of command output - uses: ./ - with: - key: test command - input: $(cat action.yml) - - validate-checksums: - name: Test validate checksum on ${{ matrix.os }} - needs: - - generate-checksums - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest] - steps: - - uses: actions/checkout@v4.1.1 - - - name: Validate checksum of valid string - id: valid-string - uses: ./ - with: - key: test string - validate: true - fail-invalid: true - input: hello world - - name: Fail if output of valid string is wrong - if: steps.valid-string.outputs.valid != 'true' - run: exit 1 - - - name: Validate checksum of INVALID string - id: invalid-string - uses: ./ - with: - key: test string - validate: true - fail-invalid: false - input: hello world! - - name: Fail if output of INVALID string is wrong - if: steps.invalid-string.outputs.valid != 'false' - run: exit 1 - - - name: Validate checksum of valid command output - id: valid-command - uses: ./ - with: - key: test command - validate: true - fail-invalid: true - input: $(cat action.yml) - - name: Fail if output of valid command is wrong - if: steps.valid-command.outputs.valid != 'true' - run: exit 1 - - - name: Validate checksum of INVALID command output - id: invalid-command - uses: ./ - with: - key: test command - validate: true - fail-invalid: false - input: $(cat README.md) - - name: Fail if output of INVALID command is wrong - if: steps.invalid-command.outputs.valid != 'false' - run: exit 1 - - validate-checksum-failures: - name: Test checksum failures on ${{ matrix.os }} - needs: - - generate-checksums - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest] - steps: - - uses: actions/checkout@v4.1.1 - - - name: Validate checksum of INVALID string - continue-on-error: true - uses: ./ - with: - key: test string - validate: true - fail-invalid: true - input: hello world! - - - name: Validate checksum of INVALID command output - continue-on-error: true - uses: ./ - with: - key: test command - validate: true - fail-invalid: true - input: $(cat README.md) diff --git a/.github/actions/checksum-validate-action/README.md b/.github/actions/checksum-validate-action/README.md deleted file mode 100644 index 6d1a2d9a2d8..00000000000 --- a/.github/actions/checksum-validate-action/README.md +++ /dev/null @@ -1,94 +0,0 @@ -# Checksum Validate Action - -[](https://github.com/CDCgov/checksum-validate-action/actions/workflows/test_action.yml) - -## Synopsis - -1. Generate a checksum from either a string or shell command (use command substitution: `$()`). -2. Validate if checksum is identical to input (even across multiple jobs), using a `key` to link the validation attempt with the correct generated checksum. - * Validation is possible across jobs since the checksum is uploaded as a workflow artifact - -## Usage - -```yml -jobs: - generate-checksums: - name: Generate checksum - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4.1.1 - - - name: Generate checksum of string - uses: .github/actions/checksum-validate-action - with: - key: test string - input: hello world - - - name: Generate checksum of command output - uses: .github/actions/checksum-validate-action - with: - key: test command - input: $(cat action.yml) - - validate-checksums: - name: Validate checksum - needs: - - generate-checksums - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4.1.1 - - - name: Validate checksum of valid string - id: valid-string - uses: .github/actions/checksum-validate-action - with: - key: test string - validate: true - fail-invalid: true - input: hello world - - - name: Validate checksum of valid command output - id: valid-command - uses: .github/actions/checksum-validate-action - with: - key: test command - validate: true - fail-invalid: true - input: $(cat action.yml) - - - name: Get outputs - run: | - echo ${{ steps.valid-string.outputs.valid }} - echo ${{ steps.valid-command.outputs.valid }} -``` - -## Workflow summary - -### ✅ test string checksum valid ✅ - -### ❌ test string checksum INVALID ❌ - -## Inputs - -```yml -inputs: - validate: - description: Check if checksums match - default: false - key: - description: String to keep unique checksums separate - required: true - fail-invalid: - description: Fail step if invalid checksum - default: false - input: - description: String or command for checksum - required: true -``` - -## Outputs -```yml -outputs: - valid: - description: True if checksums match -``` diff --git a/.github/actions/checksum-validate-action/action.yml b/.github/actions/checksum-validate-action/action.yml deleted file mode 100644 index 1ad3023476e..00000000000 --- a/.github/actions/checksum-validate-action/action.yml +++ /dev/null @@ -1,111 +0,0 @@ -# action.yml -name: Checksum Validate Action -description: Generate and validate checksums -branding: - icon: 'lock' - color: 'orange' -inputs: - validate: - description: Check if checksums match - default: false - key: - description: String to keep unique checksums separate - required: true - fail-invalid: - description: Fail step if invalid checksum - default: false - input: - description: String or command for checksum - required: true -outputs: - valid: - description: True if checksums match - value: ${{ steps.validate_checksum.outputs.valid }} - -runs: - using: "composite" - steps: - - # CHECKSUM START - - name: Generate SHA - uses: nick-fields/retry@v3.0.0 - with: - max_attempts: 5 - retry_on: any - timeout_seconds: 10 - retry_wait_seconds: 15 - command: | - function fail { - printf '%s\n' "$1" >&2 - exit "${2-1}" - } - input_cmd="${{ inputs.input }}" || fail - sha="$(echo "$input_cmd" | sha256sum)" - echo "sha=$sha" >> $GITHUB_ENV - echo "success=true" >> $GITHUB_ENV - - - name: Get input SHA - if: env.success - id: input_sha - shell: bash - run: echo "sha=${{ env.sha }}" >> $GITHUB_OUTPUT - - - name: Get input SHA - if: env.success != 'true' - shell: bash - run: | - echo "failed to generate sha" - exit 1 - # CHECKSUM END - - # UPLOAD FILE START - - name: Create checksum file - if: inputs.validate != 'true' - shell: bash - run: | - echo "${{ steps.input_sha.outputs.sha }}" > "${{ github.sha }}-${{ inputs.key }}.txt" - - - name: Upload checksum file - if: inputs.validate != 'true' - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 - with: - name: "${{ github.sha }}-${{ inputs.key }}.txt" - path: "${{ github.sha }}-${{ inputs.key }}.txt" - retention-days: 5 - # UPLOAD FILE END - - # VALIDATE FILE START - - name: Download checksum file - if: inputs.validate == 'true' - uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe - with: - name: "${{ github.sha }}-${{ inputs.key }}.txt" - - - name: Validate pre and post checksums - if: inputs.validate == 'true' - id: validate_checksum - shell: bash - run: | - echo "${{ steps.input_sha.outputs.sha }}" > "${{ github.sha }}-${{ inputs.key }}-2.txt" - DIFF=$(diff -q "${{ github.sha }}-${{ inputs.key }}-2.txt" "${{ github.sha }}-${{ inputs.key }}.txt") || true - codevalid=true - if [ "$DIFF" != "" ] - then - codevalid=false - fi - echo "valid=$codevalid" >> $GITHUB_OUTPUT - - - name: Create summary - if: inputs.validate == 'true' - run: | - # Use ternary operator to assign emoji based on validity - emoji=${{ steps.validate_checksum.outputs.valid == 'true' && '✅' || '❌' }} - valid=${{ steps.validate_checksum.outputs.valid == 'true' && 'valid' || 'INVALID' }} - echo "### $emoji ${{ inputs.key }} checksum $valid $emoji" >> $GITHUB_STEP_SUMMARY - shell: bash - # VALIDATE FILE END - - - name: Fail if invalid checksum - if: inputs.validate == 'true' && steps.validate_checksum.outputs.valid == 'false' && inputs.fail-invalid == 'true' - run: exit 1 - shell: bash diff --git a/.github/actions/git-secrets/.gitattributes b/.github/actions/git-secrets/.gitattributes deleted file mode 100644 index 107aad84b10..00000000000 --- a/.github/actions/git-secrets/.gitattributes +++ /dev/null @@ -1,10 +0,0 @@ -# Set the default behavior, in case people don't have core.autocrlf set. -* text=auto - -# Force the bash scripts to be checked out with LF line endings. -git-secrets text eol=lf -git-secrets.1 text eol=lf -test/bats/bin/* text eol=lf -test/bats/libexec/* text eol=lf -*.bats text eol=lf -*.bash text eol=lf \ No newline at end of file diff --git a/.github/actions/git-secrets/.github/PULL_REQUEST_TEMPLATE.md b/.github/actions/git-secrets/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index ab40d21d778..00000000000 --- a/.github/actions/git-secrets/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,6 +0,0 @@ -*Issue #, if available:* - -*Description of changes:* - - -By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. diff --git a/.github/actions/git-secrets/.pre-commit-hooks.yaml b/.github/actions/git-secrets/.pre-commit-hooks.yaml deleted file mode 100644 index d313836246f..00000000000 --- a/.github/actions/git-secrets/.pre-commit-hooks.yaml +++ /dev/null @@ -1,5 +0,0 @@ -- id: git-secrets - name: Git Secrets - description: git-secrets scans commits, commit messages, and --no-ff merges to prevent adding secrets into your git repositories. - entry: 'git-secrets --pre_commit_hook' - language: script diff --git a/.github/actions/git-secrets/.travis.yml b/.github/actions/git-secrets/.travis.yml deleted file mode 100644 index 259379892db..00000000000 --- a/.github/actions/git-secrets/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: bash - -before_install: -- git config --global user.email "you@example.com" -- git config --global user.name "Your Name" - -script: -- make test diff --git a/.github/actions/git-secrets/CHANGELOG.md b/.github/actions/git-secrets/CHANGELOG.md deleted file mode 100644 index cfcae4e818c..00000000000 --- a/.github/actions/git-secrets/CHANGELOG.md +++ /dev/null @@ -1,49 +0,0 @@ -# CHANGELOG - -## 1.3.0 - 2019-02-10 - -* Empty provider output is now excluded - (https://github.com/awslabs/git-secrets/issues/34) -* Spaces are now supported in git exec path, making more Windows - paths execute properly. -* Patterns with newlines and carriage returns are now loaded properly. -* Patterns that contain only "\n" are now ignored. -* Various Bash 4 fixes (https://github.com/awslabs/git-secrets/issues/66). -* Make IAM key scanning much more targeted. - -## 1.2.1 - 2016-06-27 - -* Fixed an issue where secret provider commands were causing "command not - found" errors due to a previously set IFS variable. - https://github.com/awslabs/git-secrets/pull/30 - -## 1.2.0 - 2016-05-23 - -* Fixed an issue where spaces files with spaces in their names were not being - properly scanned in the pre-commit hook. -* Now ignoring empty lines and comments (e.g., `#`) in the .gitallowed file. -* Fixed an issue where numbers were being compared to strings causing failures - on some platforms. - -## 1.1.0 - 2016-04-06 - -* Bug fix: the pre-commit hook previously only scanned the working directory - rather than staged files. This release updates the pre-commit hook to instead - scan staged files so that git-secrets will detect violations if the working - directory drifts from the staging directory. -* Added the `--scan-history` subcommand so that you can scan your entire - git history for violations. -* Added the ability to filter false positives by using a .gitallowed file. -* Added support for `--cached`, `--no-index`, and `--untracked` to the `--scan` - subcommand. - -## 1.0.1 - 2016-01-11 - -* Now works correctly with filenames in a repository that contain spaces when - executing `git secrets --scan` with no provided filename (via `git grep`). -* Now works with git repositories with hundreds of thousands of files when - using `git secrets --scan` with no provided filename (via `git grep`). - -## 1.0.0 - 2015-12-10 - -* Initial release of ``git-secrets``. diff --git a/.github/actions/git-secrets/CODE_OF_CONDUCT.md b/.github/actions/git-secrets/CODE_OF_CONDUCT.md deleted file mode 100644 index 3b64466870c..00000000000 --- a/.github/actions/git-secrets/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,4 +0,0 @@ -## Code of Conduct -This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). -For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact -opensource-codeofconduct@amazon.com with any additional questions or comments. diff --git a/.github/actions/git-secrets/CONTRIBUTING.md b/.github/actions/git-secrets/CONTRIBUTING.md deleted file mode 100644 index de6d3d38075..00000000000 --- a/.github/actions/git-secrets/CONTRIBUTING.md +++ /dev/null @@ -1,61 +0,0 @@ -# Contributing Guidelines - -Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional -documentation, we greatly value feedback and contributions from our community. - -Please read through this document before submitting any issues or pull requests to ensure we have all the necessary -information to effectively respond to your bug report or contribution. - - -## Reporting Bugs/Feature Requests - -We welcome you to use the GitHub issue tracker to report bugs or suggest features. - -When filing an issue, please check [existing open](https://github.com/awslabs/git-secrets/issues), or [recently closed](https://github.com/awslabs/git-secrets/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already -reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: - -* A reproducible test case or series of steps -* The version of our code being used -* Any modifications you've made relevant to the bug -* Anything unusual about your environment or deployment - - -## Contributing via Pull Requests -Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: - -1. You are working against the latest source on the *master* branch. -2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. -3. You open an issue to discuss any significant work - we would hate for your time to be wasted. - -To send us a pull request, please: - -1. Fork the repository. -2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. -3. Ensure local tests pass. -4. Commit to your fork using clear commit messages. -5. Send us a pull request, answering any default questions in the pull request interface. -6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. - -GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and -[creating a pull request](https://help.github.com/articles/creating-a-pull-request/). - - -## Finding contributions to work on -Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/git-secrets/labels/help%20wanted) issues is a great place to start. - - -## Code of Conduct -This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). -For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact -opensource-codeofconduct@amazon.com with any additional questions or comments. - - -## Security issue notifications -If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. - - -## Licensing - -See the [LICENSE](https://github.com/awslabs/git-secrets/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. - -We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. diff --git a/.github/actions/git-secrets/LICENSE.txt b/.github/actions/git-secrets/LICENSE.txt deleted file mode 100644 index de96b9473c9..00000000000 --- a/.github/actions/git-secrets/LICENSE.txt +++ /dev/null @@ -1,208 +0,0 @@ - - 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 [yyyy] [name of copyright owner] - - 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. - -Note: Other license terms may apply to certain, identified software files -contained within or distributed with the accompanying software if such terms -are included in the directory containing the accompanying software. Such other -license terms will then apply in lieu of the terms of the software license -above. diff --git a/.github/actions/git-secrets/Makefile b/.github/actions/git-secrets/Makefile deleted file mode 100644 index a67eee2dd23..00000000000 --- a/.github/actions/git-secrets/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -PREFIX ?= /usr/local -MANPREFIX ?= "${PREFIX}/share/man/man1" - -help: - @echo "Please use \`make <target>' where <target> is one of" - @echo " test to perform unit tests." - @echo " man to build the man file from README.rst" - @echo " install to install. Use PREFIX and MANPREFIX to customize." - -# We use bats for testing: https://github.com/sstephenson/bats -test: - LANG=C test/bats/bin/bats test/ - -# The man page is completely derived from README.rst. Edits to -# README.rst require a rebuild of the man page. -man: - rst2man.py README.rst > git-secrets.1 - -install: - @mkdir -p ${DESTDIR}${MANPREFIX} - @mkdir -p ${DESTDIR}${PREFIX}/bin - @cp -f git-secrets ${DESTDIR}${PREFIX}/bin - @cp -f git-secrets.1 ${DESTDIR}${MANPREFIX} - -.PHONY: help test man diff --git a/.github/actions/git-secrets/NOTICE.txt b/.github/actions/git-secrets/NOTICE.txt deleted file mode 100644 index a5e5da9ba01..00000000000 --- a/.github/actions/git-secrets/NOTICE.txt +++ /dev/null @@ -1,6 +0,0 @@ -git-secrets -Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. - -bats -This product bundles bats, which is available under a "MIT" license. -For details, see test/bats. diff --git a/.github/actions/git-secrets/README.rst b/.github/actions/git-secrets/README.rst deleted file mode 100644 index 1be1691ab8a..00000000000 --- a/.github/actions/git-secrets/README.rst +++ /dev/null @@ -1,565 +0,0 @@ -=========== -git-secrets -=========== - -------------------------------------------------------------------------------------------- -Prevent committing AWS , AZURE and GCP sensitive creds to a git repository. -------------------------------------------------------------------------------------------- - -.. contents:: :depth: 2 - -Synopsis --------- - -:: - - git secrets --scan [-r|--recursive] [--cached] [--no-index] [--untracked] [<files>...] - git secrets --scan-history - git secrets --install [-f|--force] [<target-directory>] - git secrets --list [--global] - git secrets --add [-a|--allowed] [-l|--literal] [--global] <pattern> - git secrets --add-provider [--global] <command> [arguments...] - git secrets --register-aws [--global] - git secrets --register-gcp [--global] - git secrets --register-azure [--global] - git secrets --aws-provider [<credentials-file>] - - -Description ------------ - -``git-secrets`` scans commits, commit messages, and ``--no-ff`` merges to -prevent adding secrets into your git repositories. If a commit, -commit message, or any commit in a ``--no-ff`` merge history matches one of -your configured prohibited regular expression patterns, then the commit is -rejected. - - -Installing git-secrets ----------------------- - -``git-secrets`` must be placed somewhere in your PATH so that it is picked up -by ``git`` when running ``git secrets``. - -\*nix (Linux/macOS) -~~~~~~~~~~~~~~~~~~~ - -You can use the ``install`` target of the provided Makefile to install ``git secrets`` and the man page. -You can customize the install path using the PREFIX and MANPREFIX variables. - -:: - - make install - -Windows -~~~~~~~ - -Run the provided ``install.ps1`` powershell script. This will copy the needed files -to an installation directory (``%USERPROFILE%/.git-secrets`` by default) and add -the directory to the current user ``PATH``. - -:: - - PS > ./install.ps1 - -Homebrew (for macOS users) -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:: - - brew install git-secrets - -.. warning:: - - You're not done yet! You MUST install the git hooks for every repo that - you wish to use with ``git secrets --install``. - -Here's a quick example of how to ensure a git repository is scanned for secrets -on each commit:: - - cd /path/to/my/repo - git secrets --install - git secrets --register-aws - git secrets --register-azure - git secrets --register-gcp - - -Advanced configuration ----------------------- - -Add a configuration template if you want to add hooks to all repositories you -initialize or clone in the future. - -:: - - git secrets --register-(aws/azure/gcp) --global - - -Add hooks to all your local repositories. - -:: - - git secrets --install ~/.git-templates/git-secrets - git config --global init.templateDir ~/.git-templates/git-secrets - - -Add custom providers to scan for security credentials. - -:: - - git secrets --add-provider -- cat /path/to/secret/file/patterns - - -Before making public a repository ---------------------------------- - -With git-secrets is also possible to scan a repository including all revisions: - -:: - - git secrets --scan-history - - -Options -------- - -Operation Modes -~~~~~~~~~~~~~~~ - -Each of these options must appear first on the command line. - -``--install`` - Installs git hooks for a repository. Once the hooks are installed for a git - repository, commits and non-fast-forward merges for that repository will be prevented - from committing secrets. - -``--scan`` - Scans one or more files for secrets. When a file contains a secret, the - matched text from the file being scanned will be written to stdout and the - script will exit with a non-zero status. Each matched line will be written with - the name of the file that matched, a colon, the line number that matched, - a colon, and then the line of text that matched. If no files are provided, - all files returned by ``git ls-files`` are scanned. - -``--scan-history`` - Scans repository including all revisions. When a file contains a secret, the - matched text from the file being scanned will be written to stdout and the - script will exit with a non-zero status. Each matched line will be written with - the name of the file that matched, a colon, the line number that matched, - a colon, and then the line of text that matched. - -``--list`` - Lists the ``git-secrets`` configuration for the current repo or in the global - git config. - -``--add`` - Adds a prohibited or allowed pattern. - -``--add-provider`` - Registers a secret provider. Secret providers are executables that when - invoked output prohibited patterns that ``git-secrets`` should treat as - prohibited. - -``--register-aws`` - Adds common AWS patterns to the git config and ensures that keys present - in ``~/.aws/credentials`` are not found in any commit. The following - checks are added: - - - AWS Access Key IDs via ``(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}`` - - AWS Secret Access Key assignments via ":" or "=" surrounded by optional - quotes - - AWS account ID assignments via ":" or "=" surrounded by optional quotes - - Allowed patterns for example AWS keys (``AKIAIOSFODNN7EXAMPLE`` and - ``wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY``) - - Known credentials from ``~/.aws/credentials`` - - .. note:: - - While the patterns registered by this command should catch most - instances of AWS credentials, these patterns are **not** guaranteed to - catch them **all**. ``git-secrets`` should be used as an extra means of - insurance -- you still need to do your due diligence to ensure that you - do not commit credentials to a repository. - -``--register-gcp`` - Secret provider which scans files for Google Cloud Platform's (GCP's) crentials JSON files. - -``--register-azure`` - Secret provider which scans files for AZURE credentials - - -``--aws-provider`` - Secret provider that outputs credentials found in an INI file. You can - optionally provide the path to an INI file. - - -Options for ``--install`` -~~~~~~~~~~~~~~~~~~~~~~~~~ - -``-f, --force`` - Overwrites existing hooks if present. - -``<target-directory>`` - When provided, installs git hooks to the given directory. The current - directory is assumed if ``<target-directory>`` is not provided. - - If the provided ``<target-directory>`` is not in a git repository, the - directory will be created and hooks will be placed in - ``<target-directory>/hooks``. This can be useful for creating git template - directories using with ``git init --template <target-directory>``. - - You can run ``git init`` on a repository that has already been initialized. - From the `git init documentation <https://git-scm.com/docs/git-init>`_: - - From the git documentation: Running ``git init`` in an existing repository - is safe. It will not overwrite things that are already there. The - primary reason for rerunning ``git init`` is to pick up newly added - templates (or to move the repository to another place if - ``--separate-git-dir`` is given). - - The following git hooks are installed: - - 1. ``pre-commit``: Used to check if any of the files changed in the commit - use prohibited patterns. - 2. ``commit-msg``: Used to determine if a commit message contains a - prohibited patterns. - 3. ``prepare-commit-msg``: Used to determine if a merge commit will - introduce a history that contains a prohibited pattern at any point. - Please note that this hook is only invoked for non fast-forward merges. - - .. note:: - - Git only allows a single script to be executed per hook. If the - repository contains Debian-style subdirectories like ``pre-commit.d`` - and ``commit-msg.d``, then the git hooks will be installed into these - directories, which assumes that you've configured the corresponding - hooks to execute all of the scripts found in these directories. If - these git subdirectories are not present, then the git hooks will be - installed to the git repo's ``.git/hooks`` directory. - - -Examples -^^^^^^^^ - -Install git hooks to the current directory:: - - cd /path/to/my/repository - git secrets --install - -Install git hooks to a repository other than the current directory:: - - git secrets --install /path/to/my/repository - -Create a git template that has ``git-secrets`` installed, and then copy that -template into a git repository:: - - git secrets --install ~/.git-templates/git-secrets - git init --template ~/.git-templates/git-secrets - -Overwrite existing hooks if present:: - - git secrets --install -f - - -Options for ``--scan`` -~~~~~~~~~~~~~~~~~~~~~~ - -``-r, --recursive`` - Scans the given files recursively. If a directory is encountered, the - directory will be scanned. If ``-r`` is not provided, directories will be - ignored. - - ``-r`` cannot be used alongside ``--cached``, ``--no-index``, or - ``--untracked``. - -``--cached`` - Searches blobs registered in the index file. - -``--no-index`` - Searches files in the current directory that is not managed by git. - -``--untracked`` - In addition to searching in the tracked files in the working tree, - ``--scan`` also in untracked files. - -``<files>...`` - The path to one or more files on disk to scan for secrets. - - If no files are provided, all files returned by ``git ls-files`` are - scanned. - - -Examples -^^^^^^^^ - -Scan all files in the repo:: - - git secrets --scan - -Scans a single file for secrets:: - - git secrets --scan /path/to/file - -Scans a directory recursively for secrets:: - - git secrets --scan -r /path/to/directory - -Scans multiple files for secrets:: - - git secrets --scan /path/to/file /path/to/other/file - -You can scan by globbing:: - - git secrets --scan /path/to/directory/* - -Scan from stdin:: - - echo 'hello!' | git secrets --scan - - - -Options for ``--list`` -~~~~~~~~~~~~~~~~~~~~~~ - -``--global`` - Lists only git-secrets configuration in the global git config. - - -Options for ``--add`` -~~~~~~~~~~~~~~~~~~~~~ - -``--global`` - Adds patterns to the global git config - -``-l, --literal`` - Escapes special regular expression characters in the provided pattern so - that the pattern is searched for literally. - -``-a, --allowed`` - Mark the pattern as allowed instead of prohibited. Allowed patterns are - used to filter our false positives. - -``<pattern>`` - The regex pattern to search. - - -Examples -^^^^^^^^ - -Adds a prohibited pattern to the current repo:: - - git secrets --add '[A-Z0-9]{20}' - -Adds a prohibited pattern to the global git config:: - - git secrets --add --global '[A-Z0-9]{20}' - -Adds a string that is scanned for literally (``+`` is escaped):: - - git secrets --add --literal 'foo+bar' - -Add an allowed pattern:: - - git secrets --add -a 'allowed pattern' - - -Options for ``--register-aws`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``--global`` - Adds AWS specific configuration variables to the global git config. - -Options for ``--register-gcp`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``--global`` - Adds GCP specific configuration variables to the global git config. - -Options for ``--register-azure`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``--global`` - Adds AZURE specific configuration variables to the global git config. - -Options for ``--aws-provider`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``[<credentials-file>]`` - If provided, specifies the custom path to an INI file to scan. If not - provided, ``~/.aws/credentials`` is assumed. - - -Options for ``--add-provider`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``--global`` - Adds the provider to the global git config. - -``<command>`` - Provider command to invoke. When invoked the command is expected to write - prohibited patterns separated by new lines to stdout. Any extra arguments - provided are passed on to the command. - - -Examples -^^^^^^^^ - -Registers a secret provider with arguments:: - - git secrets --add-provider -- git secrets --aws-provider - -Cats secrets out of a file:: - - git secrets --add-provider -- cat /path/to/secret/file/patterns - - -Defining prohibited patterns ----------------------------- - -``egrep``-compatible regular expressions are used to determine if a commit or -commit message contains any prohibited patterns. These regular expressions are -defined using the ``git config`` command. It is important to note that -different systems use different versions of egrep. For example, when running on -macOS, you will use a different version of ``egrep`` than when running on something -like Ubuntu (BSD vs GNU). - -You can add prohibited regular expression patterns to your git config using -``git secrets --add <pattern>``. - - -Ignoring false positives ------------------------- - -Sometimes a regular expression might match false positives. For example, git -commit SHAs look a lot like AWS access keys. You can specify many different -regular expression patterns as false positives using the following command: - -:: - - git secrets --add --allowed 'my regex pattern' - -You can also add regular expressions patterns to filter false positives to a -``.gitallowed`` file located in the repository's root directory. Lines starting -with ``#`` are skipped (comment line) and empty lines are also skipped. - -First, git-secrets will extract all lines from a file that contain a prohibited -match. Included in the matched results will be the full path to the name of -the file that was matched, followed by ':', followed by the line number that was -matched, followed by the entire line from the file that was matched by a secret -pattern. Then, if you've defined allowed regular expressions, git-secrets will -check to see if all of the matched lines match at least one of your registered -allowed regular expressions. If all of the lines that were flagged as secret -are canceled out by an allowed match, then the subject text does not contain -any secrets. If any of the matched lines are not matched by an allowed regular -expression, then git-secrets will fail the commit/merge/message. - -.. important:: - - Just as it is a bad practice to add prohibited patterns that are too - greedy, it is also a bad practice to add allowed patterns that are too - forgiving. Be sure to test out your patterns using ad-hoc calls to - ``git secrets --scan $filename`` to ensure they are working as intended. - - -Secret providers ----------------- - -Sometimes you want to check for an exact pattern match against a set of known -secrets. For example, you might want to ensure that no credentials present in -``~/.aws/credentials`` ever show up in a commit. In these cases, it's better to -leave these secrets in one location rather than spread them out across git -repositories in git configs. You can use "secret providers" to fetch these -types of credentials. A secret provider is an executable that when invoked -outputs prohibited patterns separated by new lines. - -You can add secret providers using the ``--add-provider`` command:: - - git secrets --add-provider -- git secrets --aws-provider - -Notice the use of ``--``. This ensures that any arguments associated with the -provider are passed to the provider each time it is invoked when scanning -for secrets. - - -Example walkthrough -------------------- - -Let's take a look at an example. Given the following subject text (stored in -``/tmp/example``):: - - This is a test! - password=ex@mplepassword - password=****** - More test... - -And the following registered patterns: - -:: - - git secrets --add 'password\s*=\s*.+' - git secrets --add --allowed --literal 'ex@mplepassword' - -Running ``git secrets --scan /tmp/example``, the result will -result in the following error output:: - - /tmp/example:3:password=****** - - [ERROR] Matched prohibited pattern - - Possible mitigations: - - Mark false positives as allowed using: git config --add secrets.allowed ... - - List your configured patterns: git config --get-all secrets.patterns - - List your configured allowed patterns: git config --get-all secrets.allowed - - Use --no-verify if this is a one-time false positive - -Breaking this down, the prohibited pattern value of ``password\s*=\s*.+`` will -match the following lines:: - - /tmp/example:2:password=ex@mplepassword - /tmp/example:3:password=****** - -...But the first match will be filtered out due to the fact that it matches the -allowed regular expression of ``ex@mplepassword``. Because there is still a -remaining line that did not match, it is considered a secret. - -Because that matching lines are placed on lines that start with the filename -and line number (e.g., ``/tmp/example:3:...``), you can create allowed -patterns that take filenames and line numbers into account in the regular -expression. For example, you could whitelist an entire file using something -like:: - - git secrets --add --allowed '/tmp/example:.*' - git secrets --scan /tmp/example && echo $? - # Outputs: 0 - -Alternatively, you could allow a specific line number of a file if that -line is unlikely to change using something like the following: - -:: - - git secrets --add --allowed '/tmp/example:3:.*' - git secrets --scan /tmp/example && echo $? - # Outputs: 0 - -Keep this in mind when creating allowed patterns to ensure that your allowed -patterns are not inadvertently matched due to the fact that the filename is -included in the subject text that allowed patterns are matched against. - - -Skipping validation -------------------- - -Use the ``--no-verify`` option in the event of a false positive match in a -commit, merge, or commit message. This will skip the execution of the -git hook and allow you to make the commit or merge. - - -About ------- - -- Author: `Michael Dowling <https://github.com/mtdowling>`_ -- Issue tracker: This project's source code and issue tracker can be found at - `https://github.com/awslabs/git-secrets <https://github.com/awslabs/git-secrets>`_ -- Special thanks to Adrian Vatchinsky and Ari Juels of Cornell University for - providing suggestions and feedback. - -Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/.github/actions/git-secrets/git-secrets b/.github/actions/git-secrets/git-secrets deleted file mode 100755 index 236d03b5cd1..00000000000 --- a/.github/actions/git-secrets/git-secrets +++ /dev/null @@ -1,409 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# -# http://aws.amazon.com/apache2.0 -# -# or in the "license" file accompanying this file. This file 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. - -NONGIT_OK=1 OPTIONS_SPEC="\ -git secrets --scan [-r|--recursive] [--cached] [--no-index] [--untracked] [<files>...] -git secrets --scan-history -git secrets --install [-f|--force] [<target-directory>] -git secrets --list [--global] -git secrets --add [-a|--allowed] [-l|--literal] [--global] <pattern> -git secrets --add-provider [--global] <command> [arguments...] -git secrets --register-aws [--global] -git secrets --register-azure [--global] -git secrets --register-gcp [--global] -git secrets --aws-provider [<credentials-file>] --- -scan Scans <files> for prohibited patterns -scan-history Scans repo for prohibited patterns -install Installs git hooks for Git repository or Git template directory -list Lists secret patterns -add Adds a prohibited or allowed pattern, ensuring to de-dupe with existing patterns -add-provider Adds a secret provider that when called outputs secret patterns on new lines -aws-provider Secret provider that outputs credentials found in an ini file -register-aws Adds common AWS patterns to the git config and scans for ~/.aws/credentials -register-azure Adds common Azure patterns to the git config -register-gcp Adds common GCP patterns to the git config -r,recursive --scan scans directories recursively -cached --scan scans searches blobs registered in the index file -no-index --scan searches files in the current directory that is not managed by Git -untracked In addition to searching in the tracked files in the working tree, --scan also in untracked files -f,force --install overwrites hooks if the hook already exists -l,literal --add and --add-allowed patterns are escaped so that they are literal -a,allowed --add adds an allowed pattern instead of a prohibited pattern -global Uses the --global git config -commit_msg_hook* commit-msg hook (internal only) -pre_commit_hook* pre-commit hook (internal only) -prepare_commit_msg_hook* prepare-commit-msg hook (internal only)" - -# Include the git setup script. This parses and normalized CLI arguments. -. "$(git --exec-path)"/git-sh-setup - -load_patterns() { - git config --get-all secrets.patterns - # Execute each provider and use their output to build up patterns - git config --get-all secrets.providers | while read -r cmd; do - # Only split words on '\n\t ' and strip "\r" from the output to account - # for carriage returns being added on Windows systems. Note that this - # trimming is done before the test to ensure that the string is not empty. - local result="$(export IFS=$'\n\t '; $cmd | tr -d $'\r')" - # Do not add empty lines from providers as that would match everything. - if [ -n "${result}" ]; then - echo "${result}" - fi - done -} - -load_allowed() { - git config --get-all secrets.allowed - local gitallowed="$(git rev-parse --show-toplevel)/.gitallowed" - if [ -e "$gitallowed" ]; then - cat $gitallowed | awk 'NF && $1!~/^#/' - fi -} - -# load patterns and combine them with | -load_combined_patterns() { - local patterns=$(load_patterns) - local combined_patterns='' - for pattern in $patterns; do - combined_patterns=${combined_patterns}${pattern}"|" - done - combined_patterns=${combined_patterns%?} - echo $combined_patterns -} - -# Scans files or a repo using patterns. -scan() { - local files=("${@}") options="" - [ "${SCAN_CACHED}" == 1 ] && options+="--cached" - [ "${SCAN_UNTRACKED}" == 1 ] && options+=" --untracked" - [ "${SCAN_NO_INDEX}" == 1 ] && options+=" --no-index" - # Scan using git-grep if there are no files or if git options are applied. - if [ ${#files[@]} -eq 0 ] || [ ! -z "${options}" ]; then - output=$(git_grep $options "${files[@]}") - else - output=$(regular_grep "${files[@]}") - fi - process_output $? "${output}" -} - -# Scans through history using patterns -scan_history() { - # git log does not support multiple patterns, so we need to combine them - local combined_patterns=$(load_combined_patterns) - [ -z "${combined_patterns}" ] && return 0 - # Looks for differences matching the patterns, reduces the number of revisions to scan - local to_scan=$(git log --all -G"${combined_patterns}" --pretty=%H) - # Scan through revisions with findings to normalize output - output=$(GREP_OPTIONS= LC_ALL=C git grep -nwHEI "${combined_patterns}" $to_scan) - process_output $? "${output}" -} - -# Performs a git-grep, taking into account patterns and options. -# Note: this function returns 1 on success, 0 on error. -git_grep() { - local options="$1"; shift - local files=("${@}") combined_patterns=$(load_combined_patterns) - - [ -z "${combined_patterns}" ] && return 1 - GREP_OPTIONS= LC_ALL=C git grep -nwHEI ${options} "${combined_patterns}" -- "${files[@]}" -} - -# Performs a regular grep, taking into account patterns and recursion. -# Note: this function returns 1 on success, 0 on error. -regular_grep() { - local files=("${@}") patterns=$(load_patterns) action='skip' - [ -z "${patterns}" ] && return 1 - [ ${RECURSIVE} -eq 1 ] && action="recurse" - GREP_OPTIONS= LC_ALL=C grep -d "${action}" -nwHEI "${patterns}" "${files[@]}" -} - -# Process the given status ($1) and output variables ($2). -# Takes into account allowed patterns, and if a bad match is found, -# prints an error message and exits 1. -process_output() { - local status="$1" output="$2" - local allowed=$(load_allowed) - case "$status" in - 0) - [ -z "${allowed}" ] && echo "${output}" >&2 && return 1 - # Determine with a negative grep if the found matches are allowed - echo "${output}" | GREP_OPTIONS= LC_ALL=C grep -Ev "${allowed}" >&2 \ - && return 1 || return 0 - ;; - 1) return 0 ;; - *) exit $status - esac -} - -# Calls the given scanning function at $1, shifts, and passes to it $@. -# Exit 0 if success, otherwise exit 1 with error message. -scan_with_fn_or_die() { - local fn="$1"; shift - $fn "$@" && exit 0 - echo >&2 - echo "[ERROR] Matched one or more prohibited patterns" >&2 - echo >&2 - echo "Possible mitigations:" >&2 - echo "- Mark false positives as allowed using: git config --add secrets.allowed ..." >&2 - echo "- Mark false positives as allowed by adding regular expressions to .gitallowed at repository's root directory" >&2 - echo "- List your configured patterns: git config --get-all secrets.patterns" >&2 - echo "- List your configured allowed patterns: git config --get-all secrets.allowed" >&2 - echo "- List your configured allowed patterns in .gitallowed at repository's root directory" >&2 - echo "- Use --no-verify if this is a one-time false positive" >&2 - exit 1 -} - -# Scans a commit message, passed in the path to a file. -commit_msg_hook() { - scan_with_fn_or_die "scan" "$1" -} - -# Scans all files that are about to be committed. -pre_commit_hook() { - SCAN_CACHED=1 - local files=() file found_match=0 rev="4b825dc642cb6eb9a060e54bf8d69288fbee4904" - # Diff against HEAD if this is not the first commit in the repo. - git rev-parse --verify HEAD >/dev/null 2>&1 && rev="HEAD" - # Filter out deleted files using --diff-filter - while IFS= read -r file; do - [ -n "$file" ] && files+=("$file") - done <<< "$(git diff-index --diff-filter 'ACMU' --name-only --cached $rev --)" - scan_with_fn_or_die "scan" "${files[@]}" -} - -# Determines if merging in a commit will introduce tainted history. -prepare_commit_msg_hook() { - case "$2,$3" in - merge,) - local git_head=$(env | grep GITHEAD) # e.g. GITHEAD_<sha>=release/1.43 - local sha="${git_head##*=}" # Get just the SHA - local branch=$(git symbolic-ref HEAD) # e.g. refs/heads/master - local dest="${branch#refs/heads/}" # cut out "refs/heads" - git log "${dest}".."${sha}" -p | scan_with_fn_or_die "scan" - - ;; - esac -} - -install_hook() { - local path="$1" hook="$2" cmd="$3" dest - # Determines the approriate path for a hook to be installed - if [ -d "${path}/hooks/${hook}.d" ]; then - dest="${path}/hooks/${hook}.d/git-secrets" - else - dest="${path}/hooks/${hook}" - fi - [ -f "${dest}" ] && [ "${FORCE}" -ne 1 ] \ - && die "${dest} already exists. Use -f to force" - echo "#!/usr/bin/env bash" > "${dest}" - echo "git secrets --${cmd} -- \"\$@\"" >> "${dest}" - chmod +x "${dest}" - say "$(tput setaf 2)✓$(tput sgr 0) Installed ${hook} hook to ${dest}" -} - -install_all_hooks() { - install_hook "$1" "commit-msg" "commit_msg_hook" - install_hook "$1" "pre-commit" "pre_commit_hook" - install_hook "$1" "prepare-commit-msg" "prepare_commit_msg_hook" -} - -# Adds a git config pattern, ensuring to de-dupe -add_config() { - local key="$1"; shift - local value="$@" - if [ ${LITERAL} -eq 1 ]; then - value=$(sed 's/[\.|$(){}?+*^]/\\&/g' <<< "${value}") - fi - if [ ${GLOBAL} -eq 1 ]; then - git config --global --get-all $key | grep -Fq "${value}" && return 1 - git config --global --add "${key}" "${value}" - else - git config --get-all $key | grep -Fq "${value}" && return 1 - git config --add "${key}" "${value}" - fi -} - -register_aws() { - # Reusable regex patterns - local aws="(AWS|aws|Aws)?_?" quote="(\"|')" connect="\s*(:|=>|=)\s*" - local opt_quote="${quote}?" - add_config 'secrets.providers' 'git secrets --aws-provider' - add_config 'secrets.patterns' '(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}' - add_config 'secrets.patterns' "${opt_quote}${aws}(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)${opt_quote}${connect}${opt_quote}[A-Za-z0-9/\+=]{40}${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}${aws}(ACCOUNT|account|Account)_?(ID|id|Id)?${opt_quote}${connect}${opt_quote}[0-9]{4}\-?[0-9]{4}\-?[0-9]{4}${opt_quote}" - add_config 'secrets.allowed' 'AKIAIOSFODNN7EXAMPLE' - add_config 'secrets.allowed' "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" - - if [[ $? == 0 ]]; then - echo 'OK' - fi - - exit $? -} - -register_azure() { - # Reusable regex patterns - local azure="(AZURE|azure|Azure)?_?" quote="(\"|')" connect="\s*(:|=>|=)\s*" - local opt_quote="${quote}" - add_config 'secrets.patterns' "${opt_quote}[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][o|O][n|N][m|M][i|I][c|C][r|R][o|O][s|S][o|O][f|F][t|T][.][c|C][o|O][m|M]${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][b|B][l|L][o|O][b|B][.][c|C][o|O][r|R][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][q|Q][u|U][e|E][u|U][e|E][.][c|C][o|O][r|R][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][t|T][a|A][b|B][l|L][e|E][.][c|C][o|O][r|R][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][d|D][a|A][t|T][a|A][b|B][a|A][s|S][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][s|S][e|E][r|R][v|V][i|I][c|C][e|E][b|B][u|U][s|S][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][t|T][i|I][m|M][e|E][s|S][e|E][r|R][i|I][e|E][s|S][.][a|A][z|Z][u|U][r|R][e|E][.][c|C][o|O][m|M]${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][a|T][c|C][c|C][e|E][s|S][s|S][c|C][o|O][n|N][t|T][r|R][o|O][l|L][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][a|A][z|Z][u|U][r|R][e|E][h|H][d|D][i|I][n|N][s|S][i|I][g|G][h|H][t|T][.][n|N][e|E][t|T]${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][c|C][l|L][o|O][u|U][d|D][a|A][p|P][p|P][.][a|A][z|Z][u|U][r|R][e|E][.][c|C][o|O][m|M]${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][c|C][l|L][o|O][u|U][d|D][a|A][p|P][p|P][.][n|N][e|E][t|T]${opt_quote}" - add_config 'secrets.patterns' "${opt_quote}.*[0-9a-zA-Z]{2,256}[.][d|D][o|O][c|C][u|U][m|M][e|E][n|N][t|T][s|S][.][a|A][z|Z][u|U][r|R][e|E][.][c|C][o|O][m|M]${opt_quote}" - add_config 'secrets.patterns' "^-----BEGIN (RSA|EC|DSA|GPP) PRIVATE KEY-----$" - add_config 'secrets.patterns' "(\"|')[A-Z0-9a-z[:punct:]]{32}(\"|')$" - add_config 'secrets.patterns' "(\"|')[A-Z0-9a-z[:punct:]]{88}(\"|')$" - - - - - if [[ $? == 0 ]]; then - echo 'OK' - fi - - exit $? -} - - - - -register_gcp() { - # Reusable regex patterns - add_config 'secrets.patterns' '\bprivate_key.*\b' - - - if [[ $? == 0 ]]; then - echo 'OK' - fi - - exit $? -} - -aws_provider() { - local fi="$1" - [ -z "$fi" ] && fi=~/.aws/credentials - # Find keys and ensure that special characters are escaped. - if [ -f $fi ]; then - awk -F "=" '/aws_access_key_id|aws_secret_access_key/ {print $2}' $fi \ - | tr -d ' "' \ - | sed 's/[]\.|$(){}?+*^]/\\&/g' - fi -} - -# Ensures that the command is what was expected for an option. -assert_option_for_command() { - local expected_command="$1" - local option_name="$2" - if [ "${COMMAND}" != "${expected_command}" ]; then - die "${option_name} can only be supplied with the ${expected_command} subcommand" - fi -} - -declare COMMAND="$1" FORCE=0 RECURSIVE=0 LITERAL=0 GLOBAL=0 ALLOWED=0 -declare SCAN_CACHED=0 SCAN_NO_INDEX=0 SCAN_UNTRACKED=0 - -# Shift off the command name -shift 1 -while [ "$#" -ne 0 ]; do - case "$1" in - -f) - assert_option_for_command "--install" "-f|--force" - FORCE=1 - ;; - -r) - assert_option_for_command "--scan" "-r|--recursive" - RECURSIVE=1 - ;; - -a) - assert_option_for_command "--add" "-a|--allowed" - ALLOWED=1 - ;; - -l) - assert_option_for_command "--add" "-l|--literal" - LITERAL=1 - ;; - --cached) - assert_option_for_command "--scan" "--cached" - SCAN_CACHED=1 - ;; - --no-index) - assert_option_for_command "--scan" "--no-index" - SCAN_NO_INDEX=1 - ;; - --untracked) - assert_option_for_command "--scan" "--untracked" - SCAN_UNTRACKED=1 - ;; - --global) GLOBAL=1 ;; - --) shift; break ;; - esac - shift -done - -# Ensure that recursive is not applied with mutually exclusive options. -if [ ${RECURSIVE} -eq 1 ]; then - if [ ${SCAN_CACHED} -eq 1 ] \ - || [ ${SCAN_NO_INDEX} -eq 1 ] \ - || [ ${SCAN_UNTRACKED} -eq 1 ]; - then - die "-r|--recursive cannot be supplied with --cached, --no-index, or --untracked" - fi -fi - -case "${COMMAND}" in - -h|--help|--) "$0" -h; exit 0 ;; - --add-provider) add_config "secrets.providers" "$@" ;; - --register-aws) register_aws ;; - --register-azure) register_azure ;; - --register-gcp) register_gcp ;; - --aws-provider) aws_provider "$1" ;; - --commit_msg_hook|--pre_commit_hook|--prepare_commit_msg_hook) - ${COMMAND:2} "$@" - ;; - --add) - if [ ${ALLOWED} -eq 1 ]; then - add_config "secrets.allowed" "$1" - else - add_config "secrets.patterns" "$1" - fi - ;; - --scan) scan_with_fn_or_die "scan" "$@" ;; - --scan-history) scan_with_fn_or_die "scan_history" "$@" ;; - --list) - if [ ${GLOBAL} -eq 1 ]; then - git config --global --get-regex secrets.* - else - git config --get-regex secrets.* - fi - ;; - --install) - DIRECTORY="$1" - if [ -z "${DIRECTORY}" ]; then - DIRECTORY=$(git rev-parse --git-dir) || die "Not in a Git repository" - elif [ -d "${DIRECTORY}"/.git ]; then - DIRECTORY="${DIRECTORY}/.git" - fi - mkdir -p "${DIRECTORY}/hooks" || die "Could not create dir: ${DIRECTORY}" - install_all_hooks "${DIRECTORY}" - ;; - *) echo "Unknown option: ${COMMAND}" && "$0" -h ;; -esac diff --git a/.github/actions/git-secrets/git-secrets.1 b/.github/actions/git-secrets/git-secrets.1 deleted file mode 100644 index 1c6d25c3413..00000000000 --- a/.github/actions/git-secrets/git-secrets.1 +++ /dev/null @@ -1,843 +0,0 @@ -.\" Man page generated from reStructuredText. -. -.TH GIT-SECRETS "" "" "" -.SH NAME -git-secrets \- Prevents you from committing passwords and other sensitive information to a git repository. -. -.nr rst2man-indent-level 0 -. -.de1 rstReportMargin -\\$1 \\n[an-margin] -level \\n[rst2man-indent-level] -level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] -- -\\n[rst2man-indent0] -\\n[rst2man-indent1] -\\n[rst2man-indent2] -.. -.de1 INDENT -.\" .rstReportMargin pre: -. RS \\$1 -. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] -. nr rst2man-indent-level +1 -.\" .rstReportMargin post: -.. -.de UNINDENT -. RE -.\" indent \\n[an-margin] -.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] -.nr rst2man-indent-level -1 -.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] -.in \\n[rst2man-indent\\n[rst2man-indent-level]]u -.. -.SS Contents -.INDENT 0.0 -.IP \(bu 2 -\fI\%Synopsis\fP -.IP \(bu 2 -\fI\%Description\fP -.IP \(bu 2 -\fI\%Installing git\-secrets\fP -.INDENT 2.0 -.IP \(bu 2 -\fI\%*nix (Linux/macOS)\fP -.IP \(bu 2 -\fI\%Windows\fP -.IP \(bu 2 -\fI\%Homebrew (for macOS users)\fP -.UNINDENT -.IP \(bu 2 -\fI\%Advanced configuration\fP -.IP \(bu 2 -\fI\%Before making public a repository\fP -.IP \(bu 2 -\fI\%Options\fP -.INDENT 2.0 -.IP \(bu 2 -\fI\%Operation Modes\fP -.IP \(bu 2 -\fI\%Options for \fB\-\-install\fP\fP -.IP \(bu 2 -\fI\%Options for \fB\-\-scan\fP\fP -.IP \(bu 2 -\fI\%Options for \fB\-\-list\fP\fP -.IP \(bu 2 -\fI\%Options for \fB\-\-add\fP\fP -.IP \(bu 2 -\fI\%Options for \fB\-\-register\-aws\fP\fP -.IP \(bu 2 -\fI\%Options for \fB\-\-aws\-provider\fP\fP -.IP \(bu 2 -\fI\%Options for \fB\-\-add\-provider\fP\fP -.UNINDENT -.IP \(bu 2 -\fI\%Defining prohibited patterns\fP -.IP \(bu 2 -\fI\%Ignoring false positives\fP -.IP \(bu 2 -\fI\%Secret providers\fP -.IP \(bu 2 -\fI\%Example walkthrough\fP -.IP \(bu 2 -\fI\%Skipping validation\fP -.IP \(bu 2 -\fI\%About\fP -.UNINDENT -.SH SYNOPSIS -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-scan [\-r|\-\-recursive] [\-\-cached] [\-\-no\-index] [\-\-untracked] [<files>...] -git secrets \-\-scan\-history -git secrets \-\-install [\-f|\-\-force] [<target\-directory>] -git secrets \-\-list [\-\-global] -git secrets \-\-add [\-a|\-\-allowed] [\-l|\-\-literal] [\-\-global] <pattern> -git secrets \-\-add\-provider [\-\-global] <command> [arguments...] -git secrets \-\-register\-aws [\-\-global] -git secrets \-\-aws\-provider [<credentials\-file>] -.ft P -.fi -.UNINDENT -.UNINDENT -.SH DESCRIPTION -.sp -\fBgit\-secrets\fP scans commits, commit messages, and \fB\-\-no\-ff\fP merges to -prevent adding secrets into your git repositories. If a commit, -commit message, or any commit in a \fB\-\-no\-ff\fP merge history matches one of -your configured prohibited regular expression patterns, then the commit is -rejected. -.SH INSTALLING GIT-SECRETS -.sp -\fBgit\-secrets\fP must be placed somewhere in your PATH so that it is picked up -by \fBgit\fP when running \fBgit secrets\fP\&. -.SS *nix (Linux/macOS) -.IP "System Message: WARNING/2 (README.rst:, line 43)" -Title underline too short. -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -\e*nix (Linux/macOS) -~~~~~~~~~~~~~~~~~ -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -You can use the \fBinstall\fP target of the provided Makefile to install \fBgit secrets\fP and the man page. -You can customize the install path using the PREFIX and MANPREFIX variables. -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -make install -.ft P -.fi -.UNINDENT -.UNINDENT -.SS Windows -.sp -Run the provided \fBinstall.ps1\fP powershell script. This will copy the needed files -to an installation directory (\fB%USERPROFILE%/.git\-secrets\fP by default) and add -the directory to the current user \fBPATH\fP\&. -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -PS > ./install.ps1 -.ft P -.fi -.UNINDENT -.UNINDENT -.SS Homebrew (for macOS users) -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -brew install git\-secrets -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -\fBWARNING:\fP -.INDENT 0.0 -.INDENT 3.5 -You\(aqre not done yet! You MUST install the git hooks for every repo that -you wish to use with \fBgit secrets \-\-install\fP\&. -.UNINDENT -.UNINDENT -.sp -Here\(aqs a quick example of how to ensure a git repository is scanned for secrets -on each commit: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -cd /path/to/my/repo -git secrets \-\-install -git secrets \-\-register\-aws -.ft P -.fi -.UNINDENT -.UNINDENT -.SH ADVANCED CONFIGURATION -.sp -Add a configuration template if you want to add hooks to all repositories you -initialize or clone in the future. -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-register\-aws \-\-global -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Add hooks to all your local repositories. -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-install ~/.git\-templates/git\-secrets -git config \-\-global init.templateDir ~/.git\-templates/git\-secrets -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Add custom providers to scan for security credentials. -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-add\-provider \-\- cat /path/to/secret/file/patterns -.ft P -.fi -.UNINDENT -.UNINDENT -.SH BEFORE MAKING PUBLIC A REPOSITORY -.sp -With git\-secrets is also possible to scan a repository including all revisions: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-scan\-history -.ft P -.fi -.UNINDENT -.UNINDENT -.SH OPTIONS -.SS Operation Modes -.sp -Each of these options must appear first on the command line. -.INDENT 0.0 -.TP -.B \fB\-\-install\fP -Installs git hooks for a repository. Once the hooks are installed for a git -repository, commits and non\-fast\-forward merges for that repository will be prevented -from committing secrets. -.TP -.B \fB\-\-scan\fP -Scans one or more files for secrets. When a file contains a secret, the -matched text from the file being scanned will be written to stdout and the -script will exit with a non\-zero status. Each matched line will be written with -the name of the file that matched, a colon, the line number that matched, -a colon, and then the line of text that matched. If no files are provided, -all files returned by \fBgit ls\-files\fP are scanned. -.TP -.B \fB\-\-scan\-history\fP -Scans repository including all revisions. When a file contains a secret, the -matched text from the file being scanned will be written to stdout and the -script will exit with a non\-zero status. Each matched line will be written with -the name of the file that matched, a colon, the line number that matched, -a colon, and then the line of text that matched. -.TP -.B \fB\-\-list\fP -Lists the \fBgit\-secrets\fP configuration for the current repo or in the global -git config. -.TP -.B \fB\-\-add\fP -Adds a prohibited or allowed pattern. -.TP -.B \fB\-\-add\-provider\fP -Registers a secret provider. Secret providers are executables that when -invoked output prohibited patterns that \fBgit\-secrets\fP should treat as -prohibited. -.TP -.B \fB\-\-register\-aws\fP -Adds common AWS patterns to the git config and ensures that keys present -in \fB~/.aws/credentials\fP are not found in any commit. The following -checks are added: -.INDENT 7.0 -.IP \(bu 2 -AWS Access Key IDs via \fB(A3T[A\-Z0\-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A\-Z0\-9]{16}\fP -.IP \(bu 2 -AWS Secret Access Key assignments via ":" or "=" surrounded by optional -quotes -.IP \(bu 2 -AWS account ID assignments via ":" or "=" surrounded by optional quotes -.IP \(bu 2 -Allowed patterns for example AWS keys (\fBAKIAIOSFODNN7EXAMPLE\fP and -\fBwJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\fP) -.IP \(bu 2 -Known credentials from \fB~/.aws/credentials\fP -.UNINDENT -.sp -\fBNOTE:\fP -.INDENT 7.0 -.INDENT 3.5 -While the patterns registered by this command should catch most -instances of AWS credentials, these patterns are \fBnot\fP guaranteed to -catch them \fBall\fP\&. \fBgit\-secrets\fP should be used as an extra means of -insurance \-\- you still need to do your due diligence to ensure that you -do not commit credentials to a repository. -.UNINDENT -.UNINDENT -.TP -.B \fB\-\-aws\-provider\fP -Secret provider that outputs credentials found in an INI file. You can -optionally provide the path to an INI file. -.UNINDENT -.SS Options for \fB\-\-install\fP -.INDENT 0.0 -.TP -.B \fB\-f, \-\-force\fP -Overwrites existing hooks if present. -.TP -.B \fB<target\-directory>\fP -When provided, installs git hooks to the given directory. The current -directory is assumed if \fB<target\-directory>\fP is not provided. -.sp -If the provided \fB<target\-directory>\fP is not in a git repository, the -directory will be created and hooks will be placed in -\fB<target\-directory>/hooks\fP\&. This can be useful for creating git template -directories using with \fBgit init \-\-template <target\-directory>\fP\&. -.sp -You can run \fBgit init\fP on a repository that has already been initialized. -From the \fI\%git init documentation\fP: -.INDENT 7.0 -.INDENT 3.5 -From the git documentation: Running \fBgit init\fP in an existing repository -is safe. It will not overwrite things that are already there. The -primary reason for rerunning \fBgit init\fP is to pick up newly added -templates (or to move the repository to another place if -\fB\-\-separate\-git\-dir\fP is given). -.UNINDENT -.UNINDENT -.sp -The following git hooks are installed: -.INDENT 7.0 -.IP 1. 3 -\fBpre\-commit\fP: Used to check if any of the files changed in the commit -use prohibited patterns. -.IP 2. 3 -\fBcommit\-msg\fP: Used to determine if a commit message contains a -prohibited patterns. -.IP 3. 3 -\fBprepare\-commit\-msg\fP: Used to determine if a merge commit will -introduce a history that contains a prohibited pattern at any point. -Please note that this hook is only invoked for non fast\-forward merges. -.UNINDENT -.sp -\fBNOTE:\fP -.INDENT 7.0 -.INDENT 3.5 -Git only allows a single script to be executed per hook. If the -repository contains Debian\-style subdirectories like \fBpre\-commit.d\fP -and \fBcommit\-msg.d\fP, then the git hooks will be installed into these -directories, which assumes that you\(aqve configured the corresponding -hooks to execute all of the scripts found in these directories. If -these git subdirectories are not present, then the git hooks will be -installed to the git repo\(aqs \fB\&.git/hooks\fP directory. -.UNINDENT -.UNINDENT -.UNINDENT -.SS Examples -.sp -Install git hooks to the current directory: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -cd /path/to/my/repository -git secrets \-\-install -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Install git hooks to a repository other than the current directory: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-install /path/to/my/repository -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Create a git template that has \fBgit\-secrets\fP installed, and then copy that -template into a git repository: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-install ~/.git\-templates/git\-secrets -git init \-\-template ~/.git\-templates/git\-secrets -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Overwrite existing hooks if present: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-install \-f -.ft P -.fi -.UNINDENT -.UNINDENT -.SS Options for \fB\-\-scan\fP -.INDENT 0.0 -.TP -.B \fB\-r, \-\-recursive\fP -Scans the given files recursively. If a directory is encountered, the -directory will be scanned. If \fB\-r\fP is not provided, directories will be -ignored. -.sp -\fB\-r\fP cannot be used alongside \fB\-\-cached\fP, \fB\-\-no\-index\fP, or -\fB\-\-untracked\fP\&. -.TP -.B \fB\-\-cached\fP -Searches blobs registered in the index file. -.TP -.B \fB\-\-no\-index\fP -Searches files in the current directory that is not managed by git. -.TP -.B \fB\-\-untracked\fP -In addition to searching in the tracked files in the working tree, -\fB\-\-scan\fP also in untracked files. -.TP -.B \fB<files>...\fP -The path to one or more files on disk to scan for secrets. -.sp -If no files are provided, all files returned by \fBgit ls\-files\fP are -scanned. -.UNINDENT -.SS Examples -.sp -Scan all files in the repo: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-scan -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Scans a single file for secrets: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-scan /path/to/file -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Scans a directory recursively for secrets: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-scan \-r /path/to/directory -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Scans multiple files for secrets: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-scan /path/to/file /path/to/other/file -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -You can scan by globbing: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-scan /path/to/directory/* -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Scan from stdin: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -echo \(aqhello!\(aq | git secrets \-\-scan \- -.ft P -.fi -.UNINDENT -.UNINDENT -.SS Options for \fB\-\-list\fP -.INDENT 0.0 -.TP -.B \fB\-\-global\fP -Lists only git\-secrets configuration in the global git config. -.UNINDENT -.SS Options for \fB\-\-add\fP -.INDENT 0.0 -.TP -.B \fB\-\-global\fP -Adds patterns to the global git config -.TP -.B \fB\-l, \-\-literal\fP -Escapes special regular expression characters in the provided pattern so -that the pattern is searched for literally. -.TP -.B \fB\-a, \-\-allowed\fP -Mark the pattern as allowed instead of prohibited. Allowed patterns are -used to filter our false positives. -.TP -.B \fB<pattern>\fP -The regex pattern to search. -.UNINDENT -.SS Examples -.sp -Adds a prohibited pattern to the current repo: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-add \(aq[A\-Z0\-9]{20}\(aq -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Adds a prohibited pattern to the global git config: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-add \-\-global \(aq[A\-Z0\-9]{20}\(aq -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Adds a string that is scanned for literally (\fB+\fP is escaped): -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-add \-\-literal \(aqfoo+bar\(aq -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Add an allowed pattern: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-add \-a \(aqallowed pattern\(aq -.ft P -.fi -.UNINDENT -.UNINDENT -.SS Options for \fB\-\-register\-aws\fP -.INDENT 0.0 -.TP -.B \fB\-\-global\fP -Adds AWS specific configuration variables to the global git config. -.UNINDENT -.SS Options for \fB\-\-aws\-provider\fP -.INDENT 0.0 -.TP -.B \fB[<credentials\-file>]\fP -If provided, specifies the custom path to an INI file to scan. If not -provided, \fB~/.aws/credentials\fP is assumed. -.UNINDENT -.SS Options for \fB\-\-add\-provider\fP -.INDENT 0.0 -.TP -.B \fB\-\-global\fP -Adds the provider to the global git config. -.TP -.B \fB<command>\fP -Provider command to invoke. When invoked the command is expected to write -prohibited patterns separated by new lines to stdout. Any extra arguments -provided are passed on to the command. -.UNINDENT -.SS Examples -.sp -Registers a secret provider with arguments: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-add\-provider \-\- git secrets \-\-aws\-provider -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Cats secrets out of a file: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-add\-provider \-\- cat /path/to/secret/file/patterns -.ft P -.fi -.UNINDENT -.UNINDENT -.SH DEFINING PROHIBITED PATTERNS -.sp -\fBegrep\fP\-compatible regular expressions are used to determine if a commit or -commit message contains any prohibited patterns. These regular expressions are -defined using the \fBgit config\fP command. It is important to note that -different systems use different versions of egrep. For example, when running on -macOS, you will use a different version of \fBegrep\fP than when running on something -like Ubuntu (BSD vs GNU). -.sp -You can add prohibited regular expression patterns to your git config using -\fBgit secrets \-\-add <pattern>\fP\&. -.SH IGNORING FALSE POSITIVES -.sp -Sometimes a regular expression might match false positives. For example, git -commit SHAs look a lot like AWS access keys. You can specify many different -regular expression patterns as false positives using the following command: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-add \-\-allowed \(aqmy regex pattern\(aq -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -You can also add regular expressions patterns to filter false positives to a -\fB\&.gitallowed\fP file located in the repository\(aqs root directory. Lines starting -with \fB#\fP are skipped (comment line) and empty lines are also skipped. -.sp -First, git\-secrets will extract all lines from a file that contain a prohibited -match. Included in the matched results will be the full path to the name of -the file that was matched, followed by \(aq:\(aq, followed by the line number that was -matched, followed by the entire line from the file that was matched by a secret -pattern. Then, if you\(aqve defined allowed regular expressions, git\-secrets will -check to see if all of the matched lines match at least one of your registered -allowed regular expressions. If all of the lines that were flagged as secret -are canceled out by an allowed match, then the subject text does not contain -any secrets. If any of the matched lines are not matched by an allowed regular -expression, then git\-secrets will fail the commit/merge/message. -.sp -\fBIMPORTANT:\fP -.INDENT 0.0 -.INDENT 3.5 -Just as it is a bad practice to add prohibited patterns that are too -greedy, it is also a bad practice to add allowed patterns that are too -forgiving. Be sure to test out your patterns using ad\-hoc calls to -\fBgit secrets \-\-scan $filename\fP to ensure they are working as intended. -.UNINDENT -.UNINDENT -.SH SECRET PROVIDERS -.sp -Sometimes you want to check for an exact pattern match against a set of known -secrets. For example, you might want to ensure that no credentials present in -\fB~/.aws/credentials\fP ever show up in a commit. In these cases, it\(aqs better to -leave these secrets in one location rather than spread them out across git -repositories in git configs. You can use "secret providers" to fetch these -types of credentials. A secret provider is an executable that when invoked -outputs prohibited patterns separated by new lines. -.sp -You can add secret providers using the \fB\-\-add\-provider\fP command: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-add\-provider \-\- git secrets \-\-aws\-provider -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Notice the use of \fB\-\-\fP\&. This ensures that any arguments associated with the -provider are passed to the provider each time it is invoked when scanning -for secrets. -.SH EXAMPLE WALKTHROUGH -.sp -Let\(aqs take a look at an example. Given the following subject text (stored in -\fB/tmp/example\fP): -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -This is a test! -password=ex@mplepassword -password=****** -More test... -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -And the following registered patterns: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-add \(aqpassword\es*=\es*.+\(aq -git secrets \-\-add \-\-allowed \-\-literal \(aqex@mplepassword\(aq -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Running \fBgit secrets \-\-scan /tmp/example\fP, the result will -result in the following error output: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -/tmp/example:3:password=****** - -[ERROR] Matched prohibited pattern - -Possible mitigations: -\- Mark false positives as allowed using: git config \-\-add secrets.allowed ... -\- List your configured patterns: git config \-\-get\-all secrets.patterns -\- List your configured allowed patterns: git config \-\-get\-all secrets.allowed -\- Use \-\-no\-verify if this is a one\-time false positive -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Breaking this down, the prohibited pattern value of \fBpassword\es*=\es*.+\fP will -match the following lines: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -/tmp/example:2:password=ex@mplepassword -/tmp/example:3:password=****** -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -\&...But the first match will be filtered out due to the fact that it matches the -allowed regular expression of \fBex@mplepassword\fP\&. Because there is still a -remaining line that did not match, it is considered a secret. -.sp -Because that matching lines are placed on lines that start with the filename -and line number (e.g., \fB/tmp/example:3:...\fP), you can create allowed -patterns that take filenames and line numbers into account in the regular -expression. For example, you could whitelist an entire file using something -like: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-add \-\-allowed \(aq/tmp/example:.*\(aq -git secrets \-\-scan /tmp/example && echo $? -# Outputs: 0 -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Alternatively, you could allow a specific line number of a file if that -line is unlikely to change using something like the following: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -git secrets \-\-add \-\-allowed \(aq/tmp/example:3:.*\(aq -git secrets \-\-scan /tmp/example && echo $? -# Outputs: 0 -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Keep this in mind when creating allowed patterns to ensure that your allowed -patterns are not inadvertently matched due to the fact that the filename is -included in the subject text that allowed patterns are matched against. -.SH SKIPPING VALIDATION -.sp -Use the \fB\-\-no\-verify\fP option in the event of a false positive match in a -commit, merge, or commit message. This will skip the execution of the -git hook and allow you to make the commit or merge. -.SH ABOUT -.INDENT 0.0 -.IP \(bu 2 -Author: \fI\%Michael Dowling\fP -.IP \(bu 2 -Issue tracker: This project\(aqs source code and issue tracker can be found at -\fI\%https://github.com/awslabs/git\-secrets\fP -.IP \(bu 2 -Special thanks to Adrian Vatchinsky and Ari Juels of Cornell University for -providing suggestions and feedback. -.UNINDENT -.sp -Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. -.\" Generated by docutils manpage writer. -. diff --git a/.github/actions/git-secrets/install.ps1 b/.github/actions/git-secrets/install.ps1 deleted file mode 100644 index fbffbfa9906..00000000000 --- a/.github/actions/git-secrets/install.ps1 +++ /dev/null @@ -1,48 +0,0 @@ -Param([string]$InstallationDirectory = $($Env:USERPROFILE + "\.git-secrets")) - -Write-Host "Checking to see if installation directory already exists..." -if (-not (Test-Path $InstallationDirectory)) -{ - Write-Host "Creating installation directory." - New-Item -ItemType Directory -Path $InstallationDirectory | Out-Null -} -else -{ - Write-Host "Installation directory already exists." -} - -Write-Host "Copying files." -Copy-Item ./git-secrets -Destination $InstallationDirectory -Force -Copy-Item ./git-secrets.1 -Destination $InstallationDirectory -Force - -Write-Host "Checking if directory already exists in Path..." -$currentPath = [Environment]::GetEnvironmentVariable("PATH", "User") -if ($currentPath -notlike "*$InstallationDirectory*") -{ - Write-Host "Adding to path." - $newPath = $currentPath - if(-not ($newPath.EndsWith(";"))) - { - $newPath = $newPath + ";" - } - $newPath = $newPath + $InstallationDirectory - [Environment]::SetEnvironmentVariable("PATH", $newPath, "User") -} -else -{ - Write-Host "Already in Path." -} - -# Adding to Session -Write-Host "Adding to user session." -$currentSessionPath = $Env:Path -if ($currentSessionPath -notlike "*$InstallationDirectory*") -{ - if(-not ($currentSessionPath.EndsWith(";"))) - { - $currentSessionPath = $currentSessionPath + ";" - } - $Env:Path = $currentSessionPath + $InstallationDirectory -} - -Write-Host "Done." \ No newline at end of file diff --git a/.github/actions/git-secrets/test/bats/LICENSE b/.github/actions/git-secrets/test/bats/LICENSE deleted file mode 100644 index bac4eb29ccf..00000000000 --- a/.github/actions/git-secrets/test/bats/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2014 Sam Stephenson - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.github/actions/git-secrets/test/bats/bin/bats b/.github/actions/git-secrets/test/bats/bin/bats deleted file mode 100755 index 71f392f757e..00000000000 --- a/.github/actions/git-secrets/test/bats/bin/bats +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env bash -set -e - -version() { - echo "Bats 0.4.0" -} - -usage() { - version - echo "Usage: bats [-c] [-p | -t] <test> [<test> ...]" -} - -help() { - usage - echo - echo " <test> is the path to a Bats test file, or the path to a directory" - echo " containing Bats test files." - echo - echo " -c, --count Count the number of test cases without running any tests" - echo " -h, --help Display this help message" - echo " -p, --pretty Show results in pretty format (default for terminals)" - echo " -t, --tap Show results in TAP format" - echo " -v, --version Display the version number" - echo - echo " For more information, see https://github.com/sstephenson/bats" - echo -} - -resolve_link() { - $(type -p greadlink readlink | head -1) "$1" -} - -abs_dirname() { - local cwd="$(pwd)" - local path="$1" - - while [ -n "$path" ]; do - cd "${path%/*}" - local name="${path##*/}" - path="$(resolve_link "$name" || true)" - done - - pwd - cd "$cwd" -} - -expand_path() { - { cd "$(dirname "$1")" 2>/dev/null - local dirname="$PWD" - cd "$OLDPWD" - echo "$dirname/$(basename "$1")" - } || echo "$1" -} - -BATS_LIBEXEC="$(abs_dirname "$0")" -export BATS_PREFIX="$(abs_dirname "$BATS_LIBEXEC")" -export BATS_CWD="$(abs_dirname .)" -export PATH="$BATS_LIBEXEC:$PATH" - -options=() -arguments=() -for arg in "$@"; do - if [ "${arg:0:1}" = "-" ]; then - if [ "${arg:1:1}" = "-" ]; then - options[${#options[*]}]="${arg:2}" - else - index=1 - while option="${arg:$index:1}"; do - [ -n "$option" ] || break - options[${#options[*]}]="$option" - let index+=1 - done - fi - else - arguments[${#arguments[*]}]="$arg" - fi -done - -unset count_flag pretty -[ -t 0 ] && [ -t 1 ] && pretty="1" -[ -n "$CI" ] && pretty="" - -for option in "${options[@]}"; do - case "$option" in - "h" | "help" ) - help - exit 0 - ;; - "v" | "version" ) - version - exit 0 - ;; - "c" | "count" ) - count_flag="-c" - ;; - "t" | "tap" ) - pretty="" - ;; - "p" | "pretty" ) - pretty="1" - ;; - * ) - usage >&2 - exit 1 - ;; - esac -done - -if [ "${#arguments[@]}" -eq 0 ]; then - usage >&2 - exit 1 -fi - -filenames=() -for filename in "${arguments[@]}"; do - if [ -d "$filename" ]; then - shopt -s nullglob - for suite_filename in "$(expand_path "$filename")"/*.bats; do - filenames["${#filenames[@]}"]="$suite_filename" - done - shopt -u nullglob - else - filenames["${#filenames[@]}"]="$(expand_path "$filename")" - fi -done - -if [ "${#filenames[@]}" -eq 1 ]; then - command="bats-exec-test" -else - command="bats-exec-suite" -fi - -if [ -n "$pretty" ]; then - extended_syntax_flag="-x" - formatter="bats-format-tap-stream" -else - extended_syntax_flag="" - formatter="cat" -fi - -set -o pipefail execfail -exec "$command" $count_flag $extended_syntax_flag "${filenames[@]}" | "$formatter" diff --git a/.github/actions/git-secrets/test/bats/libexec/bats b/.github/actions/git-secrets/test/bats/libexec/bats deleted file mode 100755 index 71f392f757e..00000000000 --- a/.github/actions/git-secrets/test/bats/libexec/bats +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env bash -set -e - -version() { - echo "Bats 0.4.0" -} - -usage() { - version - echo "Usage: bats [-c] [-p | -t] <test> [<test> ...]" -} - -help() { - usage - echo - echo " <test> is the path to a Bats test file, or the path to a directory" - echo " containing Bats test files." - echo - echo " -c, --count Count the number of test cases without running any tests" - echo " -h, --help Display this help message" - echo " -p, --pretty Show results in pretty format (default for terminals)" - echo " -t, --tap Show results in TAP format" - echo " -v, --version Display the version number" - echo - echo " For more information, see https://github.com/sstephenson/bats" - echo -} - -resolve_link() { - $(type -p greadlink readlink | head -1) "$1" -} - -abs_dirname() { - local cwd="$(pwd)" - local path="$1" - - while [ -n "$path" ]; do - cd "${path%/*}" - local name="${path##*/}" - path="$(resolve_link "$name" || true)" - done - - pwd - cd "$cwd" -} - -expand_path() { - { cd "$(dirname "$1")" 2>/dev/null - local dirname="$PWD" - cd "$OLDPWD" - echo "$dirname/$(basename "$1")" - } || echo "$1" -} - -BATS_LIBEXEC="$(abs_dirname "$0")" -export BATS_PREFIX="$(abs_dirname "$BATS_LIBEXEC")" -export BATS_CWD="$(abs_dirname .)" -export PATH="$BATS_LIBEXEC:$PATH" - -options=() -arguments=() -for arg in "$@"; do - if [ "${arg:0:1}" = "-" ]; then - if [ "${arg:1:1}" = "-" ]; then - options[${#options[*]}]="${arg:2}" - else - index=1 - while option="${arg:$index:1}"; do - [ -n "$option" ] || break - options[${#options[*]}]="$option" - let index+=1 - done - fi - else - arguments[${#arguments[*]}]="$arg" - fi -done - -unset count_flag pretty -[ -t 0 ] && [ -t 1 ] && pretty="1" -[ -n "$CI" ] && pretty="" - -for option in "${options[@]}"; do - case "$option" in - "h" | "help" ) - help - exit 0 - ;; - "v" | "version" ) - version - exit 0 - ;; - "c" | "count" ) - count_flag="-c" - ;; - "t" | "tap" ) - pretty="" - ;; - "p" | "pretty" ) - pretty="1" - ;; - * ) - usage >&2 - exit 1 - ;; - esac -done - -if [ "${#arguments[@]}" -eq 0 ]; then - usage >&2 - exit 1 -fi - -filenames=() -for filename in "${arguments[@]}"; do - if [ -d "$filename" ]; then - shopt -s nullglob - for suite_filename in "$(expand_path "$filename")"/*.bats; do - filenames["${#filenames[@]}"]="$suite_filename" - done - shopt -u nullglob - else - filenames["${#filenames[@]}"]="$(expand_path "$filename")" - fi -done - -if [ "${#filenames[@]}" -eq 1 ]; then - command="bats-exec-test" -else - command="bats-exec-suite" -fi - -if [ -n "$pretty" ]; then - extended_syntax_flag="-x" - formatter="bats-format-tap-stream" -else - extended_syntax_flag="" - formatter="cat" -fi - -set -o pipefail execfail -exec "$command" $count_flag $extended_syntax_flag "${filenames[@]}" | "$formatter" diff --git a/.github/actions/git-secrets/test/bats/libexec/bats-exec-suite b/.github/actions/git-secrets/test/bats/libexec/bats-exec-suite deleted file mode 100755 index 29ab255d062..00000000000 --- a/.github/actions/git-secrets/test/bats/libexec/bats-exec-suite +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash -set -e - -count_only_flag="" -if [ "$1" = "-c" ]; then - count_only_flag=1 - shift -fi - -extended_syntax_flag="" -if [ "$1" = "-x" ]; then - extended_syntax_flag="-x" - shift -fi - -trap "kill 0; exit 1" int - -count=0 -for filename in "$@"; do - let count+="$(bats-exec-test -c "$filename")" -done - -if [ -n "$count_only_flag" ]; then - echo "$count" - exit -fi - -echo "1..$count" -status=0 -offset=0 -for filename in "$@"; do - index=0 - { - IFS= read -r # 1..n - while IFS= read -r line; do - case "$line" in - "begin "* ) - let index+=1 - echo "${line/ $index / $(($offset + $index)) }" - ;; - "ok "* | "not ok "* ) - [ -n "$extended_syntax_flag" ] || let index+=1 - echo "${line/ $index / $(($offset + $index)) }" - [ "${line:0:6}" != "not ok" ] || status=1 - ;; - * ) - echo "$line" - ;; - esac - done - } < <( bats-exec-test $extended_syntax_flag "$filename" ) - offset=$(($offset + $index)) -done - -exit "$status" diff --git a/.github/actions/git-secrets/test/bats/libexec/bats-exec-test b/.github/actions/git-secrets/test/bats/libexec/bats-exec-test deleted file mode 100755 index 8f3bd5102e4..00000000000 --- a/.github/actions/git-secrets/test/bats/libexec/bats-exec-test +++ /dev/null @@ -1,346 +0,0 @@ -#!/usr/bin/env bash -set -e -set -E -set -T - -BATS_COUNT_ONLY="" -if [ "$1" = "-c" ]; then - BATS_COUNT_ONLY=1 - shift -fi - -BATS_EXTENDED_SYNTAX="" -if [ "$1" = "-x" ]; then - BATS_EXTENDED_SYNTAX="$1" - shift -fi - -BATS_TEST_FILENAME="$1" -if [ -z "$BATS_TEST_FILENAME" ]; then - echo "usage: bats-exec <filename>" >&2 - exit 1 -elif [ ! -f "$BATS_TEST_FILENAME" ]; then - echo "bats: $BATS_TEST_FILENAME does not exist" >&2 - exit 1 -else - shift -fi - -BATS_TEST_DIRNAME="$(dirname "$BATS_TEST_FILENAME")" -BATS_TEST_NAMES=() - -load() { - local name="$1" - local filename - - if [ "${name:0:1}" = "/" ]; then - filename="${name}" - else - filename="$BATS_TEST_DIRNAME/${name}.bash" - fi - - [ -f "$filename" ] || { - echo "bats: $filename does not exist" >&2 - exit 1 - } - - source "${filename}" -} - -run() { - local e E T oldIFS - [[ ! "$-" =~ e ]] || e=1 - [[ ! "$-" =~ E ]] || E=1 - [[ ! "$-" =~ T ]] || T=1 - set +e - set +E - set +T - output="$("$@" 2>&1)" - status="$?" - oldIFS=$IFS - IFS=$'\n' lines=($output) - [ -z "$e" ] || set -e - [ -z "$E" ] || set -E - [ -z "$T" ] || set -T - IFS=$oldIFS -} - -setup() { - true -} - -teardown() { - true -} - -skip() { - BATS_TEST_SKIPPED=${1:-1} - BATS_TEST_COMPLETED=1 - exit 0 -} - -bats_test_begin() { - BATS_TEST_DESCRIPTION="$1" - if [ -n "$BATS_EXTENDED_SYNTAX" ]; then - echo "begin $BATS_TEST_NUMBER $BATS_TEST_DESCRIPTION" >&3 - fi - setup -} - -bats_test_function() { - local test_name="$1" - BATS_TEST_NAMES["${#BATS_TEST_NAMES[@]}"]="$test_name" -} - -bats_capture_stack_trace() { - BATS_PREVIOUS_STACK_TRACE=( "${BATS_CURRENT_STACK_TRACE[@]}" ) - BATS_CURRENT_STACK_TRACE=() - - local test_pattern=" $BATS_TEST_NAME $BATS_TEST_SOURCE" - local setup_pattern=" setup $BATS_TEST_SOURCE" - local teardown_pattern=" teardown $BATS_TEST_SOURCE" - - local frame - local index=1 - - while frame="$(caller "$index")"; do - BATS_CURRENT_STACK_TRACE["${#BATS_CURRENT_STACK_TRACE[@]}"]="$frame" - if [[ "$frame" = *"$test_pattern" || \ - "$frame" = *"$setup_pattern" || \ - "$frame" = *"$teardown_pattern" ]]; then - break - else - let index+=1 - fi - done - - BATS_SOURCE="$(bats_frame_filename "${BATS_CURRENT_STACK_TRACE[0]}")" - BATS_LINENO="$(bats_frame_lineno "${BATS_CURRENT_STACK_TRACE[0]}")" -} - -bats_print_stack_trace() { - local frame - local index=1 - local count="${#@}" - - for frame in "$@"; do - local filename="$(bats_trim_filename "$(bats_frame_filename "$frame")")" - local lineno="$(bats_frame_lineno "$frame")" - - if [ $index -eq 1 ]; then - echo -n "# (" - else - echo -n "# " - fi - - local fn="$(bats_frame_function "$frame")" - if [ "$fn" != "$BATS_TEST_NAME" ]; then - echo -n "from function \`$fn' " - fi - - if [ $index -eq $count ]; then - echo "in test file $filename, line $lineno)" - else - echo "in file $filename, line $lineno," - fi - - let index+=1 - done -} - -bats_print_failed_command() { - local frame="$1" - local status="$2" - local filename="$(bats_frame_filename "$frame")" - local lineno="$(bats_frame_lineno "$frame")" - - local failed_line="$(bats_extract_line "$filename" "$lineno")" - local failed_command="$(bats_strip_string "$failed_line")" - echo -n "# \`${failed_command}' " - - if [ $status -eq 1 ]; then - echo "failed" - else - echo "failed with status $status" - fi -} - -bats_frame_lineno() { - local frame="$1" - local lineno="${frame%% *}" - echo "$lineno" -} - -bats_frame_function() { - local frame="$1" - local rest="${frame#* }" - local fn="${rest%% *}" - echo "$fn" -} - -bats_frame_filename() { - local frame="$1" - local rest="${frame#* }" - local filename="${rest#* }" - - if [ "$filename" = "$BATS_TEST_SOURCE" ]; then - echo "$BATS_TEST_FILENAME" - else - echo "$filename" - fi -} - -bats_extract_line() { - local filename="$1" - local lineno="$2" - sed -n "${lineno}p" "$filename" -} - -bats_strip_string() { - local string="$1" - printf "%s" "$string" | sed -e "s/^[ "$'\t'"]*//" -e "s/[ "$'\t'"]*$//" -} - -bats_trim_filename() { - local filename="$1" - local length="${#BATS_CWD}" - - if [ "${filename:0:length+1}" = "${BATS_CWD}/" ]; then - echo "${filename:length+1}" - else - echo "$filename" - fi -} - -bats_debug_trap() { - if [ "$BASH_SOURCE" != "$1" ]; then - bats_capture_stack_trace - fi -} - -bats_error_trap() { - BATS_ERROR_STATUS="$?" - BATS_ERROR_STACK_TRACE=( "${BATS_PREVIOUS_STACK_TRACE[@]}" ) - trap - debug -} - -bats_teardown_trap() { - trap "bats_exit_trap" exit - local status=0 - teardown >>"$BATS_OUT" 2>&1 || status="$?" - - if [ $status -eq 0 ]; then - BATS_TEARDOWN_COMPLETED=1 - elif [ -n "$BATS_TEST_COMPLETED" ]; then - BATS_ERROR_STATUS="$status" - BATS_ERROR_STACK_TRACE=( "${BATS_CURRENT_STACK_TRACE[@]}" ) - fi - - bats_exit_trap -} - -bats_exit_trap() { - local status - local skipped - trap - err exit - - skipped="" - if [ -n "$BATS_TEST_SKIPPED" ]; then - skipped=" # skip" - if [ "1" != "$BATS_TEST_SKIPPED" ]; then - skipped+=" ($BATS_TEST_SKIPPED)" - fi - fi - - if [ -z "$BATS_TEST_COMPLETED" ] || [ -z "$BATS_TEARDOWN_COMPLETED" ]; then - echo "not ok $BATS_TEST_NUMBER $BATS_TEST_DESCRIPTION" >&3 - bats_print_stack_trace "${BATS_ERROR_STACK_TRACE[@]}" >&3 - bats_print_failed_command "${BATS_ERROR_STACK_TRACE[${#BATS_ERROR_STACK_TRACE[@]}-1]}" "$BATS_ERROR_STATUS" >&3 - sed -e "s/^/# /" < "$BATS_OUT" >&3 - status=1 - else - echo "ok ${BATS_TEST_NUMBER}${skipped} ${BATS_TEST_DESCRIPTION}" >&3 - status=0 - fi - - rm -f "$BATS_OUT" - exit "$status" -} - -bats_perform_tests() { - echo "1..$#" - test_number=1 - status=0 - for test_name in "$@"; do - "$0" $BATS_EXTENDED_SYNTAX "$BATS_TEST_FILENAME" "$test_name" "$test_number" || status=1 - let test_number+=1 - done - exit "$status" -} - -bats_perform_test() { - BATS_TEST_NAME="$1" - if [ "$(type -t "$BATS_TEST_NAME" || true)" = "function" ]; then - BATS_TEST_NUMBER="$2" - if [ -z "$BATS_TEST_NUMBER" ]; then - echo "1..1" - BATS_TEST_NUMBER="1" - fi - - BATS_TEST_COMPLETED="" - BATS_TEARDOWN_COMPLETED="" - trap "bats_debug_trap \"\$BASH_SOURCE\"" debug - trap "bats_error_trap" err - trap "bats_teardown_trap" exit - "$BATS_TEST_NAME" >>"$BATS_OUT" 2>&1 - BATS_TEST_COMPLETED=1 - - else - echo "bats: unknown test name \`$BATS_TEST_NAME'" >&2 - exit 1 - fi -} - -if [ -z "$TMPDIR" ]; then - BATS_TMPDIR="/tmp" -else - BATS_TMPDIR="${TMPDIR%/}" -fi - -BATS_TMPNAME="$BATS_TMPDIR/bats.$$" -BATS_PARENT_TMPNAME="$BATS_TMPDIR/bats.$PPID" -BATS_OUT="${BATS_TMPNAME}.out" - -bats_preprocess_source() { - BATS_TEST_SOURCE="${BATS_TMPNAME}.src" - { tr -d '\r' < "$BATS_TEST_FILENAME"; echo; } | bats-preprocess > "$BATS_TEST_SOURCE" - trap "bats_cleanup_preprocessed_source" err exit - trap "bats_cleanup_preprocessed_source; exit 1" int -} - -bats_cleanup_preprocessed_source() { - rm -f "$BATS_TEST_SOURCE" -} - -bats_evaluate_preprocessed_source() { - if [ -z "$BATS_TEST_SOURCE" ]; then - BATS_TEST_SOURCE="${BATS_PARENT_TMPNAME}.src" - fi - source "$BATS_TEST_SOURCE" -} - -exec 3<&1 - -if [ "$#" -eq 0 ]; then - bats_preprocess_source - bats_evaluate_preprocessed_source - - if [ -n "$BATS_COUNT_ONLY" ]; then - echo "${#BATS_TEST_NAMES[@]}" - else - bats_perform_tests "${BATS_TEST_NAMES[@]}" - fi -else - bats_evaluate_preprocessed_source - bats_perform_test "$@" -fi diff --git a/.github/actions/git-secrets/test/bats/libexec/bats-format-tap-stream b/.github/actions/git-secrets/test/bats/libexec/bats-format-tap-stream deleted file mode 100755 index 614768f4d9e..00000000000 --- a/.github/actions/git-secrets/test/bats/libexec/bats-format-tap-stream +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Just stream the TAP output (sans extended syntax) if tput is missing -command -v tput >/dev/null || exec grep -v "^begin " - -header_pattern='[0-9]+\.\.[0-9]+' -IFS= read -r header - -if [[ "$header" =~ $header_pattern ]]; then - count="${header:3}" - index=0 - failures=0 - skipped=0 - name="" - count_column_width=$(( ${#count} * 2 + 2 )) -else - # If the first line isn't a TAP plan, print it and pass the rest through - printf "%s\n" "$header" - exec cat -fi - -update_screen_width() { - screen_width="$(tput cols)" - count_column_left=$(( $screen_width - $count_column_width )) -} - -trap update_screen_width WINCH -update_screen_width - -begin() { - go_to_column 0 - printf_with_truncation $(( $count_column_left - 1 )) " %s" "$name" - clear_to_end_of_line - go_to_column $count_column_left - printf "%${#count}s/${count}" "$index" - go_to_column 1 -} - -pass() { - go_to_column 0 - printf " ✓ %s" "$name" - advance -} - -skip() { - local reason="$1" - [ -z "$reason" ] || reason=": $reason" - go_to_column 0 - printf " - %s (skipped%s)" "$name" "$reason" - advance -} - -fail() { - go_to_column 0 - set_color 1 bold - printf " ✗ %s" "$name" - advance -} - -log() { - set_color 1 - printf " %s\n" "$1" - clear_color -} - -summary() { - printf "\n%d test%s" "$count" "$(plural "$count")" - - printf ", %d failure%s" "$failures" "$(plural "$failures")" - - if [ "$skipped" -gt 0 ]; then - printf ", %d skipped" "$skipped" - fi - - printf "\n" -} - -printf_with_truncation() { - local width="$1" - shift - local string="$(printf "$@")" - - if [ "${#string}" -gt "$width" ]; then - printf "%s..." "${string:0:$(( $width - 4 ))}" - else - printf "%s" "$string" - fi -} - -go_to_column() { - local column="$1" - printf "\x1B[%dG" $(( $column + 1 )) -} - -clear_to_end_of_line() { - printf "\x1B[K" -} - -advance() { - clear_to_end_of_line - echo - clear_color -} - -set_color() { - local color="$1" - local weight="$2" - printf "\x1B[%d;%dm" $(( 30 + $color )) "$( [ "$weight" = "bold" ] && echo 1 || echo 22 )" -} - -clear_color() { - printf "\x1B[0m" -} - -plural() { - [ "$1" -eq 1 ] || echo "s" -} - -_buffer="" - -buffer() { - _buffer="${_buffer}$("$@")" -} - -flush() { - printf "%s" "$_buffer" - _buffer="" -} - -finish() { - flush - printf "\n" -} - -trap finish EXIT - -while IFS= read -r line; do - case "$line" in - "begin "* ) - let index+=1 - name="${line#* $index }" - buffer begin - flush - ;; - "ok "* ) - skip_expr="ok $index # skip (\(([^)]*)\))?" - if [[ "$line" =~ $skip_expr ]]; then - let skipped+=1 - buffer skip "${BASH_REMATCH[2]}" - else - buffer pass - fi - ;; - "not ok "* ) - let failures+=1 - buffer fail - ;; - "# "* ) - buffer log "${line:2}" - ;; - esac -done - -buffer summary diff --git a/.github/actions/git-secrets/test/bats/libexec/bats-preprocess b/.github/actions/git-secrets/test/bats/libexec/bats-preprocess deleted file mode 100755 index 04297ed019b..00000000000 --- a/.github/actions/git-secrets/test/bats/libexec/bats-preprocess +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -set -e - -encode_name() { - local name="$1" - local result="test_" - - if [[ ! "$name" =~ [^[:alnum:]\ _-] ]]; then - name="${name//_/-5f}" - name="${name//-/-2d}" - name="${name// /_}" - result+="$name" - else - local length="${#name}" - local char i - - for ((i=0; i<length; i++)); do - char="${name:$i:1}" - if [ "$char" = " " ]; then - result+="_" - elif [[ "$char" =~ [[:alnum:]] ]]; then - result+="$char" - else - result+="$(printf -- "-%02x" \'"$char")" - fi - done - fi - - echo "$result" -} - -tests=() -index=0 -pattern='^ *@test *([^ ].*) *\{ *(.*)$' - -while IFS= read -r line; do - let index+=1 - if [[ "$line" =~ $pattern ]]; then - quoted_name="${BASH_REMATCH[1]}" - body="${BASH_REMATCH[2]}" - name="$(eval echo "$quoted_name")" - encoded_name="$(encode_name "$name")" - tests["${#tests[@]}"]="$encoded_name" - echo "${encoded_name}() { bats_test_begin ${quoted_name} ${index}; ${body}" - else - printf "%s\n" "$line" - fi -done - -for test_name in "${tests[@]}"; do - echo "bats_test_function ${test_name}" -done diff --git a/.github/actions/git-secrets/test/commit-msg.bats b/.github/actions/git-secrets/test/commit-msg.bats deleted file mode 100644 index 009121952c9..00000000000 --- a/.github/actions/git-secrets/test/commit-msg.bats +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bats -load test_helper - -@test "Rejects commit messages with prohibited patterns" { - setup_good_repo - repo_run git-secrets --install $TEST_REPO - run git commit -m '@todo in the message??' - [ $status -eq 1 ] - [ "${lines[0]}" == ".git/COMMIT_EDITMSG:1:@todo in the message??" ] -} - -@test "Allows commit messages that do not match a prohibited pattern" { - setup_good_repo - repo_run git-secrets --install $TEST_REPO - cd $TEST_REPO - run git commit -m 'This is OK' - [ $status -eq 0 ] -} diff --git a/.github/actions/git-secrets/test/git-secrets.bats b/.github/actions/git-secrets/test/git-secrets.bats deleted file mode 100644 index b7a5b1c0903..00000000000 --- a/.github/actions/git-secrets/test/git-secrets.bats +++ /dev/null @@ -1,361 +0,0 @@ -#!/usr/bin/env bats -load test_helper - -@test "no arguments prints usage instructions" { - repo_run git-secrets - [ $status -eq 0 ] - [ $(expr "${lines[0]}" : "usage: git secrets") -ne 0 ] -} - -@test "-h prints help" { - repo_run git-secrets -h - [ $(expr "${lines[0]}" : "usage: git secrets") -ne 0 ] -} - -@test "Invalid scan filename fails" { - repo_run git-secrets --scan /path/to/not/there - [ $status -eq 2 ] - echo "$output" | grep "No such file" -} - -@test "Does not require secrets" { - git config --unset-all secrets.patterns || true - repo_run git-secrets --scan $BATS_TEST_FILENAME - [ $status -eq 0 ] -} - -@test "No prohibited matches exits 0" { - echo 'it is ok' > "$BATS_TMPDIR/test.txt" - repo_run git-secrets --scan "$BATS_TMPDIR/test.txt" - [ $status -eq 0 ] -} - -@test "Scans all files when no file provided" { - setup_bad_repo - repo_run git-secrets --scan - [ $status -eq 1 ] -} - -@test "Scans all files including history" { - setup_bad_repo - repo_run git-secrets --scan-history - [ $status -eq 1 ] -} - -@test "Scans all files when no file provided with secret in history" { - setup_bad_repo_history - repo_run git-secrets --scan - [ $status -eq 0 ] -} - -@test "Scans all files including history with secret in history" { - setup_bad_repo_history - repo_run git-secrets --scan-history - [ $status -eq 1 ] -} - -@test "Scans history with secrets distributed among branches in history" { - cd $TEST_REPO - echo '@todo' > $TEST_REPO/history_failure.txt - git add -A - git commit -m "Testing history" - echo 'todo' > $TEST_REPO/history_failure.txt - git add -A - git commit -m "Testing history" - git checkout -b testbranch - echo '@todo' > $TEST_REPO/history_failure.txt - git add -A - git commit -m "Testing history" - git checkout master - cd - - repo_run git-secrets --scan-history - [ $status -eq 1 ] -} - -@test "Scans recursively" { - setup_bad_repo - mkdir -p $TEST_REPO/foo/bar/baz - echo '@todo more stuff' > $TEST_REPO/foo/bar/baz/data.txt - repo_run git-secrets --scan -r $TEST_REPO/foo - [ $status -eq 1 ] -} - -@test "Scans recursively only if -r is given" { - setup_bad_repo - mkdir -p $TEST_REPO/foo/bar/baz - echo '@todo more stuff' > $TEST_REPO/foo/bar/baz/data.txt - repo_run git-secrets --scan $TEST_REPO/foo - [ $status -eq 0 ] -} - -@test "Excludes allowed patterns from failures" { - git config --add secrets.patterns 'foo="baz{1,5}"' - git config --add secrets.allowed 'foo="bazzz"' - echo 'foo="bazzz" is ok because 3 "z"s' > "$BATS_TMPDIR/test.txt" - repo_run git-secrets --scan "$BATS_TMPDIR/test.txt" - [ $status -eq 0 ] - echo 'This is NOT: ok foo="bazzzz"' > "$BATS_TMPDIR/test.txt" - repo_run git-secrets --scan "$BATS_TMPDIR/test.txt" - [ $status -eq 1 ] -} - -@test "Prohibited matches exits 1" { - file="$TEST_REPO/test.txt" - echo '@todo stuff' > $file - echo 'this is forbidden right?' >> $file - repo_run git-secrets --scan $file - [ $status -eq 1 ] - [ "${lines[0]}" == "$file:1:@todo stuff" ] - [ "${lines[1]}" == "$file:2:this is forbidden right?" ] -} - -@test "Only matches on word boundaries" { - file="$TEST_REPO/test.txt" - # Note that the following does not match as it is not a word. - echo 'mesa Jar Jar Binks' > $file - # The following do match because they are in word boundaries. - echo 'foo.me' >> $file - echo '"me"' >> $file - repo_run git-secrets --scan $file - [ $status -eq 1 ] - [ "${lines[0]}" == "$file:2:foo.me" ] - [ "${lines[1]}" == "$file:3:\"me\"" ] -} - -@test "Can scan from stdin using -" { - echo "foo" | "${BATS_TEST_DIRNAME}/../git-secrets" --scan - - echo "me" | "${BATS_TEST_DIRNAME}/../git-secrets" --scan - && exit 1 || true -} - -@test "installs hooks for repo" { - setup_bad_repo - repo_run git-secrets --install $TEST_REPO - [ -f $TEST_REPO/.git/hooks/pre-commit ] - [ -f $TEST_REPO/.git/hooks/prepare-commit-msg ] - [ -f $TEST_REPO/.git/hooks/commit-msg ] -} - -@test "fails if hook exists and no -f" { - repo_run git-secrets --install $TEST_REPO - repo_run git-secrets --install $TEST_REPO - [ $status -eq 1 ] -} - -@test "Overwrites hooks if -f is given" { - repo_run git-secrets --install $TEST_REPO - repo_run git-secrets --install -f $TEST_REPO - [ $status -eq 0 ] -} - -@test "installs hooks for repo with Debian style directories" { - setup_bad_repo - mkdir $TEST_REPO/.git/hooks/pre-commit.d - mkdir $TEST_REPO/.git/hooks/prepare-commit-msg.d - mkdir $TEST_REPO/.git/hooks/commit-msg.d - run git-secrets --install $TEST_REPO - [ -f $TEST_REPO/.git/hooks/pre-commit.d/git-secrets ] - [ -f $TEST_REPO/.git/hooks/prepare-commit-msg.d/git-secrets ] - [ -f $TEST_REPO/.git/hooks/commit-msg.d/git-secrets ] -} - -@test "installs hooks to template directory" { - setup_bad_repo - run git-secrets --install $TEMPLATE_DIR - [ $status -eq 0 ] - run git init --template $TEMPLATE_DIR - [ $status -eq 0 ] - [ -f "${TEST_REPO}/.git/hooks/pre-commit" ] - [ -f "${TEST_REPO}/.git/hooks/prepare-commit-msg" ] - [ -f "${TEST_REPO}/.git/hooks/commit-msg" ] -} - -@test "Scans using keys from credentials file" { - echo 'aws_access_key_id = abc123' > $BATS_TMPDIR/test.ini - echo 'aws_secret_access_key=foobaz' >> $BATS_TMPDIR/test.ini - echo 'aws_access_key_id = "Bernard"' >> $BATS_TMPDIR/test.ini - echo 'aws_secret_access_key= "Laverne"' >> $BATS_TMPDIR/test.ini - echo 'aws_access_key_id= Hoagie+man' >> $BATS_TMPDIR/test.ini - cd $TEST_REPO - run git secrets --aws-provider $BATS_TMPDIR/test.ini - [ $status -eq 0 ] - echo "$output" | grep -F "foobaz" - echo "$output" | grep -F "abc123" - echo "$output" | grep -F "Bernard" - echo "$output" | grep -F "Laverne" - echo "$output" | grep -F 'Hoagie\+man' - run git secrets --add-provider -- git secrets --aws-provider $BATS_TMPDIR/test.ini - [ $status -eq 0 ] - echo '(foobaz) test' > $TEST_REPO/bad_file - echo "abc123 test" >> $TEST_REPO/bad_file - echo 'Bernard test' >> $TEST_REPO/bad_file - echo 'Laverne test' >> $TEST_REPO/bad_file - echo 'Hoagie+man test' >> $TEST_REPO/bad_file - repo_run git-secrets --scan $TEST_REPO/bad_file - [ $status -eq 1 ] - echo "$output" | grep "foobaz" - echo "$output" | grep "abc123" - echo "$output" | grep "Bernard" - echo "$output" | grep "Laverne" - echo "$output" | grep -F 'Hoagie+man' -} - -@test "Lists secrets for a repo" { - repo_run git-secrets --list - [ $status -eq 0 ] - echo "$output" | grep -F 'secrets.patterns @todo' - echo "$output" | grep -F 'secrets.patterns forbidden|me' -} - -@test "Adds secrets to a repo and de-dedupes" { - repo_run git-secrets --add 'testing+123' - [ $status -eq 0 ] - repo_run git-secrets --add 'testing+123' - [ $status -eq 1 ] - repo_run git-secrets --add --literal 'testing+abc' - [ $status -eq 0 ] - repo_run git-secrets --add -l 'testing+abc' - [ $status -eq 1 ] - repo_run git-secrets --list - echo "$output" | grep -F 'secrets.patterns @todo' - echo "$output" | grep -F 'secrets.patterns forbidden|me' - echo "$output" | grep -F 'secrets.patterns testing+123' - echo "$output" | grep -F 'secrets.patterns testing\+abc' -} - -@test "Adds allowed patterns to a repo and de-dedupes" { - repo_run git-secrets --add -a 'testing+123' - [ $status -eq 0 ] - repo_run git-secrets --add --allowed 'testing+123' - [ $status -eq 1 ] - repo_run git-secrets --add -a -l 'testing+abc' - [ $status -eq 0 ] - repo_run git-secrets --add -a -l 'testing+abc' - [ $status -eq 1 ] - repo_run git-secrets --list - echo "$output" | grep -F 'secrets.patterns @todo' - echo "$output" | grep -F 'secrets.patterns forbidden|me' - echo "$output" | grep -F 'secrets.allowed testing+123' - echo "$output" | grep -F 'secrets.allowed testing\+abc' -} - -@test "Empty lines must be ignored in .gitallowed files" { - setup_bad_repo - echo '' >> $TEST_REPO/.gitallowed - repo_run git-secrets --scan - [ $status -eq 1 ] -} - -@test "Comment lines must be ignored in .gitallowed files" { - setup_bad_repo_with_hash - repo_run git-secrets --scan - [ $status -eq 1 ] - echo '#hash' > $TEST_REPO/.gitallowed - repo_run git-secrets --scan - [ $status -eq 1 ] - echo 'hash' > $TEST_REPO/.gitallowed - repo_run git-secrets --scan - [ $status -eq 0 ] -} - -@test "Scans all files and allowing none of the bad patterns in .gitallowed" { - setup_bad_repo - echo 'hello' > $TEST_REPO/.gitallowed - repo_run git-secrets --scan - [ $status -eq 1 ] -} - -@test "Scans all files and allowing all bad patterns in .gitallowed" { - setup_bad_repo - echo '@todo' > $TEST_REPO/.gitallowed - echo 'forbidden' >> $TEST_REPO/.gitallowed - echo 'me' >> $TEST_REPO/.gitallowed - repo_run git-secrets --scan - [ $status -eq 0 ] -} - -@test "Adds common AWS patterns" { - repo_run git config --unset-all secrets - repo_run git-secrets --register-aws - git config --local --get secrets.providers - repo_run git-secrets --list - echo "$output" | grep -F '(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}' - echo "$output" | grep "AKIAIOSFODNN7EXAMPLE" - echo "$output" | grep "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" -} - -@test "Adds providers" { - repo_run git-secrets --add-provider -- echo foo baz bar - [ $status -eq 0 ] - repo_run git-secrets --add-provider -- echo bam - [ $status -eq 0 ] - repo_run git-secrets --list - echo "$output" | grep -F 'echo foo baz bar' - echo "$output" | grep -F 'echo bam' - echo 'foo baz bar' > $TEST_REPO/bad_file - echo 'bam' >> $TEST_REPO/bad_file - repo_run git-secrets --scan $TEST_REPO/bad_file - [ $status -eq 1 ] - echo "$output" | grep -F 'foo baz bar' - echo "$output" | grep -F 'bam' -} - -@test "Strips providers that return nothing" { - repo_run git-secrets --add-provider -- 'echo' - [ $status -eq 0 ] - repo_run git-secrets --add-provider -- 'echo 123' - [ $status -eq 0 ] - repo_run git-secrets --list - echo "$output" | grep -F 'echo 123' - echo 'foo' > $TEST_REPO/bad_file - repo_run git-secrets --scan $TEST_REPO/bad_file - [ $status -eq 0 ] -} - -@test "--recursive cannot be used with SCAN_*" { - repo_run git-secrets --scan -r --cached - [ $status -eq 1 ] - repo_run git-secrets --scan -r --no-index - [ $status -eq 1 ] - repo_run git-secrets --scan -r --untracked - [ $status -eq 1 ] -} - -@test "--recursive can be used with --scan" { - repo_run git-secrets --scan -r - [ $status -eq 0 ] -} - -@test "--recursive can't be used with --list" { - repo_run git-secrets --list -r - [ $status -eq 1 ] -} - -@test "-f can only be used with --install" { - repo_run git-secrets --scan -f - [ $status -eq 1 ] -} - -@test "-a can only be used with --add" { - repo_run git-secrets --scan -a - [ $status -eq 1 ] -} - -@test "-l can only be used with --add" { - repo_run git-secrets --scan -l - [ $status -eq 1 ] -} - -@test "--cached can only be used with --scan" { - repo_run git-secrets --list --cached - [ $status -eq 1 ] -} - -@test "--no-index can only be used with --scan" { - repo_run git-secrets --list --no-index - [ $status -eq 1 ] -} - -@test "--untracked can only be used with --scan" { - repo_run git-secrets --list --untracked - [ $status -eq 1 ] -} diff --git a/.github/actions/git-secrets/test/pre-commit.bats b/.github/actions/git-secrets/test/pre-commit.bats deleted file mode 100644 index 5ace267cbb3..00000000000 --- a/.github/actions/git-secrets/test/pre-commit.bats +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bats -load test_helper - -@test "Rejects commits with prohibited patterns in changeset" { - setup_bad_repo - repo_run git-secrets --install $TEST_REPO - cd $TEST_REPO - run git commit -m 'Contents are bad not the message' - [ $status -eq 1 ] - [ "${lines[0]}" == "data.txt:1:@todo more stuff" ] - [ "${lines[1]}" == "failure1.txt:1:another line... forbidden" ] - [ "${lines[2]}" == "failure2.txt:1:me" ] -} - -@test "Rejects commits with prohibited patterns in changeset with filename that contain spaces" { - setup_bad_repo_with_spaces - repo_run git-secrets --install $TEST_REPO - cd $TEST_REPO - run git commit -m 'Contents are bad not the message' - [ $status -eq 1 ] - [ "${lines[0]}" == "da ta.txt:1:@todo more stuff" ] -} - -@test "Scans staged files" { - cd $TEST_REPO - repo_run git-secrets --install $TEST_REPO - echo '@todo more stuff' > $TEST_REPO/data.txt - echo 'hi there' > $TEST_REPO/ok.txt - git add -A - echo 'fixed the working directory, but not staged' > $TEST_REPO/data.txt - run git commit -m 'Contents are bad not the message' - [ $status -eq 1 ] - [ "${lines[0]}" == "data.txt:1:@todo more stuff" ] -} - -@test "Allows commits that do not match prohibited patterns" { - setup_good_repo - repo_run git-secrets --install $TEST_REPO - cd $TEST_REPO - run git commit -m 'This is fine' - [ $status -eq 0 ] - # Ensure deleted files are filtered out of the grep - rm $TEST_REPO/data.txt - echo 'aaa' > $TEST_REPO/data_2.txt - run git add -A - run git commit -m 'This is also fine' - [ $status -eq 0 ] -} - -@test "Rejects commits with prohibited patterns in changeset when AWS provider is enabled" { - setup_bad_repo - repo_run git-secrets --install $TEST_REPO - repo_run git-secrets --register-aws $TEST_REPO - cd $TEST_REPO - run git commit -m 'Contents are bad not the message' - [ $status -eq 1 ] - echo "${lines}" | grep -vq 'git secrets --aws-provider: command not found' - - [ "${lines[0]}" == "data.txt:1:@todo more stuff" ] - [ "${lines[1]}" == "failure1.txt:1:another line... forbidden" ] - [ "${lines[2]}" == "failure2.txt:1:me" ] -} diff --git a/.github/actions/git-secrets/test/prepare-commit-msg.bats b/.github/actions/git-secrets/test/prepare-commit-msg.bats deleted file mode 100644 index a211c1318a2..00000000000 --- a/.github/actions/git-secrets/test/prepare-commit-msg.bats +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bats -load test_helper - -@test "Rejects merges with prohibited patterns in history" { - setup_good_repo - repo_run git-secrets --install $TEST_REPO - cd $TEST_REPO - git commit -m 'OK' - git checkout -b feature - echo '@todo' > data.txt - git add -A - git commit -m 'Bad commit' --no-verify - echo 'Fixing!' > data.txt - git add -A - git commit -m 'Fixing commit' - git checkout master - run git merge --no-ff feature - [ $status -eq 1 ] -} - -@test "Allows merges that do not match prohibited patterns" { - setup_good_repo - cd $TEST_REPO - repo_run git-secrets --install - git commit -m 'OK' - git checkout -b feature - echo 'Not bad' > data.txt - git add -A - git commit -m 'Good commit' - git checkout master - run git merge --no-ff feature - [ $status -eq 0 ] -} diff --git a/.github/actions/git-secrets/test/test_helper.bash b/.github/actions/git-secrets/test/test_helper.bash deleted file mode 100644 index 9133e5162ec..00000000000 --- a/.github/actions/git-secrets/test/test_helper.bash +++ /dev/null @@ -1,94 +0,0 @@ -#!/bin/bash -export TEST_REPO="$BATS_TMPDIR/test-repo" -export TEMP_HOME="$BATS_TMPDIR/home" -export TEMPLATE_DIR="${BATS_TMPDIR}/template" -INITIAL_PATH="${PATH}" -INITIAL_HOME=${HOME} - -setup() { - setup_repo - [ -d "${TEMPLATE_DIR}" ] && rm -rf "${TEMPLATE_DIR}" - [ -d "${TEMP_HOME}" ] && rm -rf "${TEMP_HOME}" - mkdir -p $TEMP_HOME - export HOME=$TEMP_HOME - export PATH="${BATS_TEST_DIRNAME}/..:${INITIAL_PATH}" - cd $TEST_REPO -} - -teardown() { - delete_repo - export PATH="${INITIAL_PATH}" - export HOME="${INITIAL_HOME}" - [ -d "${TEMP_HOME}" ] && rm -rf "${TEMP_HOME}" -} - -delete_repo() { - [ -d $TEST_REPO ] && rm -rf $TEST_REPO || true -} - -setup_repo() { - delete_repo - mkdir -p $TEST_REPO - cd $TEST_REPO - git init - git config --local --add secrets.patterns '@todo' - git config --local --add secrets.patterns 'forbidden|me' - git config --local --add secrets.patterns '#hash' - git config --local user.email "you@example.com" - git config --local user.name "Your Name" - cd - -} - -repo_run() { - cmd="$1" - shift - cd "${TEST_REPO}" - run "${BATS_TEST_DIRNAME}/../${cmd}" $@ - cd - -} - -# Creates a repo that should fail -setup_bad_repo() { - cd $TEST_REPO - echo '@todo more stuff' > $TEST_REPO/data.txt - echo 'hi there' > $TEST_REPO/ok.txt - echo 'another line... forbidden' > $TEST_REPO/failure1.txt - echo 'me' > $TEST_REPO/failure2.txt - git add -A - cd - -} - -# Creates a repo that should fail -setup_bad_repo_with_spaces() { - cd $TEST_REPO - echo '@todo more stuff' > "$TEST_REPO/da ta.txt" - git add -A - cd - -} - -# Creates a repo that should fail -setup_bad_repo_with_hash() { - cd $TEST_REPO - echo '#hash' > "$TEST_REPO/data.txt" - git add -A - cd - -} - -# Creates a repo that should fail -setup_bad_repo_history() { - cd $TEST_REPO - echo '@todo' > $TEST_REPO/history_failure.txt - git add -A - git commit -m "Testing history" - echo 'todo' > $TEST_REPO/history_failure.txt - git add -A - cd - -} - -# Creates a repo that does not fail -setup_good_repo() { - cd $TEST_REPO - echo 'hello!' > $TEST_REPO/data.txt - git add -A - cd - -} diff --git a/.github/actions/randomrepo/.github/workflows/main.yml b/.github/actions/randomrepo/.github/workflows/main.yml deleted file mode 100644 index 1888987d707..00000000000 --- a/.github/actions/randomrepo/.github/workflows/main.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Fetch Random Repo Name - -on: [push] - -jobs: - test_run: - runs-on: ubuntu-latest - name: A job to fetch a random repo name - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Fetch random repo name - uses: ./ - id: random - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Get the repo name - run: | - echo "Random repo name: ${{ steps.random.outputs.repo }}" diff --git a/.github/actions/randomrepo/Dockerfile b/.github/actions/randomrepo/Dockerfile deleted file mode 100644 index f2cefc6b1a2..00000000000 --- a/.github/actions/randomrepo/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -# Container image that runs your code -FROM curlimages/curl - -# Install GitHub CLI -USER root -COPY /lib/install.sh /lib/install.sh -RUN ["sh", "/lib/install.sh"] - -# Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh - -# Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["sh", "/entrypoint.sh"] \ No newline at end of file diff --git a/.github/actions/randomrepo/README.md b/.github/actions/randomrepo/README.md deleted file mode 100644 index e4813fe7fca..00000000000 --- a/.github/actions/randomrepo/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Fetch random repo name - -This action is useful for running actions against random repositories (e.g. leak scanner, etc.) - -## Inputs - -`github-token` - -**Required** Token used to authenticate with GitHub - -## Outputs - -`repo` - -The name of the random repo. diff --git a/.github/actions/randomrepo/action.yml b/.github/actions/randomrepo/action.yml deleted file mode 100644 index 537a98e6542..00000000000 --- a/.github/actions/randomrepo/action.yml +++ /dev/null @@ -1,18 +0,0 @@ -# action.yml -name: 'randomrepo-action' -description: 'Fetch name of a random GitHub repository' -branding: - icon: 'search' - color: 'yellow' -inputs: - github-token: - description: 'Token used to login to GitHub' - required: true -outputs: - repo: - description: 'Name of random repo' -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.github-token }} diff --git a/.github/actions/randomrepo/entrypoint.sh b/.github/actions/randomrepo/entrypoint.sh deleted file mode 100644 index 79d8fc7b128..00000000000 --- a/.github/actions/randomrepo/entrypoint.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -echo "$1" >auth.txt -gh auth login --with-token <auth.txt -if [[ $? -ne 0 ]]; then - echo "GitHub auth failed" - exit 1 -fi - -random=$(< /dev/urandom tr -dc A-Za-z0-9_ | head -c1) - -repo=$(gh search repos $random \ ---sort updated \ ---order desc \ ---visibility public \ ---limit 1 \ ---include-forks false \ ---stars=">=10" \ ---size=">=500" \ ---json fullName,updatedAt,url,pushedAt \ ---jq '.[].fullName') - -echo "repo=$repo" >> $GITHUB_OUTPUT -echo $repo diff --git a/.github/actions/randomrepo/lib/install.sh b/.github/actions/randomrepo/lib/install.sh deleted file mode 100644 index fb5b051cba1..00000000000 --- a/.github/actions/randomrepo/lib/install.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# Install latest GitHub CLI -VERSION=`curl "https://api.github.com/repos/cli/cli/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/' | cut -c2-` -echo $VERSION -curl -sSL https://github.com/cli/cli/releases/download/v${VERSION}/gh_${VERSION}_linux_amd64.tar.gz -o gh_${VERSION}_linux_amd64.tar.gz -tar xvf gh_${VERSION}_linux_amd64.tar.gz -cp gh_${VERSION}_linux_amd64/bin/gh /usr/local/bin/ diff --git a/.github/actions/rapid-wsl/.gitignore b/.github/actions/rapid-wsl/.gitignore deleted file mode 100644 index 699ff4165b5..00000000000 --- a/.github/actions/rapid-wsl/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -backups/* -etc/* -!etc/sample.gitconfig -courgette.log -bin/* -.env -*.deb -nohup.out diff --git a/.github/actions/rapid-wsl/README.md b/.github/actions/rapid-wsl/README.md deleted file mode 100644 index 34abe5fb27d..00000000000 --- a/.github/actions/rapid-wsl/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# Rapid WSL - - - -[](https://open.vscode.dev/JosiahSiegel/rapid-wsl) - ->⚠️Disable Docker Desktop resource saver due to instability - -## Synopsis - -Easily manage your WSL environment with a handful of generic commands. -Applications are seperated into [modules](modules/). -Remote repository is cloned into distro if specified (`-r`). - -## Quick start - -```sh -./rwsl init -d "Ubuntu-18.04" -m "generic" # -d:Distro, -m:Module, -u:User, -r:Repo -``` -or -```sh -./rwsl init $(cat defaults/generic.sh) -``` - -## Security - -Place `.pgp` & `.gitconfig` files into the `etc/` directory (*ignored by git*). - -Example `.gitconfig`: - -```sh -[core] - editor = "vim" -[user] - name = First Last - email = my@email.com - signingKey = xxx - -[commit] - gpgSign = True -``` - -How to import pgp files from keybase.io: - -```sh -keybase pgp export -q xxx > etc/public.pgp -keybase pgp export -q xxx --secret > etc/private.pgp -``` - -For GitHub authorization, add GitHub personal access token file `git.token` to `etc/`. - -## Commands - -1. `init` (Re)initialize env. -2. `status` Status of env. -3. `backup` Backup env to `.tar` file. -4. `down` Shutdown env. -5. `up` Bring env up after shutdown. -6. `destroy` Destroy env. -7. `restore` Restore env from `.tar` file. -8. `test` Run module specific env tests. -9. `fix` Run module specific env fixes. - -The following scripts can be run in various order, but some run orders are not compatible (e.g. *destroy then test*): - -```sh -d="Ubuntu"; # Distro name -m="generic"; # Module name - -./rwsl init -d $d -m $m; -./rwsl status -d $d -m $m; -./rwsl backup -d $d -m $m; -./rwsl down -d $d -m $m; -./rwsl up -d $d -m $m; -./rwsl destroy -d $d -m $m; -./rwsl restore -d $d -m $m; -./rwsl test -d $d -m $m; - -``` ---- - -**Mandatory:** - * `-d`, `--distro` VAL Distro name (*reference: wsl --list --online*) - * `-m`, `--module` VAL Application name - -**Optional:** - * `-u`, `--user` VAL username - * `-r`, `--repo` VAL Repository url - * `-h`, `--help` Prints this help - -**Examples:** - * `./rwsl [COMMAND] -d VAL -m VAL -u VAL` - * `./rwsl init -d 'Ubuntu-18.04' -m 'generic' -u 'user3' -r 'https://github.com/JosiahSiegel/rapid-wsl.git'` - -## Tips - -Quick re-use arguments: - 1. Create `defaults/my_args.sh` file - * `-d Ubuntu-20.04 -m generic -u user3` - 2. Run script using default file - * `./rwsl init $(cat defaults/my_args.sh)` - -## Notes - -Currently, only tested using Git Bash via Windows Terminal. diff --git a/.github/actions/rapid-wsl/defaults/demo.sh b/.github/actions/rapid-wsl/defaults/demo.sh deleted file mode 100644 index dd1eac9b43f..00000000000 --- a/.github/actions/rapid-wsl/defaults/demo.sh +++ /dev/null @@ -1 +0,0 @@ --d Ubuntu-22.04 -m demo -u demouser -r 'https://github.com/JosiahSiegel/terraform-templates.git' \ No newline at end of file diff --git a/.github/actions/rapid-wsl/defaults/generic.sh b/.github/actions/rapid-wsl/defaults/generic.sh deleted file mode 100644 index abd8896fc1b..00000000000 --- a/.github/actions/rapid-wsl/defaults/generic.sh +++ /dev/null @@ -1 +0,0 @@ --d Ubuntu -m generic -u user \ No newline at end of file diff --git a/.github/actions/rapid-wsl/defaults/macos.sh b/.github/actions/rapid-wsl/defaults/macos.sh deleted file mode 100644 index 2065b58fddc..00000000000 --- a/.github/actions/rapid-wsl/defaults/macos.sh +++ /dev/null @@ -1 +0,0 @@ --d Ubuntu-22.04 -m macos -u macuser \ No newline at end of file diff --git a/.github/actions/rapid-wsl/defaults/prime-data-ingestion.sh b/.github/actions/rapid-wsl/defaults/prime-data-ingestion.sh deleted file mode 100644 index b9d5039f1c1..00000000000 --- a/.github/actions/rapid-wsl/defaults/prime-data-ingestion.sh +++ /dev/null @@ -1 +0,0 @@ --d Ubuntu-18.04 -m prime-data-ingestion -u primeuser \ No newline at end of file diff --git a/.github/actions/rapid-wsl/defaults/prime-reportstream.sh b/.github/actions/rapid-wsl/defaults/prime-reportstream.sh deleted file mode 100644 index 5907cf8956b..00000000000 --- a/.github/actions/rapid-wsl/defaults/prime-reportstream.sh +++ /dev/null @@ -1 +0,0 @@ --d Ubuntu-20.04 -m prime-reportstream -u primeuser \ No newline at end of file diff --git a/.github/actions/rapid-wsl/etc/sample.gitconfig b/.github/actions/rapid-wsl/etc/sample.gitconfig deleted file mode 100644 index c274e17793d..00000000000 --- a/.github/actions/rapid-wsl/etc/sample.gitconfig +++ /dev/null @@ -1,9 +0,0 @@ -[core] - editor = "vim" -[user] - name = First Last - email = my@email.com - signingKey = xxx - -[commit] - gpgSign = True diff --git a/.github/actions/rapid-wsl/lib/args.sh b/.github/actions/rapid-wsl/lib/args.sh deleted file mode 100644 index 37e6d9c4b52..00000000000 --- a/.github/actions/rapid-wsl/lib/args.sh +++ /dev/null @@ -1,163 +0,0 @@ -#!/bin/bash - -source ./lib/controller.sh - -#Declare the number of mandatory args -margs=2 - -# Common functions - BEGIN -function example { - echo -e "example: ./rwsl [COMMAND] -d VAL -m VAL -u VAL -r VAL" - echo -e "example: ./rwsl init -d 'Ubuntu-18.04' -m 'generic' -u 'user3' -r 'https://github.com/JosiahSiegel/rapid-wsl.git'" -} - -function usage { - echo -e "usage: ./rwsl [COMMAND] MANDATORY [OPTION]\n" -} - -function help { - usage - echo -e "MANDATORY:" - echo -e " -d, --distro VAL Distro name (*reference: wsl --list --online*)" - echo -e " -m, --module VAL Application name\n" - echo -e "OPTION:" - echo -e " -u, --user VAL Username" - echo -e " -r, --repo VAL Repository url" - echo -e " -h, --help Prints this help\n" - example -} - -# Ensures that the number of passed args are at least equals -# to the declared number of mandatory args. -# It also handles the special case of the -h or --help arg. -function margs_precheck { - if [ $2 ] && [ $1 -lt $margs ]; then - if [ $2 == "--help" ] || [ $2 == "-h" ]; then - help - exit - else - usage - example - exit 1 # error - fi - fi -} - -# Ensures that all the mandatory args are not empty -function margs_check { - if [ $# -lt $margs ]; then - usage - example - exit 1 # error - fi -} -# Common functions - END - -# Main -margs_precheck $# $1 - -distro= -module= -user="user" -repo= - -i=0 -command="" -# Args while-loop -while [ "$1" != "" ]; do - ((i++)) - case $1 in - -d | --distro) - shift - distro=$1 - ;; - -m | --module) - shift - module=$1 - ;; - -u | --user) - shift - user=$1 - ;; - -r | --repo) - shift - repo=$1 - ;; - -h | --help) - help - exit - ;; - *) - if [ $i -eq 1 ]; then - case $1 in - -h | --help) - help - exit - ;; - init) - command="init" - ;; - destroy) - command="destroy" - ;; - install) - command="install" - ;; - docker) - command="docker" - ;; - setup) - command="setup" - ;; - test) - command="test" - ;; - test-pipe) - command="test-pipe" - ;; - enter) - command="enter" - ;; - backup) - command="backup" - ;; - down) - command="down" - ;; - fix) - command="fix" - ;; - restore) - command="restore" - ;; - status) - command="status" - ;; - up) - command="up" - ;; - tips) - command="tips" - ;; - *) - echo "[COMMAND]: illegal option $1" - usage - example - exit 1 # error - ;; - esac - else - echo "[COMMAND]: illegal option $1" - usage - example - exit 1 # error - fi - ;; - esac - shift -done - -# Pass here your mandatory args for check -margs_check $distro $module -echo "$distro $module $user $repo" -eval $command "$distro" "$module" "$user" "$repo" diff --git a/.github/actions/rapid-wsl/lib/commands/backup_distro.sh b/.github/actions/rapid-wsl/lib/commands/backup_distro.sh deleted file mode 100644 index 6a6c5739881..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/backup_distro.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -echo "Creating backup: backups\\$1-$2-backup.tar" -wsl --export $1 'backups\'"$1"'-'"$2"'-backup.tar' -echo "Backup complete" diff --git a/.github/actions/rapid-wsl/lib/commands/destroy_distro.sh b/.github/actions/rapid-wsl/lib/commands/destroy_distro.sh deleted file mode 100644 index ae0c24fe29b..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/destroy_distro.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -./lib/commands/down_distro.sh $1 $2 $3 $4 - -wsl --unregister $1 - -if [[ ${?} == 0 ]]; then - sleep 5 -fi diff --git a/.github/actions/rapid-wsl/lib/commands/docker_distro.sh b/.github/actions/rapid-wsl/lib/commands/docker_distro.sh deleted file mode 100644 index bbdd829c9c7..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/docker_distro.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -wsl --set-default $1 - -echo "Restarting docker" -powershell -c 'Stop-Process -Name "Docker Desktop"' -sleep 1 -dpath=$(which docker | sed 's/\/resources\/bin\/docker/\/Docker Desktop.exe/g') -"$dpath" -sleep 1 - -echo "Waiting for docker..." -wsl -d $1 -e bash -c \ -' \ -until usermod -aG docker '"$3"'; do sleep 5; done \ -' &>/dev/null - -wsl -d $1 -u $3 -e bash -c \ -' \ -until docker info; do sleep 5; done \ -' &>/dev/null - -echo "Docker restart complete" - -FILE=./modules/$2/docker_distro.sh -if [[ -f "$FILE" ]]; then - echo "Updating module specific docker" - $FILE $1 $2 $3 $4 - echo "Module specific docker update complete" -fi diff --git a/.github/actions/rapid-wsl/lib/commands/down_distro.sh b/.github/actions/rapid-wsl/lib/commands/down_distro.sh deleted file mode 100644 index 108415f1f1c..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/down_distro.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -FILE=./modules/$2/down_distro.sh -if [[ -f "$FILE" ]]; then - echo "Running module specific shutdown" - $FILE $1 $2 $3 $4 &>/dev/null - echo "Completed module specific shutdown" -fi diff --git a/.github/actions/rapid-wsl/lib/commands/enter_distro.sh b/.github/actions/rapid-wsl/lib/commands/enter_distro.sh deleted file mode 100644 index 8d84c48a561..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/enter_distro.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - - -if [ ! -z "$4" ] -then - wsl -d $1 -u $3 -e bash -c \ - ' \ - mkdir -p ~/repos/; \ - cd ~/repos/; \ - repo_url='"$4"'; \ - repo_name='"$(basename $4 .git)"'; \ - cd $repo_name; \ - code .; \ - ' -fi - -wsl -d $1 -u $3 -e bash diff --git a/.github/actions/rapid-wsl/lib/commands/fix_distro.sh b/.github/actions/rapid-wsl/lib/commands/fix_distro.sh deleted file mode 100644 index e63a5a1eb05..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/fix_distro.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -FILE=./modules/$2/fix_distro.sh -if [[ -f "$FILE" ]]; then - echo "Running module specific fixes" - $FILE $1 $2 $3 $4 - echo "Completed module specific fixes" -fi diff --git a/.github/actions/rapid-wsl/lib/commands/git_setup.sh b/.github/actions/rapid-wsl/lib/commands/git_setup.sh deleted file mode 100644 index 5814e2939e7..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/git_setup.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -echo "" -echo "***" -echo "Setting up GitHub personal access token" - -TKN_FILES=(./etc/git.token) -if [[ -e "${TKN_FILES[0]}" ]]; then - echo "Importing ./etc/git.token file:" - echo "***" - echo "" - wsl -d $1 -u $3 -e bash -c \ - ' \ - path=$(wslpath -u '"$PWD"')/etc/; \ - path=$(echo $path | sed "s3/c/c/3/c/3g"); \ - gh auth login --with-token < $path/git.token; \ - ' - echo "" - echo "***" - echo "Import complete" - echo "***" - echo "" -else - echo "No etc/git.token file found" - echo "GitHub personal access token > etc/git.token" - echo "***" - echo "" -fi diff --git a/.github/actions/rapid-wsl/lib/commands/gpg_import.sh b/.github/actions/rapid-wsl/lib/commands/gpg_import.sh deleted file mode 100644 index 3dcbb85215f..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/gpg_import.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -echo "" -echo "***" -echo "Setting up gpg" - -PGP_FILES=(./etc/*.pgp) -GIT_CONFIG=(./etc/.gitconfig) -if [[ -e "${PGP_FILES[0]}" ]]; then - if [[ -f "${GIT_CONFIG}" ]]; then - echo "Importing etc/*.pgp files:" - echo "***" - echo "" - wsl -d $1 -u $3 -e bash -c \ - ' \ - path=$(wslpath -u '"$PWD"')/etc/; \ - path=$(echo $path | sed "s3/c/c/3/c/3g"); \ - gpg --pinentry-mode loopback --import $path/*.pgp; \ - cp $path/.gitconfig ~/.gitconfig; \ - cp $path/.gitconfig /home/'"$3"'/.gitconfig; \ - sudo chown -R '"$3"':'"$3"' /home/'"$3"'/.gitconfig; \ - echo "export GPG_TTY=\$(tty)" >>~/.bashrc; \ - ' - echo "" - echo "***" - echo "Import complete" - echo "***" - echo "" - else - echo "No etc/.gitconfig file found" - echo "***" - cat "etc/sample.gitconfig" - echo "***" - echo "" - fi -else - echo "No etc/*.pgp files found" - echo "keybase pgp export -q xxx > etc/public.pgp" - echo "keybase pgp export -q xxx --secret > etc/private.pgp" - echo "***" - echo "" -fi diff --git a/.github/actions/rapid-wsl/lib/commands/init_distro.sh b/.github/actions/rapid-wsl/lib/commands/init_distro.sh deleted file mode 100644 index d11614c44b9..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/init_distro.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -source ./lib/waiting.sh - -echo ' -################################ -# env init # -################################ -' - -echo "(Re)initializing raw distro" -./rwsl destroy -d $1 -m $2 -u $3 -r $4 - -./rwsl install -d $1 -m $2 -u $3 -r $4 - -echo ' -################################ -# docker integration # -################################ -' - -echo "Preparing docker desktop...(if installed)" -./rwsl docker -d $1 -m $2 -u $3 -r $4 - -echo ' -################################ -# env setup # -################################ -' - -echo "Preparing env..." -./rwsl setup -d $1 -m $2 -u $3 -r $4 - -echo ' -################################ -# run tests # -################################ -' - -echo "Running test...(if exists)" -./rwsl test -d $1 -m $2 -u $3 -r $4 - -echo ' -################################ -# enter distro # -################################ -' - -echo "Entering distro..." -./rwsl enter -d $1 -m $2 -u $3 -r $4 diff --git a/.github/actions/rapid-wsl/lib/commands/install_distro.sh b/.github/actions/rapid-wsl/lib/commands/install_distro.sh deleted file mode 100644 index 4521aaacb7e..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/install_distro.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -source ./lib/waiting.sh - -nohup wsl --install -d $1 & -sleep 30 -wsl -d $1 -e bash -c \ -' \ -poweroff -f \ -' -wsl.exe --shutdown -sleep 10 -wsl --manage $1 --set-sparse true - -echo "Creating dev env" -echo "" -echo "***" -echo "CHANGE ROOT PASSWORD FROM 'temp'!" -echo "User '"$3"'" -echo "***" -echo "" - -wsl -d $1 -e bash -c \ -' \ -echo "root:temp" | chpasswd; \ -useradd -m '"$3"'; \ -echo "'"$3"' ALL=(ALL:ALL) NOPASSWD: ALL" | tee /etc/sudoers.d/'"$3"'; \ -chown -R '"$3"':'"$3"' /home/'"$3"'/; \ -curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg; \ -echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null; \ -apt-get update; \ -apt-get install gh -y; \ -echo "nameserver 1.1.1.1" >> /etc/resolv.conf; \ -' - -./lib/commands/gpg_import.sh $1 $2 $3 $4 -./lib/commands/git_setup.sh $1 $2 $3 $4 - -wsl -d $1 -u $3 -e bash -c \ -' \ -echo "cd ~" >>~/.bashrc; \ -' - -# https://github.com/microsoft/WSL/issues/8022 -wsl -d $1 -e bash -c \ -' \ -echo -e "[network] \ngenerateResolvConf = false" > /etc/wsl.conf; \ -' -wsl --terminate $1 -wsl -d $1 -e bash -c \ -' \ -rm -f /etc/resolv.conf; \ -echo "nameserver 8.8.8.8" > /etc/resolv.conf; \ -' - -FILE=./modules/$2/install_distro.sh -if [[ -f "$FILE" ]]; then - echo "Running module specific install" - $FILE $1 $2 $3 $4 &>/dev/null & waiting - echo "Completed module specific install" -fi diff --git a/.github/actions/rapid-wsl/lib/commands/restore_distro.sh b/.github/actions/rapid-wsl/lib/commands/restore_distro.sh deleted file mode 100644 index f2b752518e2..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/restore_distro.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -echo "Restoring backup: backups\\$1-$2-backup.tar" -wsl --import $1 'bin\'"$1"''-''"$2"'\' 'backups\'"$1"'-'"$2"'-backup.tar' - -if [[ ${?} == 0 ]]; then - ./lib/commands/up_distro.sh $1 $2 $3 $4 - echo "Restore complete" -fi diff --git a/.github/actions/rapid-wsl/lib/commands/setup_distro.sh b/.github/actions/rapid-wsl/lib/commands/setup_distro.sh deleted file mode 100644 index c4493079e3f..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/setup_distro.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -if [ -z "$4" ] -then - echo "Repository url is empty" -else - echo "Repository url is $4" - - wsl -d $1 -u $3 -e bash -c \ - ' \ - mkdir -p ~/repos/; \ - cd ~/repos/; \ - repo_url='"$4"'; \ - repo_name='"$(basename $4 .git)"'; \ - git clone $repo_url; \ - git config --global --add safe.directory $repo_name; \ - sudo chown -R '"$3"':'"$3"' .; \ - echo "cd ~/repos/$repo_name/" >>~/.bashrc; \ - ' -fi - -FILE=./modules/$2/setup_distro.sh -if [[ -f "$FILE" ]]; then - echo "Running module specific setup" - $FILE $1 $2 $3 $4 #>/dev/null - echo "Completed module specific setup" -fi diff --git a/.github/actions/rapid-wsl/lib/commands/status_distro.sh b/.github/actions/rapid-wsl/lib/commands/status_distro.sh deleted file mode 100644 index 02376554f47..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/status_distro.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -echo ' -################################ -# installed distros # -################################ -' -wsl -l --all - -echo ' -################################ -# images # -################################ -' -docker images - -echo ' -################################ -# containers # -################################ -' -docker ps -q | xargs -i sh -c '\ -echo "========================================================="; \ -echo "========================================================="; \ -docker ps --filter "id={}" --format "{{.Names}}"; \ -docker logs --tail 8 {};' - -echo ' -################################ -# application # -################################ -' - -FILE=./modules/$2/status_distro.sh -if [[ -f "$FILE" ]]; then - echo "Fetching module specific application status" - $FILE $1 $2 $3 $4 - echo "Completed module specific application status" -fi diff --git a/.github/actions/rapid-wsl/lib/commands/test-pipe_distro.sh b/.github/actions/rapid-wsl/lib/commands/test-pipe_distro.sh deleted file mode 100644 index a690c137cf9..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/test-pipe_distro.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -FILE=./modules/$2/test-pipe_distro.sh -if [[ -f "$FILE" ]]; then - echo "Running module specific pipeline test" - $FILE $1 $2 $3 $4 - echo "Completed module specific pipeline test" -fi diff --git a/.github/actions/rapid-wsl/lib/commands/test_distro.sh b/.github/actions/rapid-wsl/lib/commands/test_distro.sh deleted file mode 100644 index 31834a8d819..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/test_distro.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -FILE=./modules/$2/test_distro.sh -if [[ -f "$FILE" ]]; then - echo "Running module specific test" - $FILE $1 $2 $3 $4 - echo "Completed module specific test" -fi diff --git a/.github/actions/rapid-wsl/lib/commands/tips_distro.sh b/.github/actions/rapid-wsl/lib/commands/tips_distro.sh deleted file mode 100644 index 6c827087e8d..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/tips_distro.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -echo -e " -======================= - -# GENERIC TIPS - -======================= - -## Resolve merge conflicts - -### Find conflict files: -grep -lr '<<<<<<<' . - -### Accept local/ours version: -git checkout --ours PATH/FILE - -### For multiple files: -grep -lr '<<<<<<<' . | xargs git checkout --ours - -### Accept remote/theirs version: -git checkout --theirs PATH/FILE - -### For multiple files: -grep -lr '<<<<<<<' . | xargs git checkout --theirs - -## Resolve failing git fetch: -rm -f .git/FETCH_HEAD -======================= -" - -FILE=./modules/$2/tips_distro.sh -if [[ -f $FILE ]]; then - echo -e "# MODULE SPECIFIC TIPS\n" - echo -e "=======================" - $FILE $1 $2 $3 $4 -fi diff --git a/.github/actions/rapid-wsl/lib/commands/up_distro.sh b/.github/actions/rapid-wsl/lib/commands/up_distro.sh deleted file mode 100644 index 4a738bcdd67..00000000000 --- a/.github/actions/rapid-wsl/lib/commands/up_distro.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -./lib/commands/docker_distro.sh $1 $2 $3 $4 - -FILE=./modules/$2/up_distro.sh -if [[ -f "$FILE" ]]; then - echo "Running module specific startup" - $FILE $1 $2 $3 $4 - echo "Completed module specific startup" -fi - -./lib/commands/enter_distro.sh $1 $2 $3 $4 diff --git a/.github/actions/rapid-wsl/lib/controller.sh b/.github/actions/rapid-wsl/lib/controller.sh deleted file mode 100644 index a95aa00effc..00000000000 --- a/.github/actions/rapid-wsl/lib/controller.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/bash - -source ./lib/waiting.sh - -COMMAND_PATH=./lib/commands/ -COMMAND="\${COMMAND_PATH}\${FUNCNAME[0]}_distro.sh \$1 \$2 \$3 \$4" - -init() { - eval ${COMMAND} -} - -destroy() { - eval ${COMMAND} & waiting -} - -install() { - eval ${COMMAND} -} - -docker() { - eval ${COMMAND} & waiting -} - -setup() { - eval ${COMMAND} -} - -test() { - eval ${COMMAND} -} - -test-pipe() { - eval ${COMMAND} -} - -enter() { - eval ${COMMAND} -} - -backup() { - eval ${COMMAND} & waiting -} - -down() { - eval ${COMMAND} & waiting -} - -fix() { - eval ${COMMAND} -} - -restore() { - eval ${COMMAND} & waiting -} - -status() { - eval ${COMMAND} & waiting -} - -up() { - eval ${COMMAND} -} - -tips() { - eval ${COMMAND} -} diff --git a/.github/actions/rapid-wsl/lib/waiting.sh b/.github/actions/rapid-wsl/lib/waiting.sh deleted file mode 100644 index ccd1e093989..00000000000 --- a/.github/actions/rapid-wsl/lib/waiting.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -waiting() { - pid=$! - - spin='-\|/' - - START_TIME=$SECONDS - i=0 - while kill -0 $pid 2>/dev/null; do - i=$(((i + 1) % 4)) - printf "\r${spin:$i:1} " - sleep .1 - done - ELAPSED_TIME=$(($SECONDS - $START_TIME)) - echo -e "\n$(($ELAPSED_TIME/60)) min $(($ELAPSED_TIME%60)) sec" -} diff --git a/.github/actions/rapid-wsl/modules/README.md b/.github/actions/rapid-wsl/modules/README.md deleted file mode 100644 index e094bec82d0..00000000000 --- a/.github/actions/rapid-wsl/modules/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Modules - -A few open-source projects are included. -By default, new modules are ignored. diff --git a/.github/actions/rapid-wsl/modules/demo/install_distro.sh b/.github/actions/rapid-wsl/modules/demo/install_distro.sh deleted file mode 100644 index 12f396991ff..00000000000 --- a/.github/actions/rapid-wsl/modules/demo/install_distro.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -wsl -d $1 -e bash -c \ -' \ -touch /home/'"$3"'/.hushlogin; \ -touch /home/'"$3"'/.landscape; \ -touch /home/'"$3"'/.motd_shown; \ -apt update; \ -apt -y upgrade; \ -apt --yes install lsb-release gpg; \ -curl https://packages.microsoft.com/keys/microsoft.asc \ - | gpg --dearmor \ - | tee "/etc/apt/trusted.gpg.d/microsoft.gpg"; \ -echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" \ - | tee "/etc/apt/sources.list.d/dotnetdev.list"; \ -apt update; \ -apt remove azure-cli -y && apt autoremove -y; \ -curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash; \ -apt -y install unzip; \ -apt update; \ -curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add -; \ -apt-add-repository "deb [arch=$(dpkg --print-architecture)] https://apt.releases.hashicorp.com $(lsb_release -cs) main"; \ -apt install terraform -y; \ -apt install strongswan -y; \ -apt install strongswan-pki -y; \ -apt install make -y; \ -apt install jq -y; \ -' diff --git a/.github/actions/rapid-wsl/modules/generic/README.md b/.github/actions/rapid-wsl/modules/generic/README.md deleted file mode 100644 index 804b07a0d75..00000000000 --- a/.github/actions/rapid-wsl/modules/generic/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# generic - -```sh -d="Ubuntu"; # Distro name -m="generic"; # Module name - -./rwsl init -d $d -m $m; -./rwsl status -d $d -m $m; -./rwsl backup -d $d -m $m; -./rwsl down -d $d -m $m; -./rwsl up -d $d -m $m; -./rwsl destroy -d $d -m $m; -./rwsl restore -d $d -m $m; -./rwsl test -d $d -m $m; - -``` ---- diff --git a/.github/actions/rapid-wsl/modules/macos/install_distro.sh b/.github/actions/rapid-wsl/modules/macos/install_distro.sh deleted file mode 100644 index 355391d6a70..00000000000 --- a/.github/actions/rapid-wsl/modules/macos/install_distro.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -wsl -d $1 -e bash -c \ -' \ -touch /home/'"$3"'/.hushlogin; \ -touch /home/'"$3"'/.landscape; \ -touch /home/'"$3"'/.motd_shown; \ -add-apt-repository -y ppa:apt-fast/stable; \ -apt-get update; \ -DEBIAN_FRONTEND=noninteractive apt-get install -y apt-fast; \ -echo debconf apt-fast/maxdownloads string 16 | debconf-set-selections; \ -echo debconf apt-fast/dlflag boolean true | debconf-set-selections; \ -echo debconf apt-fast/aptmanager string apt-get | debconf-set-selections; \ -echo "alias apt-get='\''apt-fast'\''" >> ~/.bashrc; \ -apt update; \ -apt-get update; \ -apt-get -y upgrade; \ -apt-get update; \ -apt-get install cmake automake clang-15 bison flex libfuse-dev libudev-dev pkg-config libc6-dev-i386 \ -gcc-multilib libcairo2-dev libgl1-mesa-dev curl libglu1-mesa-dev libtiff5-dev \ -libfreetype6-dev git git-lfs libelf-dev libxml2-dev libegl1-mesa-dev libfontconfig1-dev \ -libbsd-dev libxrandr-dev libxcursor-dev libgif-dev libavutil-dev libpulse-dev \ -libavformat-dev libavcodec-dev libswresample-dev libdbus-1-dev libxkbfile-dev \ -libssl-dev libstdc++-12-dev -y \ -apt-get update; \ -apt-get install make -y; \ -apt-get install jq -y; \ -' diff --git a/.github/actions/rapid-wsl/modules/macos/setup_distro.sh b/.github/actions/rapid-wsl/modules/macos/setup_distro.sh deleted file mode 100644 index 17be50fe920..00000000000 --- a/.github/actions/rapid-wsl/modules/macos/setup_distro.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -wsl -d $1 -u $3 -e bash -c \ -' \ -git clone --recursive https://github.com/darlinghq/darling.git \ -cd darling \ -tools/uninstall \ -mkdir build && cd build \ -cmake .. \ -make -j4 \ -sudo make install \ -darling shell \ -sudo rm -rf /Library/Developer/CommandLineTools \ -sudo xcode-select --install \ -curl -fsSL -o install.sh https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh \ -NONINTERACTIVE=1 /bin/bash install.sh \ -(echo; echo 'eval "$(/usr/local/bin/brew shellenv)"') >> /Users/macuser/.profile \ -eval "$(/usr/local/bin/brew shellenv)" \ -brew tap homebrew/core \ -brew install gradle \ -brew install docker \ -brew install docker-compose \ -brew install openjdk@17 postgresql -' diff --git a/.github/actions/rapid-wsl/modules/prime-data-ingestion/README.md b/.github/actions/rapid-wsl/modules/prime-data-ingestion/README.md deleted file mode 100644 index 4d8fd26b96a..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-data-ingestion/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# [prime-data-ingestion](https://github.com/CDCgov/prime-data-ingestion) - -```sh -d="Ubuntu-18.04"; # Distro name -m="prime-data-ingestion"; # Module name -u="primeuser"; # Username - -./rwsl init -d $d -m $m -u $u; -./rwsl status -d $d -m $m -u $u; -./rwsl backup -d $d -m $m -u $u; -./rwsl down -d $d -m $m -u $u; -./rwsl up -d $d -m $m -u $u; -./rwsl destroy -d $d -m $m -u $u; -./rwsl restore -d $d -m $m -u $u; -./rwsl test -d $d -m $m -u $u; - -``` ---- diff --git a/.github/actions/rapid-wsl/modules/prime-data-ingestion/install_distro.sh b/.github/actions/rapid-wsl/modules/prime-data-ingestion/install_distro.sh deleted file mode 100644 index 6331536052b..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-data-ingestion/install_distro.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -wsl -d $1 -e bash -c \ -' \ -touch /home/'"$3"'/.hushlogin; \ -touch /home/'"$3"'/.landscape; \ -touch /home/'"$3"'/.motd_shown; \ -add-apt-repository -y ppa:apt-fast/stable; \ -apt-get update; \ -DEBIAN_FRONTEND=noninteractive apt-get install -y apt-fast; \ -echo debconf apt-fast/maxdownloads string 16 | debconf-set-selections; \ -echo debconf apt-fast/dlflag boolean true | debconf-set-selections; \ -echo debconf apt-fast/aptmanager string apt-get | debconf-set-selections; \ -echo "alias apt-get='\''apt-fast'\''" >> ~/.bashrc; \ -apt update; \ -apt-get update; \ -apt-get -y upgrade; \ -apt -y install openjdk-11-jdk; \ -apt-get --yes install lsb-release gpg; \ -curl https://packages.microsoft.com/keys/microsoft.asc \ - | gpg --dearmor \ - | tee "/etc/apt/trusted.gpg.d/microsoft.gpg"; \ -echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" \ - | tee "/etc/apt/sources.list.d/dotnetdev.list"; \ -apt-get update; \ -apt-get --yes install azure-functions-core-tools-3; \ -apt remove azure-cli -y && apt autoremove -y; \ -curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash; \ -apt-get install python3.6 -y; \ -apt-get install python3-venv -y; \ -apt-get -y install unzip; \ -apt -y install gradle; \ -wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -; \ -echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list; \ -apt-get update; \ -apt-get -y install postgresql-11; \ -curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose; \ -curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add -; \ -apt-add-repository "deb [arch=$(dpkg --print-architecture)] https://apt.releases.hashicorp.com $(lsb_release -cs) main"; \ -apt install terraform=1.0.5 -y; \ -apt-get install make -y; \ -chmod +x /usr/local/bin/docker-compose; \ -curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash; \ -' diff --git a/.github/actions/rapid-wsl/modules/prime-data-ingestion/setup_distro.sh b/.github/actions/rapid-wsl/modules/prime-data-ingestion/setup_distro.sh deleted file mode 100644 index 13a24ecf8e2..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-data-ingestion/setup_distro.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -wsl -d $1 -u $3 -e bash -c \ -' \ -docker --version; \ -git --version; \ -az --version; \ -mkdir -p ~/repos/; \ -cd ~/repos/; \ -git clone https://github.com/CDCgov/prime-public-health-data-infrastructure.git; \ -cd prime-public-health-data-infrastructure/; \ -python3 -m venv .venv; \ -source .venv/bin/activate; \ -sudo chown -R '"$3"':'"$3"' .; \ -echo "cd ~/repos/prime-public-health-data-infrastructure/" >>~/.bashrc; \ -' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/README.md b/.github/actions/rapid-wsl/modules/prime-reportstream/README.md deleted file mode 100644 index 4434b96a816..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-reportstream/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# [prime-reportstream](https://github.com/CDCgov/prime-reportstream) - -```sh -d="Ubuntu-20.04"; # Distro name -m="prime-reportstream"; # Module name -u="primeuser"; # Username - -./rwsl init -d $d -m $m -u $u; -./rwsl status -d $d -m $m -u $u; -./rwsl backup -d $d -m $m -u $u; -./rwsl down -d $d -m $m -u $u; -./rwsl up -d $d -m $m -u $u; -./rwsl destroy -d $d -m $m -u $u; -./rwsl restore -d $d -m $m -u $u; -./rwsl test -d $d -m $m -u $u; - -``` ---- diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/down_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/down_distro.sh deleted file mode 100644 index 6548eb67d22..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-reportstream/down_distro.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -wsl -d $1 -u $3 -e bash -c \ -' \ -cd ~/repos/prime-reportstream/prime-router/; \ -./devenv-infrastructure.sh down; \ -docker-compose down --remove-orphans; \ -' - -docker stop $(docker ps -a --format "{{.ID}} {{.Names}}" | grep prime-router.*) -docker rm -f $(docker ps -a --format "{{.ID}} {{.Names}}" | grep prime-router.*) diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/fix_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/fix_distro.sh deleted file mode 100644 index e0ce6c7e640..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-reportstream/fix_distro.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -wsl -d $1 -u $3 -e bash -c \ -' \ -cd ~/repos/prime-reportstream/prime-router/; \ -./cleanslate.sh --prune-volumes; \ -' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/install_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/install_distro.sh deleted file mode 100644 index eb47327cf99..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-reportstream/install_distro.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -wsl -d $1 -e bash -c \ -' \ -touch /home/'"$3"'/.hushlogin; \ -touch /home/'"$3"'/.landscape; \ -touch /home/'"$3"'/.motd_shown; \ -add-apt-repository -y ppa:apt-fast/stable; \ -apt-get update; \ -DEBIAN_FRONTEND=noninteractive apt-get install -y apt-fast; \ -echo debconf apt-fast/maxdownloads string 16 | debconf-set-selections; \ -echo debconf apt-fast/dlflag boolean true | debconf-set-selections; \ -echo debconf apt-fast/aptmanager string apt-get | debconf-set-selections; \ -echo "alias apt-get='\''apt-fast'\''" >> ~/.bashrc; \ -apt update; \ -apt-get update; \ -apt-get -y upgrade; \ -apt -y install openjdk-17-jdk; \ -apt-get --yes install lsb-release gpg; \ -curl https://packages.microsoft.com/keys/microsoft.asc \ - | gpg --dearmor \ - | tee "/etc/apt/trusted.gpg.d/microsoft.gpg"; \ -echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" \ - | tee "/etc/apt/sources.list.d/dotnetdev.list"; \ -apt-get update; \ -apt-get --yes install azure-functions-core-tools-4; \ -apt remove azure-cli -y && apt autoremove -y; \ -curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash; \ -apt-get -y install unzip; \ -apt -y install gradle; \ -wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -; \ -echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list; \ -apt-get update; \ -apt-get -y install postgresql-11; \ -curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose; \ -curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add -; \ -apt-add-repository "deb [arch=$(dpkg --print-architecture)] https://apt.releases.hashicorp.com $(lsb_release -cs) main"; \ -apt install terraform=1.0.5 -y; \ -apt-get install strongswan -y; \ -apt-get install strongswan-pki -y; \ -apt-get install make -y; \ -apt-get install jq -y; \ -chmod +x /usr/local/bin/docker-compose; \ -curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash; \ -' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/setup_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/setup_distro.sh deleted file mode 100644 index 9496aa4fe2f..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-reportstream/setup_distro.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -wsl -d $1 -u $3 -e bash -c \ -' \ -gradle --version; \ -docker --version; \ -git --version; \ -java --version; \ -psql --version; \ -az --version; \ -docker-compose --version; \ -mkdir -p ~/repos/; \ -cd ~/repos/; \ -git clone --filter=tree:0 https://github.com/CDCgov/prime-reportstream.git; \ -git config --global --add safe.directory prime-reportstream; \ -cd prime-reportstream/; \ -sudo chown -R '"$3"':'"$3"' .; \ -cd prime-router/; \ -echo "cleanslate.sh started"; \ -./cleanslate.sh --prune-volumes; \ -echo "cleanslate.sh finished"; \ -export $(xargs < .vault/env/.env.local); \ -echo "docker-compose started"; \ -docker-compose --file "docker-compose.build.yml" up --detach; \ -echo "docker-compose finished"; \ -echo "devenv-infrastructure.sh started"; \ -./devenv-infrastructure.sh; \ -echo "devenv-infrastructure.sh finished"; \ -./prime create-credential --type=UserPass \ - --persist=DEFAULT-SFTP \ - --user foo \ - --pass pass; \ -./prime multiple-settings \ - set --silent --input settings/organizations.yml; \ -cd ../; \ -echo "cd ~/repos/prime-reportstream/" >>~/.bashrc; \ -cp -n operations/app/src/environments/configurations/dev-sample.tfbackend operations/app/src/environments/configurations/dev.tfbackend; \ -cp -n operations/app/src/environments/configurations/dev-sample.tfvars operations/app/src/environments/configurations/dev.tfvars; \ -cp -n operations/app/src/environments/configurations/dev.tfbackend /mnt'${PWD}'/etc/; \ -cp -n operations/app/src/environments/configurations/dev.tfvars /mnt'${PWD}'/etc/; \ -cp /mnt'${PWD}'/etc/dev.tfbackend operations/app/src/environments/configurations/; \ -cp /mnt'${PWD}'/etc/dev.tfvars operations/app/src/environments/configurations/; \ -sudo chown -R '"$3"':'"$3"' .; \ -' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/status_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/status_distro.sh deleted file mode 100644 index 557a514fbb9..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-reportstream/status_distro.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -wsl -d $1 -u $3 -e bash -c \ -' \ -cd ~/repos/prime-reportstream/prime-router/; \ -./gradlew ; \ -' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/test-pipe_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/test-pipe_distro.sh deleted file mode 100644 index 481fd09353a..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-reportstream/test-pipe_distro.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -wsl -d $1 -u $3 -e bash -c \ -' \ -cd ~/repos/prime-reportstream/; \ -sudo chown -R '"$3"':'"$3"' .git; \ -act -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04 -W .github/workflows/release.yml --graph; \ -' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/test_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/test_distro.sh deleted file mode 100644 index 945615ddd83..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-reportstream/test_distro.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -wsl -d $1 -u $3 -e bash -c \ -' \ -cd ~/repos/prime-reportstream/prime-router/; \ -DB_USER='\''prime'\''; \ -DB_PASSWORD='\''changeIT!'\''; \ -./gradlew ktlintCheck; \ -docker-compose -f docker-compose.postgres.yml up -d; \ -./gradlew package -x fatjar -Pshowtests; \ -mkdir -p .vault/env; \ -touch .vault/env/.env.local; \ -docker-compose -f docker-compose.yml up -d vault; \ -sleep 30; \ -docker-compose -f docker-compose.yml up -d prime_dev sftp azurite azurite-stage; \ -./gradlew reloadTables; \ -./gradlew reloadSettings; \ -./gradlew testIntegration -Pshowtests; \ -docker-compose exec -T sftp chmod 777 /home/foo/upload; \ -export $(xargs < .vault/env/.env.local); \ -./prime create-credential --type=UserPass --persist=DEFAULT-SFTP --user foo --pass pass; \ -./gradlew testSmoke; \ -' diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/tips_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/tips_distro.sh deleted file mode 100644 index a89a2404634..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-reportstream/tips_distro.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -echo -e " -## Run local terraform options: - -1. -(cd operations/app/src/environments/01-network/ && \ -sed -i -e 's/backend \"azurerm\"/backend \"local\"/g' main.tf && \ -terraform init && ../tf --verbose -c \"validate\") - -2. -(cd operations/ make tf-cmd TF_STAGE=\"01-network\" TF_CMD=\"tf validate\") - -## Run local frontend: - -sudo apt-get install node -y -sudo apt-get install npm -y -sudo npm install -g n -n 14 - -sudo apt remove cmdtest -sudo apt remove yarn -curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - -echo \"deb https://dl.yarnpkg.com/debian/ stable main\" | sudo tee /etc/apt/sources.list.d/yarn.list -sudo apt-get update -sudo apt-get install yarn -y - -sudo npm install pm2 -g -pm2 --name HelloWorld start npm -- start - -#pm2 ps -#pm2 delete 0 -#pm2 logs -" diff --git a/.github/actions/rapid-wsl/modules/prime-reportstream/up_distro.sh b/.github/actions/rapid-wsl/modules/prime-reportstream/up_distro.sh deleted file mode 100644 index 09fffa53b0d..00000000000 --- a/.github/actions/rapid-wsl/modules/prime-reportstream/up_distro.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -wsl -d $1 -u $3 -e bash -c \ -' \ -cd ~/repos/prime-reportstream/prime-router; \ -docker-compose --file "docker-compose.build.yml" up --detach; \ -./devenv-infrastructure.sh up; \ -cd ../; \ -' diff --git a/.github/actions/rapid-wsl/rwsl b/.github/actions/rapid-wsl/rwsl deleted file mode 100644 index 64549a1ae4d..00000000000 --- a/.github/actions/rapid-wsl/rwsl +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -source lib/waiting.sh -source lib/args.sh diff --git a/.github/actions/reliable-pull-request-action/.github/workflows/test-action.yml b/.github/actions/reliable-pull-request-action/.github/workflows/test-action.yml deleted file mode 100644 index 9d09461dbd3..00000000000 --- a/.github/actions/reliable-pull-request-action/.github/workflows/test-action.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Test Action - -on: - push: - branches-ignore: - - 'main' - -jobs: - test-action: - name: Test create PR on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest] - steps: - - name: Checkout the repo - uses: actions/checkout@v4.1.1 - - - name: Create Pull Request - id: create_pr - uses: ./ - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - title: 'Automated Pull Request' - sourceBranch: ${{ github.ref_name }} - targetBranch: 'main' - body: | - ## Merge ${{ github.ref_name }} - This PR contains the changes from ${{ github.ref_name }}. - labels: 'invalid,wontfix' - assignees: ${{ github.actor }} - - - name: Output PR URL - run: echo "The PR URL is ${{ steps.create_pr.outputs.PRURL }}" diff --git a/.github/actions/reliable-pull-request-action/LICENSE b/.github/actions/reliable-pull-request-action/LICENSE deleted file mode 100644 index ade79f7960a..00000000000 --- a/.github/actions/reliable-pull-request-action/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Josiah Siegel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.github/actions/reliable-pull-request-action/README.md b/.github/actions/reliable-pull-request-action/README.md deleted file mode 100644 index f2a5da85084..00000000000 --- a/.github/actions/reliable-pull-request-action/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# Reliable* Pull Request Action - -> *Only uses built-in GitHub runner commands - -[](https://github.com/CDCgov/reliable-pull-request-action/actions/workflows/test-action.yml) - -## Synopsis - -1. Create a pull request on a GitHub repository using existing branches. -2. [actions/checkout](https://github.com/actions/checkout) determins the active repo. - -## Usage - -```yml -jobs: - create-pr: - name: Test create PR on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest] - steps: - - name: Checkout the repo - uses: actions/checkout@v4.1.1 - - - name: Create Pull Request - id: create_pr - uses: .github/actions/reliable-pull-request-action - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - title: 'Automated Pull Request' - sourceBranch: ${{ github.ref_name }} - targetBranch: 'main' - body: 'This is an automated pull request.' - labels: 'automated,pr' - assignees: 'octocat' - - - name: Output PR URL - run: echo "The PR URL is ${{ steps.create_pr.outputs.PRURL }}" -``` - -## Inputs - -```yml -inputs: - title: - description: 'Pull Request Title' - required: true - sourceBranch: - description: 'Source Branch Name' - required: true - targetBranch: - description: 'Target Branch Name' - required: true - body: - description: 'Pull Request Body' - required: false - labels: - description: 'Labels (comma-separated)' - required: false - assignees: - description: 'Assignees (comma-separated)' - required: false -``` - -## Outputs -```yml -outputs: - PRURL: - description: 'The URL of the created pull request' -``` - -## Requirements - -The following permissions must be set for the repository: - * `Settings > Actions > General` - * Workflow permissions - 1. Read and write permissions - 2. Allow GitHub Actions to create and approve pull requests - 3. Save - ->*Alternative is to set [jobs.<job_id>.permissions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idpermissions)* diff --git a/.github/actions/reliable-pull-request-action/action.yml b/.github/actions/reliable-pull-request-action/action.yml deleted file mode 100644 index 4fcda57b318..00000000000 --- a/.github/actions/reliable-pull-request-action/action.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Reliable Pull Request Action -description: Creates a pull request on a GitHub repository using existing branches -branding: - icon: 'git-pull-request' - color: 'blue' -inputs: - title: - description: 'Pull Request Title' - required: true - sourceBranch: - description: 'Source Branch Name' - required: true - targetBranch: - description: 'Target Branch Name' - required: true - body: - description: 'Pull Request Body' - required: false - labels: - description: 'Labels (comma-separated)' - required: false - assignees: - description: 'Assignees (comma-separated)' - required: false -outputs: - PRURL: - description: 'The URL of the created pull request' - value: ${{ steps.create_pr.outputs.PR_URL }} -runs: - using: 'composite' - steps: - - name: Create Pull Request - id: create_pr - shell: bash - run: bash ${{github.action_path}}/create-pr.sh - env: - INPUT_TITLE: ${{ inputs.title }} - INPUT_SOURCEBRANCH: ${{ inputs.sourceBranch }} - INPUT_TARGETBRANCH: ${{ inputs.targetBranch }} - INPUT_BODY: ${{ inputs.body }} - INPUT_LABELS: ${{ inputs.labels }} - INPUT_ASSIGNEES: ${{ inputs.assignees }} diff --git a/.github/actions/reliable-pull-request-action/create-pr.sh b/.github/actions/reliable-pull-request-action/create-pr.sh deleted file mode 100644 index c6046c0582d..00000000000 --- a/.github/actions/reliable-pull-request-action/create-pr.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -# Create Pull Request and capture the output -PR_OUTPUT=$(gh pr create \ - --title "$INPUT_TITLE" \ - --body "$INPUT_BODY" \ - --base "$INPUT_TARGETBRANCH" \ - --head "$INPUT_SOURCEBRANCH" \ - --label "$INPUT_LABELS" \ - --assignee "$INPUT_ASSIGNEES" 2>&1) - -# Extract PR URL from the output -PR_URL=$(echo "$PR_OUTPUT" | grep -o 'https://github.com/[^ ]*') - -# Set the PR URL as the output -echo "PR_URL=$PR_URL" >> $GITHUB_OUTPUT diff --git a/.github/actions/remote-branch-action/.github/workflows/test-action.yml b/.github/actions/remote-branch-action/.github/workflows/test-action.yml deleted file mode 100644 index 1a0e4c25e1e..00000000000 --- a/.github/actions/remote-branch-action/.github/workflows/test-action.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Test Action - -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - test-action: - name: Test create branch on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest] - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Checkout second repo - uses: actions/checkout@v4 - with: - sparse-checkout: . - repository: josiahsiegel/rapid-wsl - token: ${{ secrets.SECONDARY_REPO_TOKEN }} - path: second-repo - - - name: Test action - id: test-action - uses: ./ - with: - branch: test-action - - - name: Test action on second repo - id: test-action-second-repo - uses: ./ - with: - branch: test-action-second-repo - path: second-repo - - - name: Get create branch status - if: steps.test-action.outputs.create-status != 'false' - run: echo ${{ steps.test-action.outputs.create-status }} - - - name: Get create branch status on second repo - if: steps.test-action-second-repo.outputs.create-status != 'false' - run: echo ${{ steps.test-action-second-repo.outputs.create-status }} diff --git a/.github/actions/remote-branch-action/LICENSE b/.github/actions/remote-branch-action/LICENSE deleted file mode 100644 index ade79f7960a..00000000000 --- a/.github/actions/remote-branch-action/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Josiah Siegel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.github/actions/remote-branch-action/README.md b/.github/actions/remote-branch-action/README.md deleted file mode 100644 index c7543163db3..00000000000 --- a/.github/actions/remote-branch-action/README.md +++ /dev/null @@ -1,108 +0,0 @@ -# Remote Branch Action - -[](https://github.com/CDCgov/remote-branch-action/actions/workflows/test-action.yml) - -## Synopsis - -1. Create a branch on a remote repository. -2. [actions/checkout](https://github.com/actions/checkout) determins the active repo. - -## Usage - -### Single repo -```yml -jobs: - create-branch-action: - name: Create branch - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Create branch - uses: .github/actions/remote-branch-action - with: - branch: new-branch -``` -### Single alternative repo -```yml -jobs: - create-branch-action: - name: Create branch - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Checkout alt repo - uses: actions/checkout@v4 - with: - sparse-checkout: . - repository: me/alt-repo - token: ${{ secrets.ALT_REPO_TOKEN }} - path: alt-repo - - - name: Create branch on alt repo - uses: .github/actions/remote-branch-action - with: - branch: new-branch-alt-repo - path: alt-repo -``` -### Multiple repos -```yml -jobs: - create-branch-action: - name: Create branch - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Checkout second repo - uses: actions/checkout@v4 - with: - sparse-checkout: . - repository: me/second-repo - token: ${{ secrets.SECONDARY_REPO_TOKEN }} - path: second-repo - - - name: Create branch - id: create-branch-action - uses: .github/actions/remote-branch-action - with: - branch: new-branch - - - name: Create branch on second repo - id: create-branch-action-second-repo - uses: .github/actions/remote-branch-action - with: - branch: new-branch-second-repo - path: second-repo - - - name: Get create branch status - run: echo ${{ steps.create-branch-action.outputs.create-status }} - - - name: Get create branch status on second repo - run: echo ${{ steps.create-branch-action-second-repo.outputs.create-status }} -``` - -## Inputs - -```yml -inputs: - branch: - description: Branch name - required: true - path: - description: Relative path under $GITHUB_WORKSPACE to place the repository - required: false - default: '.' -``` - -## Outputs -```yml -outputs: - create-status: - description: Branch creation status - value: ${{ steps.create-branch.outputs.create_status }} -``` diff --git a/.github/actions/remote-branch-action/action.yml b/.github/actions/remote-branch-action/action.yml deleted file mode 100644 index 215d2203407..00000000000 --- a/.github/actions/remote-branch-action/action.yml +++ /dev/null @@ -1,61 +0,0 @@ -# action.yml -name: Remote Branch Action -description: Create and manage a remote branch -branding: - icon: 'git-branch' - color: 'blue' -inputs: - branch: - description: Branch name - required: true - path: - description: Relative path under $GITHUB_WORKSPACE to place the repository - required: false - default: '.' -outputs: - create-status: - description: Branch creation status - value: ${{ steps.create-branch.outputs.create_status }} - -runs: - using: "composite" - steps: - - - name: Create branch - id: create-branch - working-directory: ${{ inputs.path }} - shell: bash - run: | - # Assign the arguments to variables - branch_name=${{ inputs.branch }} - - # Create a new branch locally - git checkout -b $branch_name - - # Check if the branch exists on the remote - check_status=$(git ls-remote --heads origin $branch_name | wc -l) - - # Check if the branch does not exist on the remote - if [ $check_status -eq 0 ]; then - # Push the new branch to the remote repository using the token - git push -u origin $branch_name - - # Store the status of the push command - status=$? - - # Check if the push was successful - if [ $status -eq 0 ]; then - # Print a success message - echo "Branch $branch_name created and pushed" - else - # Print an error message - echo "Branch creation failed with status $status" - status="Branch creation failed with status $status" - fi - else - # Print a message that the branch already exists on the remote - echo "Branch $branch_name already exists" - status="Branch $branch_name already exists" - fi - - echo "create_status=$status" >> $GITHUB_OUTPUT diff --git a/.github/actions/runleaks/.github/dependabot.yml b/.github/actions/runleaks/.github/dependabot.yml deleted file mode 100644 index 92f5139560b..00000000000 --- a/.github/actions/runleaks/.github/dependabot.yml +++ /dev/null @@ -1,16 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "github-actions" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" - - - package-ecosystem: "docker" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" diff --git a/.github/actions/runleaks/.github/runleaks/exclusions.txt b/.github/actions/runleaks/.github/runleaks/exclusions.txt deleted file mode 100644 index 74dddf21418..00000000000 --- a/.github/actions/runleaks/.github/runleaks/exclusions.txt +++ /dev/null @@ -1,26 +0,0 @@ -#################################################################### - -# Add regular expressions patterns to filter false positives. - -# GUID -("|')[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}("|') -# Azure blob endpoint -("|').*[0-9a-zA-Z]{2,256}[.][b|B][l|L][o|O][b|B][.][c|C][o|O][r|R][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]("|') -# Azure queue endpoint -("|').*[0-9a-zA-Z]{2,256}[.][q|Q][u|U][e|E][u|U][e|E][.][c|C][o|O][r|R][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]("|') -# Azure table endpoint -("|').*[0-9a-zA-Z]{2,256}[.][t|T][a|A][b|B][l|L][e|E][.][c|C][o|O][r|R][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]("|') -# Azure database endpoint -("|').*[0-9a-zA-Z]{2,256}[.][d|D][a|A][t|T][a|A][b|B][a|A][s|S][e|E][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]("|') -# Azure servicebus endpoint -("|').*[0-9a-zA-Z]{2,256}[.][s|S][e|E][r|R][v|V][i|I][c|C][e|E][b|B][u|U][s|S][.][w|W][i|I][n|N][d|D][o|O][w|W][s|S][.][n|N][e|E][t|T]("|') -# Azure timeseries endpoint -("|').*[0-9a-zA-Z]{2,256}[.][t|T][i|I][m|M][e|E][s|S][e|E][r|R][i|I][e|E][s|S][.][a|A][z|Z][u|U][r|R][e|E][.][c|C][o|O][m|M]("|') -# 32 length string -("|')[A-Z0-9a-z[:punct:]]{32}("|')$ -# 88 length string -("|')[A-Z0-9a-z[:punct:]]{88}("|')$ -# Common storage credential -.*-Amz-Credential=.* - -#################################################################### diff --git a/.github/actions/runleaks/.github/runleaks/patterns.txt b/.github/actions/runleaks/.github/runleaks/patterns.txt deleted file mode 100644 index 405f0bf7f08..00000000000 --- a/.github/actions/runleaks/.github/runleaks/patterns.txt +++ /dev/null @@ -1,20 +0,0 @@ -#################################################################### - -# Register a secret provider ---register-azure ---register-aws ---register-gcp - -#################################################################### - -# Add a prohibited pattern ---add [A-Z0-9]{20} ---add Account[k|K]ey ---add Shared[a|A]ccessSignature - -#################################################################### - -# Add a string that is scanned for literally (+ is escaped): ---add --literal foo+bar - -#################################################################### diff --git a/.github/actions/runleaks/.github/runleaks/patterns_force_failure.txt b/.github/actions/runleaks/.github/runleaks/patterns_force_failure.txt deleted file mode 100644 index fbd849a10fa..00000000000 --- a/.github/actions/runleaks/.github/runleaks/patterns_force_failure.txt +++ /dev/null @@ -1,21 +0,0 @@ -#################################################################### - -# Register a secret provider ---register-azure ---register-aws ---register-gcp - -#################################################################### - -# Add a prohibited pattern ---add [A-Z0-9]{20} ---add Account[k|K]ey ---add Shared[a|A]ccessSignature ---add token - -#################################################################### - -# Add a string that is scanned for literally (+ is escaped): ---add --literal foo+bar - -#################################################################### diff --git a/.github/actions/runleaks/.github/workflows/main.yml b/.github/actions/runleaks/.github/workflows/main.yml deleted file mode 100644 index 186fea02512..00000000000 --- a/.github/actions/runleaks/.github/workflows/main.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Scan Action Logs - -on: [push] - -jobs: - scan_run_logs_1: - runs-on: ubuntu-latest - name: Scan repo run logs 1 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Scan run logs - uses: ./ - id: scan - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - run-limit: 5 - fail-on-leak: false - - name: Get scan exceptions - if: steps.scan.outputs.count > 0 - run: | - echo "count=${{ steps.scan.outputs.count }}" - exceptions='${{ steps.scan.outputs.exceptions }}' - echo ${exceptions//"%0A"/} | jq '.' - - name: Confirm no exceptions - if: steps.scan.outputs.count == 0 - run: | - echo "No exceptions found!" - scan_run_logs_2: - runs-on: ubuntu-latest - name: Scan repo run logs 2 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Scan run logs - uses: ./ - id: scan - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - run-limit: 5 - patterns-path: ".github/runleaks/patterns_force_failure.txt" - exclusions-path: ".github/runleaks/exclusions.txt" - fail-on-leak: false - - name: Get scan exceptions - if: steps.scan.outputs.count > 0 - run: | - echo "count=${{ steps.scan.outputs.count }}" - exceptions='${{ steps.scan.outputs.exceptions }}' - echo ${exceptions//"%0A"/} | jq '.' diff --git a/.github/actions/runleaks/.github/workflows/scan_public.yml b/.github/actions/runleaks/.github/workflows/scan_public.yml deleted file mode 100644 index 9365f5e2557..00000000000 --- a/.github/actions/runleaks/.github/workflows/scan_public.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Scan Public Logs - -on: - workflow_dispatch: - inputs: - repos: - description: 'Repos to scan:' - required: true - default: '[\"prime-reportstream\/runleaks\",\"prime-reportstream\/azviz-action\"]' - type: string - schedule: - - cron: "0 3 * * *" - push: - -jobs: - pre_job: - name: "Set Build Environment" - runs-on: ubuntu-latest - outputs: - repos: ${{ env.REPOS }} - steps: - - name: Set workflow_dispatch matrix - if: github.event_name == 'workflow_dispatch' - run: echo "REPOS=${{ github.event.inputs.repos }}" >> $GITHUB_ENV - - - name: Fetch random repo name - if: github.event_name == 'schedule' || github.event_name == 'push' - uses: .github/actions/randomrepo - id: random - with: - github-token: ${{ secrets.MY_TOKEN }} - - - name: Set schedule matrix - if: github.event_name == 'schedule' || github.event_name == 'push' - run: | - format_string=$(echo "[\"${{ steps.random.outputs.repo }}\"]" | sed 's/\//\\\//g') - echo "REPOS='$format_string'" - echo "REPOS=$format_string" >> $GITHUB_ENV - - scan_public_logs: - runs-on: ubuntu-latest - name: Scan public logs - needs: - - pre_job - strategy: - matrix: - repo: ${{ fromJson(needs.pre_job.outputs.repos) }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Scan run logs - ${{ matrix.repo }} - uses: ./ - id: scan - with: - github-token: ${{ secrets.MY_TOKEN }} - repo: ${{ matrix.repo }} - run-limit: 100 - min-days-old: 1 - max-days-old: 3 - exclusions-path: ".github/runleaks/exclusions.txt" - fail-on-leak: true diff --git a/.github/actions/runleaks/Dockerfile b/.github/actions/runleaks/Dockerfile deleted file mode 100644 index 872e8c611a0..00000000000 --- a/.github/actions/runleaks/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM debian:stable-slim -RUN apt update -RUN apt install git gh make parallel jq -y - -RUN git clone https://github.com/JosiahSiegel/git-secrets.git -RUN make -C /git-secrets install -COPY lib/* / - -ENTRYPOINT ["bash", "/scan.sh"] diff --git a/.github/actions/runleaks/LICENSE b/.github/actions/runleaks/LICENSE deleted file mode 100644 index 251ac3d0313..00000000000 --- a/.github/actions/runleaks/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 Josiah Siegel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.github/actions/runleaks/README.md b/.github/actions/runleaks/README.md deleted file mode 100644 index 8df5d96f09a..00000000000 --- a/.github/actions/runleaks/README.md +++ /dev/null @@ -1,163 +0,0 @@ -# runleaks - -[](https://github.com/CDCgov/runleaks/actions/workflows/main.yml) - -Leverages [git-secrets](https://github.com/awslabs/git-secrets) to identify potential leaks in GitHub action run logs. - - * Common Azure and Google Cloud patterns are available, thanks to fork [msalemcode/git-secrets](https://github.com/msalemcode/git-secrets). - - -## Inputs -```yml - github-token: - description: 'Token used to login to GitHub' - required: true - repo: - description: 'Repo to scan run logs for exceptions' - required: false - default: ${{ github.repository }} - run-limit: - description: 'Limit on how many runs to scan' - required: false - default: '50' - min-days-old: - description: 'Min age of runs in days' - required: false - default: '0' - max-days-old: - description: 'Max age of runs in days' - required: false - default: '3' - patterns-path: - description: 'Patterns file path' - required: false - default: ".runleaks/patterns.txt" - exclusions-path: - description: 'Excluded patterns file path' - required: false - default: ".runleaks/exclusions.txt" - fail-on-leak: - description: 'Fail action if leak is found' - required: false - default: true -``` - -## Outputs -```yml - exceptions: - description: 'Json output of run logs with exceptions' - count: - description: 'Count of exceptions' -``` - -## Usage - * Note: [GitHub rate limits](#rate-limits) -```yml - - name: Checkout - uses: actions/checkout@v3 - - name: Scan run logs - uses: .github/actions/runleaks - id: scan - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - run-limit: 500 - fail-on-leak: false - - name: Get scan exceptions - if: steps.scan.outputs.count > 0 - run: echo "${{ steps.scan.outputs.exceptions }}" -``` -or -```yml - - name: Checkout - uses: actions/checkout@v3 - - name: Scan run logs - uses: .github/actions/runleaks - id: scan - with: - github-token: ${{ secrets.MY_TOKEN }} - patterns-path: ".github/patterns.txt" - exclusions-path: ".github/exclusions.txt" - fail-on-leak: false - - name: Get scan exceptions - if: steps.scan.outputs.count > 0 - run: echo "${{ steps.scan.outputs.exceptions }}" -``` -or -```yml - - name: Checkout - uses: actions/checkout@v3 - with: - repository: 'me/my-repo' - - name: Scan run logs - uses: .github/actions/runleaks - id: scan - with: - github-token: ${{ secrets.MY_TOKEN }} - repo: 'me/my-repo' - run-limit: 200 - min-days-old: 0 - max-days-old: 4 - fail-on-leak: true -``` - -## Local testing - * Registers default patterns -```sh -git clone https://github.com/CDCgov/runleaks.git -cd runleaks/ -docker build -t runleaks . -docker run scan "<PERSONAL_ACCESS_TOKEN>" "<REPO>" <RUN_LIMIT> <MIN_DAYS_OLD> <MAX_DAYS_OLD> -``` - -## Pattern file - * Default location: `.runleaks/patterns.txt` - -``` -#################################################################### - -# Register a secret provider -#--register-azure -#--register-gcp ---register-aws - -#################################################################### - -# Add a prohibited pattern ---add [A-Z0-9]{20} ---add Account[k|K]ey ---add Shared[a|A]ccessSignature - -#################################################################### - -# Add a string that is scanned for literally (+ is escaped): ---add --literal foo+bar - -#################################################################### -``` - -## Exclusion file - * Default location: `.runleaks/exclusions.txt` -``` -#################################################################### - -# Add regular expressions patterns to filter false positives. - -# Allow GUID -("|')[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}("|') - -#################################################################### -``` - -## Performance - - * Scan 50 runs = 1 min - - * Scan 500 runs = 8 mins - -* Scan 3000 runs = 50 mins - -## Rate limits - -Built-in secret `GITHUB_TOKEN` is [limited to 1,000 requests per hour per repository](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#requests-from-github-actions). - -To avoid repo-wide rate limiting, personal access tokens can be added to secrets, which are [limited to 5,000 requests per hour and per authenticated user](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#requests-from-personal-accounts). diff --git a/.github/actions/runleaks/action.yml b/.github/actions/runleaks/action.yml deleted file mode 100644 index d431caf1cf6..00000000000 --- a/.github/actions/runleaks/action.yml +++ /dev/null @@ -1,55 +0,0 @@ -# action.yml -name: 'runleaks' -description: 'Identify potential leaks in GitHub action logs' -branding: - icon: 'search' - color: 'red' -inputs: - github-token: - description: 'Token used to login to GitHub' - required: true - repo: - description: 'Repo to scan run logs for exceptions' - required: false - default: ${{ github.repository }} - run-limit: - description: 'Limit on how many runs to scan' - required: false - default: '100' - min-days-old: - description: 'Min age of runs in days' - required: false - default: '0' - max-days-old: - description: 'Max age of runs in days' - required: false - default: '3' - patterns-path: - description: 'Patterns file path' - required: false - default: ".github/runleaks/patterns.txt" - exclusions-path: - description: 'Excluded patterns file path' - required: false - default: ".github/runleaks/exclusions.txt" - fail-on-leak: - description: 'Fail action if leak is found' - required: false - default: true -outputs: - exceptions: - description: 'Json output of run logs with exceptions' - count: - description: 'Count of exceptions' -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.github-token }} - - ${{ inputs.repo }} - - ${{ inputs.run-limit }} - - ${{ inputs.min-days-old }} - - ${{ inputs.max-days-old }} - - ${{ inputs.patterns-path }} - - ${{ inputs.exclusions-path }} - - ${{ inputs.fail-on-leak }} diff --git a/.github/actions/runleaks/lib/scan.sh b/.github/actions/runleaks/lib/scan.sh deleted file mode 100644 index 4a4b9cadb61..00000000000 --- a/.github/actions/runleaks/lib/scan.sh +++ /dev/null @@ -1,126 +0,0 @@ -#!/bin/bash - -# Init -repo="$2" -run_count=$3 -min_days_old=$4 -max_days_old=$5 -fail_on_leak=$8 -max_proc_count=32 -log_file=exceptions_search.txt - -# Set defaults -if [[ -z $min_days_old ]]; then - min_days_old="0" - echo "min_days_old: $min_days_old" -fi - -if [[ -z $max_days_old ]]; then - max_days_old="3" - echo "max_days_old: $max_days_old" -fi - -if [[ -z $fail_on_leak ]]; then - fail_on_leak=true - echo "fail_on_leak: $fail_on_leak" -fi - -# If file path error, use default patterns -if cp "$6" /patterns.txt; then - # Register patterns - grep -v -E '(#.*$)|(^$)' /patterns.txt >/clean_patterns.txt - while read line; do - if [[ "$line" != "#*" ]]; then - git secrets $line --global - fi - done </clean_patterns.txt -else - git secrets --register-azure --global - git secrets --register-aws --global - git secrets --register-gcp --global -fi - -cp "$7" /.gitallowed - -# GitHub auth -echo "$1" >auth.txt -gh auth login --with-token <auth.txt -if [[ $? -ne 0 ]]; then - exit 1 -fi - -echo "Repo: $repo" -echo "============================================================================================" -echo "Scan patterns:" -echo "============================================================================================" -git secrets --list --global -echo "============================================================================================" -echo "Excluded patterns:" -echo "============================================================================================" -cat /.gitallowed -echo "============================================================================================" - -# Collect up to 400/day id runs -run_list_limit=$(($max_days_old * 400)) - -echo "Raw run list limited to: $run_list_limit" - -# Collect ids of workflow runs -declare -a run_ids=$(gh run list --repo "$repo" --json databaseId,status,updatedAt --jq '.[] | select(.updatedAt <= ((now - ('"$min_days_old"'*86400))|strftime("%Y-%m-%dT%H:%M:%S %Z"))) | select(.updatedAt > ((now - ('"$max_days_old"'*86400))|strftime("%Y-%m-%dT%H:%M:%S %Z"))) | select(.status =="completed").databaseId' --limit $run_list_limit) -run_ids_limited=$(printf "%s\n" ${run_ids[@]} | head -$run_count) - -echo "Runs to scan: $run_count" -echo $run_ids_limited - -touch "$log_file" - -run_for_each() { - local each=$@ - - # Collect run logs and remove null - log_out=$(gh run view $each --repo "$repo" --log 2>/dev/null | sed 's/\x0//g') - if [[ $? -ne 0 ]]; then - exit 1 - fi - - # Identify potential exceptions - scan_out=$(echo "$log_out" | git secrets --scan - 2>&1) - status=$? - - # If exception, add to array of details - if (($status != 0)); then - raw_log_full=$(echo "$scan_out" | grep '(standard input)') - exception_line=$(echo "$raw_log_full" | awk -F '\t' '{print $2$3}' | sed 's/[^a-zA-Z0-9]/_/g' | sed 's/.*/"&",/') - exception=$(gh run view $each --repo "$repo" --json name,createdAt,databaseId,url,updatedAt,headBranch 2>/dev/null) - if [[ $? -ne 0 ]]; then - exit 1 - fi - exception_with_detail=$(echo $exception | jq '. + {exception_detail: {'"$exception_line"'}}') - echo $exception_with_detail >>"$log_file" - fi -} - -# Make visible to subprocesses -export -f run_for_each -export log_file -export repo - -parallel -0 --jobs $max_proc_count run_for_each ::: $run_ids_limited - -json_out=$(jq -n '.exceptions |= [inputs]' "$log_file") -json_out_length=$(echo $json_out | jq -s '.[].exceptions' | jq length) -echo "$json_out" - -# Make output friendly -json_out="${json_out//'%'/'%25'}" -json_out="${json_out//$'\n'/'%0A'}" -json_out="${json_out//$'\r'/'%0D'}" -echo "exceptions=$json_out" >> $GITHUB_OUTPUT -echo "count=$json_out_length" >> $GITHUB_OUTPUT - -rm "$log_file" - -if [[ $fail_on_leak = true && json_out_length -gt 0 ]]; then - echo "Failing since leak!" - exit 1 -fi diff --git a/.github/actions/slack-boltjs-app/.dockerignore b/.github/actions/slack-boltjs-app/.dockerignore deleted file mode 100644 index 2eea525d885..00000000000 --- a/.github/actions/slack-boltjs-app/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -.env \ No newline at end of file diff --git a/.github/actions/slack-boltjs-app/.env.example b/.github/actions/slack-boltjs-app/.env.example deleted file mode 100644 index 6f435a07baf..00000000000 --- a/.github/actions/slack-boltjs-app/.env.example +++ /dev/null @@ -1,10 +0,0 @@ -# App-Level Token w/connections:write -SLACK_APP_TOKEN= -# Bot User OAuth Token under the OAuth & Permissions sidebar -SLACK_BOT_TOKEN= -# https://github.com/settings/tokens -GITHUB_TOKEN= -# Only pushes to this repo if specified -GITHUB_REPO= -# Only pushes are permitted to these branches -GITHUB_TARGET_BRANCHES= \ No newline at end of file diff --git a/.github/actions/slack-boltjs-app/.eslintignore b/.github/actions/slack-boltjs-app/.eslintignore deleted file mode 100644 index 3c3629e647f..00000000000 --- a/.github/actions/slack-boltjs-app/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/.github/actions/slack-boltjs-app/.eslintrc.cjs b/.github/actions/slack-boltjs-app/.eslintrc.cjs deleted file mode 100644 index 7a28658ef87..00000000000 --- a/.github/actions/slack-boltjs-app/.eslintrc.cjs +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - env: { - browser: false, - es2021: true, - }, - extends: ["eslint:recommended", "plugin:node/recommended"], - rules: { - "node/no-missing-import": [ - "error", - { - allowModules: ["@slack/bolt"], - }, - ], - }, -}; diff --git a/.github/actions/slack-boltjs-app/.github/dependabot.yml b/.github/actions/slack-boltjs-app/.github/dependabot.yml deleted file mode 100644 index 82f4a9f460e..00000000000 --- a/.github/actions/slack-boltjs-app/.github/dependabot.yml +++ /dev/null @@ -1,17 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "npm" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" - - package-ecosystem: "github-actions" - # Workflow files stored in the - # default location of `.github/workflows` - directory: "/" - schedule: - interval: "weekly" diff --git a/.github/actions/slack-boltjs-app/.github/workflows/codacy.yml b/.github/actions/slack-boltjs-app/.github/workflows/codacy.yml deleted file mode 100644 index 43f560f1615..00000000000 --- a/.github/actions/slack-boltjs-app/.github/workflows/codacy.yml +++ /dev/null @@ -1,61 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# This workflow checks out code, performs a Codacy security scan -# and integrates the results with the -# GitHub Advanced Security code scanning feature. For more information on -# the Codacy security scan action usage and parameters, see -# https://github.com/codacy/codacy-analysis-cli-action. -# For more information on Codacy Analysis CLI in general, see -# https://github.com/codacy/codacy-analysis-cli. - -name: Codacy Security Scan - -on: - push: - branches: [ "main" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "main" ] - schedule: - - cron: '24 4 * * 1' - -permissions: - contents: read - -jobs: - codacy-security-scan: - permissions: - contents: read # for actions/checkout to fetch code - security-events: write # for github/codeql-action/upload-sarif to upload SARIF results - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status - name: Codacy Security Scan - runs-on: ubuntu-latest - steps: - # Checkout the repository to the GitHub Actions runner - - name: Checkout code - uses: actions/checkout@v4 - - # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis - - name: Run Codacy Analysis CLI - uses: codacy/codacy-analysis-cli-action@3ff8e64eb4b714c4bee91b7b4eea31c6fc2c4f93 - with: - # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository - # You can also omit the token and run the tools that support default configurations - project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} - verbose: true - output: results.sarif - format: sarif - # Adjust severity of non-security issues - gh-code-scanning-compat: true - # Force 0 exit code to allow SARIF file generation - # This will handover control about PR rejection to the GitHub side - max-allowed-issues: 2147483647 - - # Upload the SARIF file generated in the previous step - - name: Upload SARIF results file - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: results.sarif diff --git a/.github/actions/slack-boltjs-app/.github/workflows/codeql.yml b/.github/actions/slack-boltjs-app/.github/workflows/codeql.yml deleted file mode 100644 index 0626d41b044..00000000000 --- a/.github/actions/slack-boltjs-app/.github/workflows/codeql.yml +++ /dev/null @@ -1,74 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ "main" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "main" ] - -jobs: - analyze: - name: Analyze - runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'javascript' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Use only 'java' to analyze code written in Java, Kotlin or both - # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:${{matrix.language}}" diff --git a/.github/actions/slack-boltjs-app/.github/workflows/confirm_push.yml b/.github/actions/slack-boltjs-app/.github/workflows/confirm_push.yml deleted file mode 100644 index 6e15bf969ce..00000000000 --- a/.github/actions/slack-boltjs-app/.github/workflows/confirm_push.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Confirm Push - -on: - push: - branches: - - dummy - -permissions: read-all - -jobs: - confirm_push: - runs-on: ubuntu-latest - name: Validate - Push - steps: - - run: echo "Success!" diff --git a/.github/actions/slack-boltjs-app/.github/workflows/confirm_run.yml b/.github/actions/slack-boltjs-app/.github/workflows/confirm_run.yml deleted file mode 100644 index c8f5046da1f..00000000000 --- a/.github/actions/slack-boltjs-app/.github/workflows/confirm_run.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Confirm Run - -on: - workflow_dispatch: - inputs: - choice_name: - options: - - choice1 - - choice2 - - choice3 - type: choice - -jobs: - confirm_run: - name: "Confirm run: ${{ github.event.inputs.choice_name }}" - runs-on: ubuntu-latest - steps: - - name: Echo choice - run: echo "${{ github.event.inputs.choice_name }}"; \ No newline at end of file diff --git a/.github/actions/slack-boltjs-app/.github/workflows/dependency-review.yml b/.github/actions/slack-boltjs-app/.github/workflows/dependency-review.yml deleted file mode 100644 index 0d4a01360d7..00000000000 --- a/.github/actions/slack-boltjs-app/.github/workflows/dependency-review.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Dependency Review Action -# -# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. -# -# Source repository: https://github.com/actions/dependency-review-action -# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement -name: 'Dependency Review' -on: [pull_request] - -permissions: - contents: read - -jobs: - dependency-review: - runs-on: ubuntu-latest - steps: - - name: 'Checkout Repository' - uses: actions/checkout@v4 - - name: 'Dependency Review' - uses: actions/dependency-review-action@v4 diff --git a/.github/actions/slack-boltjs-app/.github/workflows/njsscan.yml b/.github/actions/slack-boltjs-app/.github/workflows/njsscan.yml deleted file mode 100644 index f68e77f8528..00000000000 --- a/.github/actions/slack-boltjs-app/.github/workflows/njsscan.yml +++ /dev/null @@ -1,40 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# This workflow integrates njsscan with GitHub's Code Scanning feature -# nodejsscan is a static security code scanner that finds insecure code patterns in your Node.js applications - -name: njsscan sarif - -on: - push: - branches: [ "main" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "main" ] - -permissions: - contents: read - -jobs: - njsscan: - permissions: - contents: read # for actions/checkout to fetch code - security-events: write # for github/codeql-action/upload-sarif to upload SARIF results - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status - runs-on: ubuntu-latest - name: njsscan code scanning - steps: - - name: Checkout the code - uses: actions/checkout@v4 - - name: nodejsscan scan - id: njsscan - uses: ajinabraham/njsscan-action@d58d8b2f26322cd35a9efb8003baac517f226d81 - with: - args: '. --sarif --output results.sarif || true' - - name: Upload njsscan report - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: results.sarif diff --git a/.github/actions/slack-boltjs-app/.github/workflows/validate.yml b/.github/actions/slack-boltjs-app/.github/workflows/validate.yml deleted file mode 100644 index 610c317c2c3..00000000000 --- a/.github/actions/slack-boltjs-app/.github/workflows/validate.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Validate - -on: - pull_request: - branches: - - main - -permissions: read-all - -jobs: - run_docker: - runs-on: ubuntu-latest - name: Run Docker - outputs: - result: ${{ steps.validate_bot.outputs.result }} - steps: - - uses: actions/checkout@v4 - - name: Build and run Docker - run: | - echo "${{ secrets.ENV }}" > .env - cp .help.example .help - make - shell: bash - - name: Validate bot - id: validate_bot - run: | - # Define a function to send a message to Slack and store the result - out=true; - - send_message() { - text=$1 - ok=$(curl -d "text=<@${{ vars.VALIDATE_BOTID }}> $text" -d "channel=${{ secrets.VALIDATE_CHANNEL }}" \ - -H "Authorization: Bearer ${{ secrets.VALIDATE_TOKEN }}" \ - -X POST https://slack.com/api/chat.postMessage | jq .ok) - - if [ "$ok" = false ] ; then - out=false; - fi - } - - # Loop through the messages and call the function with a counter - messages=(":wave:" "gh-targets" "help" "gh-run confirm_run.yml --inputs choice_name:choice2" \ - "gh-run confirm_run.yml" "gh-lock add dummy" "gh-deploy main to dummy" "gh-lock show dummy" \ - "gh-lock remove dummy" "gh-deploy main to dummy") - counter=1 - for message in "${messages[@]}"; do - send_message "$message" "$counter" - sleep 5 - ((counter++)) - done - - echo "result=$out" >> $GITHUB_OUTPUT - shell: bash - - no_fail_check: - needs: - - run_docker - runs-on: ubuntu-latest - name: Validate - No failure - steps: - - if: needs.run_docker.outputs.result == 'true' - run: echo "Success!" - - if: needs.run_docker.outputs.result != 'true' - run: exit 1 diff --git a/.github/actions/slack-boltjs-app/.gitignore b/.github/actions/slack-boltjs-app/.gitignore deleted file mode 100644 index 1e268ce3f2d..00000000000 --- a/.github/actions/slack-boltjs-app/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.env -.help -node_modules diff --git a/.github/actions/slack-boltjs-app/.glitch-assets b/.github/actions/slack-boltjs-app/.glitch-assets deleted file mode 100644 index 954626f61ff..00000000000 --- a/.github/actions/slack-boltjs-app/.glitch-assets +++ /dev/null @@ -1,6 +0,0 @@ -{"name":"drag-in-files.svg","date":"2016-10-22T16:17:49.954Z","url":"https://cdn.hyperdev.com/drag-in-files.svg","type":"image/svg","size":7646,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/drag-in-files.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(102, 153, 205)","uuid":"adSBq97hhhpFNUna"} -{"name":"click-me.svg","date":"2016-10-23T16:17:49.954Z","url":"https://cdn.hyperdev.com/click-me.svg","type":"image/svg","size":7116,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/click-me.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(243, 185, 186)","uuid":"adSBq97hhhpFNUnb"} -{"name":"paste-me.svg","date":"2016-10-24T16:17:49.954Z","url":"https://cdn.hyperdev.com/paste-me.svg","type":"image/svg","size":7242,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/paste-me.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(42, 179, 185)","uuid":"adSBq97hhhpFNUnc"} -{"uuid":"adSBq97hhhpFNUna","deleted":true} -{"uuid":"adSBq97hhhpFNUnb","deleted":true} -{"uuid":"adSBq97hhhpFNUnc","deleted":true} diff --git a/.github/actions/slack-boltjs-app/.help.example b/.github/actions/slack-boltjs-app/.help.example deleted file mode 100644 index b24e6e72302..00000000000 --- a/.github/actions/slack-boltjs-app/.help.example +++ /dev/null @@ -1,32 +0,0 @@ -USAGE - [<@bot>] gh-deploy [<branch>] to [<branch>] [OPTIONAL: for <owner/repo>] - -EXAMPLES - @MyBot gh-deploy master to feature3 - @MyBot gh-deploy feature1 to feature3 for my/awesome_repo - -========================================================================== - -USAGE - [<@bot>] gh-lock [add|remove|show] [<branch>] - -EXAMPLES - @MyBot gh-lock add feature3 - @MyBot gh-lock remove feature1 - -========================================================================== - -USAGE - [<@bot>] gh-run [<workflow file>] [OPTIONAL: <owner/repo> <branch>] [OPTIONAL: --inputs <a:b,c:d>] - -EXAMPLES - @MyBot gh-run log_management.yml --inputs name:alpha,desc:uat env - @MyBot gh-run log_management.yml my/awesome_repo main --inputs name:alpha,desc:uat env - -========================================================================== - -USAGE - [<@bot>] gh-targets - -EXAMPLES - @bot gh-targets diff --git a/.github/actions/slack-boltjs-app/Dockerfile.example b/.github/actions/slack-boltjs-app/Dockerfile.example deleted file mode 100644 index 0bc2f1257f9..00000000000 --- a/.github/actions/slack-boltjs-app/Dockerfile.example +++ /dev/null @@ -1,11 +0,0 @@ -FROM node:20-bookworm-slim - -WORKDIR /usr/app -COPY --chown=node:node ./ ./ -RUN mkdir -p ./src/.locks && chown node ./src/.locks -VOLUME /usr/app/src/.locks - -RUN npm install -USER node - -CMD ["npm", "start"] diff --git a/.github/actions/slack-boltjs-app/Makefile b/.github/actions/slack-boltjs-app/Makefile deleted file mode 100644 index 032201f6d6d..00000000000 --- a/.github/actions/slack-boltjs-app/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -default: start - -start: build - docker stop slack-boltjs-app || true - docker rm slack-boltjs-app || true - docker run --name=slack-boltjs-app --env-file .env \ - --volume gh_locks:/usr/app/src/.locks -d slack_boltjs_app - -build: - docker build -t slack_boltjs_app -f Dockerfile.example . \ No newline at end of file diff --git a/.github/actions/slack-boltjs-app/README.md b/.github/actions/slack-boltjs-app/README.md deleted file mode 100644 index 8294b09ca78..00000000000 --- a/.github/actions/slack-boltjs-app/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# Slack-Boltjs-App - -[](https://github.com/CDCgov/slack-bolt/actions/workflows/codeql.yml) -[](https://github.com/CDCgov/slack-bolt/actions/workflows/njsscan.yml) -[](https://app.codacy.com/gh/CDCgov/slack-boltjs-app/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) - - - -<a href="https://glitch.com/edit/#!/remix/slack-boltjs-app"><img alt="Remix on Glitch" src="https://cdn.gomix.com/f3620a78-0ad3-4f81-a271-c8a4faa20f86%2Fremix-button.svg"></a> - -## A secure and simple Boltjs app for Slack ChatOps - -> App includes basic GitHub push functionality to get you started - -### Quickstart -* `cp .env.example .env` - * Update `.env` ⚠️(required)⚠️ -* `cp .help.example .help` - * Update `.help` (optional) -* `npm install` -* `npm start` - -### Helpful links - -* [Bolt getting started guide](https://api.slack.com/start/building/bolt) -* [Bolt documentation](https://slack.dev/bolt) -* [Slack app home](https://api.slack.com/apps) - -### Example Slack app manifest - -```yml -display_information: - name: Bolt DevBot -features: - app_home: - home_tab_enabled: true - messages_tab_enabled: false - messages_tab_read_only_enabled: false - bot_user: - display_name: DevBot - always_online: true -oauth_config: - scopes: - bot: - - app_mentions:read - - calls:write - - channels:read - - chat:write - - commands - - channels:history - - reactions:read -settings: - event_subscriptions: - bot_events: - - app_home_opened - - app_mention - - message.channels - - reaction_added - interactivity: - is_enabled: true - org_deploy_enabled: false - socket_mode_enabled: true - token_rotation_enabled: false -``` - -Tips: - -* For [external workspace users][1], add an `app.message` per `app.command`. -* Check vulnerabilities: `npm audit` -* Fix Glitch out of sync with repo: - * `git pull` - * `refresh` -* Hard refresh Glitch from repo: - * `git fetch --all` - * `git reset --hard origin/main` - * `refresh` - ---- - -[Glitch](https://glitch.com/~slack-boltjs-app) -[GitHub](https://github.com/CDCgov/slack-boltjs-app) - -[1]: https://slack.com/help/articles/115004151203-Slack-Connect-guide--work-with-external-organizations diff --git a/.github/actions/slack-boltjs-app/npm-shrinkwrap.json b/.github/actions/slack-boltjs-app/npm-shrinkwrap.json deleted file mode 100644 index aa05bfa7ddf..00000000000 --- a/.github/actions/slack-boltjs-app/npm-shrinkwrap.json +++ /dev/null @@ -1,4263 +0,0 @@ -{ - "name": "slack-bolt", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "slack-bolt", - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@slack/bolt": "^3.18.0", - "dotenv": "^16", - "node-localstorage": "^3.0.5" - }, - "devDependencies": { - "eslint": "9.3.0", - "eslint-config-eslint": "^10.0.0" - }, - "engines": { - "node": "^20", - "npm": "^10" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.2", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", - "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", - "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.5", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@es-joy/jsdoccomment": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.42.0.tgz", - "integrity": "sha512-R1w57YlVA6+YE01wch3GPYn6bCsrOV3YW/5oGGE2tmX6JcL9Nr+b5IikrjMPF+v9CV3ay+obImEdsDhovhJrzw==", - "dev": true, - "dependencies": { - "comment-parser": "1.4.1", - "esquery": "^1.5.0", - "jsdoc-type-pratt-parser": "~4.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@eslint-community/eslint-plugin-eslint-comments": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.3.0.tgz", - "integrity": "sha512-6e93KtgsndNkvwCCa07LOQJSwzzLLxwrFll3+huyFoiiQXWG0KBcmo0Q1bVgYQQDLfWOOZl2VPBsXqZL6vHIBQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^4.0.0", - "ignore": "^5.2.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.3.0.tgz", - "integrity": "sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "dev": true - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", - "dev": true, - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@slack/bolt": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/@slack/bolt/-/bolt-3.18.0.tgz", - "integrity": "sha512-A7bDi5kY50fS6/nsmURkQdO3iMxD8aX/rA+m1UXEM2ue2z4KijeQtx2sOZ4YkJQ/h7BsgTQM0CYh3qqmo+m5sQ==", - "dependencies": { - "@slack/logger": "^4.0.0", - "@slack/oauth": "^2.6.2", - "@slack/socket-mode": "^1.3.3", - "@slack/types": "^2.11.0", - "@slack/web-api": "^6.11.2", - "@types/express": "^4.16.1", - "@types/promise.allsettled": "^1.0.3", - "@types/tsscmp": "^1.0.0", - "axios": "^1.6.0", - "express": "^4.16.4", - "path-to-regexp": "^6.2.1", - "please-upgrade-node": "^3.2.0", - "promise.allsettled": "^1.0.2", - "raw-body": "^2.3.3", - "tsscmp": "^1.0.6" - }, - "engines": { - "node": ">=12.13.0", - "npm": ">=6.12.0" - } - }, - "node_modules/@slack/logger": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-4.0.0.tgz", - "integrity": "sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA==", - "dependencies": { - "@types/node": ">=18.0.0" - }, - "engines": { - "node": ">= 18", - "npm": ">= 8.6.0" - } - }, - "node_modules/@slack/oauth": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@slack/oauth/-/oauth-2.6.2.tgz", - "integrity": "sha512-2R3MyB/R63hTRXzk5J6wcui59TBxXzhk+Uh2/Xu3Wp3O4pXg/BNucQhP/DQbL/ScVhLvFtMXirLrKi0Yo5gIVw==", - "dependencies": { - "@slack/logger": "^3.0.0", - "@slack/web-api": "^6.11.2", - "@types/jsonwebtoken": "^8.3.7", - "@types/node": ">=12", - "jsonwebtoken": "^9.0.0", - "lodash.isstring": "^4.0.1" - }, - "engines": { - "node": ">=12.13.0", - "npm": ">=6.12.0" - } - }, - "node_modules/@slack/oauth/node_modules/@slack/logger": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-3.0.0.tgz", - "integrity": "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==", - "dependencies": { - "@types/node": ">=12.0.0" - }, - "engines": { - "node": ">= 12.13.0", - "npm": ">= 6.12.0" - } - }, - "node_modules/@slack/socket-mode": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@slack/socket-mode/-/socket-mode-1.3.3.tgz", - "integrity": "sha512-vN3zG4woRtf2Ut6rZgRW6G/Oe56uLMlnz39I08Q7DOvVfB+1MmDbNv0PNOiFgujdKXJR+bXF41/F/VvryXcqlw==", - "dependencies": { - "@slack/logger": "^3.0.0", - "@slack/web-api": "^6.11.2", - "@types/node": ">=12.0.0", - "@types/p-queue": "^2.3.2", - "@types/ws": "^7.4.7", - "eventemitter3": "^3.1.0", - "finity": "^0.5.4", - "p-cancelable": "^1.1.0", - "p-queue": "^2.4.2", - "ws": "^7.5.3" - }, - "engines": { - "node": ">=12.13.0", - "npm": ">=6.12.0" - } - }, - "node_modules/@slack/socket-mode/node_modules/@slack/logger": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-3.0.0.tgz", - "integrity": "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==", - "dependencies": { - "@types/node": ">=12.0.0" - }, - "engines": { - "node": ">= 12.13.0", - "npm": ">= 6.12.0" - } - }, - "node_modules/@slack/types": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@slack/types/-/types-2.11.0.tgz", - "integrity": "sha512-UlIrDWvuLaDly3QZhCPnwUSI/KYmV1N9LyhuH6EDKCRS1HWZhyTG3Ja46T3D0rYfqdltKYFXbJSSRPwZpwO0cQ==", - "engines": { - "node": ">= 12.13.0", - "npm": ">= 6.12.0" - } - }, - "node_modules/@slack/web-api": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-6.12.0.tgz", - "integrity": "sha512-RPw6F8rWfGveGkZEJ4+4jUin5iazxRK2q3FpQDz/FvdgzC3nZmPyLx8WRzc6nh0w3MBjEbphNnp2VZksfhpBIQ==", - "dependencies": { - "@slack/logger": "^3.0.0", - "@slack/types": "^2.11.0", - "@types/is-stream": "^1.1.0", - "@types/node": ">=12.0.0", - "axios": "^1.6.5", - "eventemitter3": "^3.1.0", - "form-data": "^2.5.0", - "is-electron": "2.2.2", - "is-stream": "^1.1.0", - "p-queue": "^6.6.1", - "p-retry": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0", - "npm": ">= 6.12.0" - } - }, - "node_modules/@slack/web-api/node_modules/@slack/logger": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-3.0.0.tgz", - "integrity": "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==", - "dependencies": { - "@types/node": ">=12.0.0" - }, - "engines": { - "node": ">= 12.13.0", - "npm": ">= 6.12.0" - } - }, - "node_modules/@slack/web-api/node_modules/p-queue": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", - "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", - "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@slack/web-api/node_modules/p-queue/node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.43", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", - "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" - }, - "node_modules/@types/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@types/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/jsonwebtoken": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", - "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" - }, - "node_modules/@types/node": { - "version": "20.11.23", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.23.tgz", - "integrity": "sha512-ZUarKKfQuRILSNYt32FuPL20HS7XwNT7/uRwSV8tiHWfyyVwDLYZNF6DZKc2bove++pgfsXn9sUwII/OsQ82cQ==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true - }, - "node_modules/@types/p-queue": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@types/p-queue/-/p-queue-2.3.2.tgz", - "integrity": "sha512-eKAv5Ql6k78dh3ULCsSBxX6bFNuGjTmof5Q/T6PiECDq0Yf8IIn46jCyp3RJvCi8owaEmm3DZH1PEImjBMd/vQ==" - }, - "node_modules/@types/promise.allsettled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/promise.allsettled/-/promise.allsettled-1.0.6.tgz", - "integrity": "sha512-wA0UT0HeT2fGHzIFV9kWpYz5mdoyLxKrTgMdZQM++5h6pYAFH73HXcQhefg24nD1yivUFEn5KU+EF4b+CXJ4Wg==" - }, - "node_modules/@types/qs": { - "version": "6.9.12", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.12.tgz", - "integrity": "sha512-bZcOkJ6uWrL0Qb2NAWKa7TBU+mJHPzhx9jjLL1KHF+XpzEcR7EXHvjbHlGtR/IsP1vyPrehuS6XqkmaePy//mg==" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/tsscmp": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/tsscmp/-/tsscmp-1.0.2.tgz", - "integrity": "sha512-cy7BRSU8GYYgxjcx0Py+8lo5MthuDhlyu076KUcYzVNXL23luYgRHkMG2fIFEc6neckeh/ntP82mw+U4QjZq+g==" - }, - "node_modules/@types/ws": { - "version": "7.4.7", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", - "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/are-docs-informative": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", - "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/array.prototype.map": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.6.tgz", - "integrity": "sha512-nK1psgF2cXqP3wSyCSq0Hc7zwNq3sfljQqaG27r/7a7ooNUnn5nGq6yYWyks9jMO5EoFQ0ax80hSg6oXSRNXaw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", - "dependencies": { - "follow-redirects": "^1.15.4", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axios/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001614", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz", - "integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ci-info": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", - "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/clean-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", - "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/clean-regexp/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comment-parser": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", - "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", - "dev": true, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/core-js-compat": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.0.tgz", - "integrity": "sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==", - "dev": true, - "dependencies": { - "browserslist": "^4.23.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.751", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.751.tgz", - "integrity": "sha512-2DEPi++qa89SMGRhufWTiLmzqyuGmNF3SK4+PQetW1JKiZdEpF4XQonJXJCzyuYSA6mauiMhbyVhqYAP45Hvfw==", - "dev": true - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", - "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.22.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz", - "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.1", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.0", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.3.0.tgz", - "integrity": "sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.3.0", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.0", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.1", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.0.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-compat-utils": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.0.tgz", - "integrity": "sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==", - "dev": true, - "dependencies": { - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, - "node_modules/eslint-config-eslint": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-eslint/-/eslint-config-eslint-10.0.0.tgz", - "integrity": "sha512-ejGeXkQLyAEqUBr6UE246sZBRqscQVuJOTuYLYIt+GaCrfRodLsoJ6/oXRHZumcLDbZYnZoXRpgYy8+NJ/UOOw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-plugin-eslint-comments": "^4.3.0", - "@eslint/js": "^9.0.0", - "eslint-plugin-jsdoc": "^48.2.3", - "eslint-plugin-n": "^17.2.0", - "eslint-plugin-unicorn": "^52.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-plugin-es-x": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.6.0.tgz", - "integrity": "sha512-I0AmeNgevgaTR7y2lrVCJmGYF0rjoznpDvqV/kIkZSZbZ8Rw3eu4cGlvBBULScfkSOCzqKbff5LR4CNrV7mZHA==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.1.2", - "@eslint-community/regexpp": "^4.6.0", - "eslint-compat-utils": "^0.5.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "eslint": ">=8" - } - }, - "node_modules/eslint-plugin-jsdoc": { - "version": "48.2.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.3.tgz", - "integrity": "sha512-r9DMAmFs66VNvNqRLLjHejdnJtILrt3xGi+Qx0op0oRfFGVpOR1Hb3BC++MacseHx93d8SKYPhyrC9BS7Os2QA==", - "dev": true, - "dependencies": { - "@es-joy/jsdoccomment": "~0.42.0", - "are-docs-informative": "^0.0.2", - "comment-parser": "1.4.1", - "debug": "^4.3.4", - "escape-string-regexp": "^4.0.0", - "esquery": "^1.5.0", - "is-builtin-module": "^3.2.1", - "semver": "^7.6.0", - "spdx-expression-parse": "^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-n": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.3.1.tgz", - "integrity": "sha512-25+HTtKe1F8U/M4ERmdzbz/xkm/gaY0OYC8Fcv1z/WvpLJ8Xfh9LzJ13JV5uj4QyCUD8kOPJrNjn/3y+tc57Vw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "enhanced-resolve": "^5.15.0", - "eslint-plugin-es-x": "^7.5.0", - "get-tsconfig": "^4.7.0", - "globals": "^15.0.0", - "ignore": "^5.2.4", - "minimatch": "^9.0.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": ">=8.23.0" - } - }, - "node_modules/eslint-plugin-n/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/eslint-plugin-n/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/eslint-plugin-unicorn": { - "version": "52.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz", - "integrity": "sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "@eslint-community/eslint-utils": "^4.4.0", - "@eslint/eslintrc": "^2.1.4", - "ci-info": "^4.0.0", - "clean-regexp": "^1.0.0", - "core-js-compat": "^3.34.0", - "esquery": "^1.5.0", - "indent-string": "^4.0.0", - "is-builtin-module": "^3.2.1", - "jsesc": "^3.0.2", - "pluralize": "^8.0.0", - "read-pkg-up": "^7.0.1", - "regexp-tree": "^0.1.27", - "regjsparser": "^0.10.0", - "semver": "^7.5.4", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" - }, - "peerDependencies": { - "eslint": ">=8.56.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-scope": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", - "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", - "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", - "dev": true, - "dependencies": { - "acorn": "^8.11.3", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" - }, - "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/finity": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/finity/-/finity-0.5.4.tgz", - "integrity": "sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA==" - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-tsconfig": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", - "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", - "dev": true, - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.1.0.tgz", - "integrity": "sha512-926gJqg+4mkxwYKiFvoomM4J0kWESfk3qfTvRL2/oc/tK/eTDBbrfcKnSa2KtfdxB5onoL7D3A3qIHQFpd4+UA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", - "dev": true, - "dependencies": { - "builtin-modules": "^3.3.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-electron": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", - "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==" - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/iterate-iterator": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.2.tgz", - "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/iterate-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", - "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", - "dependencies": { - "es-get-iterator": "^1.0.2", - "iterate-iterator": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdoc-type-pratt-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", - "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", - "dev": true, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-localstorage": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-3.0.5.tgz", - "integrity": "sha512-GCwtK33iwVXboZWYcqQHu3aRvXEBwmPkAMRBLeaX86ufhqslyUkLGsi4aW3INEfdQYpUB5M9qtYf3eHvAk2VBg==", - "dependencies": { - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-2.4.2.tgz", - "integrity": "sha512-n8/y+yDJwBjoLQe1GSJbbaYQLTI7QHNZI2+rpmCDbe++WLf9HC3gf6iqj5yfPAV71W4UF3ql5W1+UBPXoXTxng==", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-to-regexp": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dependencies": { - "semver-compare": "^1.0.0" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/promise.allsettled": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.7.tgz", - "integrity": "sha512-hezvKvQQmsFkOdrZfYxUxkyxl8mgFQeT259Ajj9PXdbg9VzBCWrItOev72JyWxkCD5VSSqAeHmlN3tWx4DlmsA==", - "dependencies": { - "array.prototype.map": "^1.0.5", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "iterate-value": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "dev": true, - "bin": { - "regexp-tree": "bin/regexp-tree" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dependencies": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regjsparser": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz", - "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==", - "dev": true, - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==" - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", - "dependencies": { - "define-data-property": "^1.1.2", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", - "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-correct/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", - "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", - "dev": true - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tsscmp": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", - "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", - "engines": { - "node": ">=0.6.x" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", - "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", - "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", - "dependencies": { - "available-typed-arrays": "^1.0.6", - "call-bind": "^1.0.5", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/.github/actions/slack-boltjs-app/package.json b/.github/actions/slack-boltjs-app/package.json deleted file mode 100644 index 877b244e3f0..00000000000 --- a/.github/actions/slack-boltjs-app/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "slack-bolt", - "version": "1.0.0", - "description": "A secure and simple Bolt app for Slack ChatOps", - "main": "src/index.js", - "type": "module", - "scripts": { - "start": "node -r dotenv/config src/index.js" - }, - "dependencies": { - "dotenv": "^16", - "@slack/bolt": "^3.18.0", - "node-localstorage": "^3.0.5" - }, - "engines": { - "node": "^20", - "npm": "^10" - }, - "repository": { - "url": "https://github.com/JosiahSiegel/slack-boltjs-app" - }, - "license": "MIT", - "devDependencies": { - "eslint": "9.3.0", - "eslint-config-eslint": "^10.0.0" - } -} diff --git a/.github/actions/slack-boltjs-app/src/index.js b/.github/actions/slack-boltjs-app/src/index.js deleted file mode 100644 index a40aabce36a..00000000000 --- a/.github/actions/slack-boltjs-app/src/index.js +++ /dev/null @@ -1,47 +0,0 @@ -import boltjs from "@slack/bolt"; -const { App, directMention } = boltjs; -import fs from "fs"; -import ghRouter from "./utils/github/router.js"; -import appHome from "./views/app_home.js"; - -const app = new App({ - token: process.env.SLACK_BOT_TOKEN, - appToken: process.env.SLACK_APP_TOKEN, - socketMode: true, -}); - -// Use a single message listener with a switch statement to handle different commands -app.message(directMention(), async ({ message, say }) => { - const command = message.text.split(" ")[1].split("-")[0]; - switch (command) { - case ":wave:": - await say(`Hello, <@${message.user}>`); - break; - case "gh": - await ghRouter({ message, say }); - break; - case "help": - const filename = ".help"; - const data = fs.readFileSync(filename, "utf8"); - say(data); - break; - default: - // Handle unknown commands - say( - `Sorry, I don't recognize that command. Try typing \`@bot help\` for more information.` - ); - } -}); - -// Listen for users opening App Home -app.event("app_home_opened", async ({ event, client }) => { - appHome({ event, client }); -}); - -(async () => { - const port = process.env.PORT || 3000; - - // Start your app - await app.start(port); - console.log(`⚡️ Slack Bolt app is running on port ${port}!`); -})(); diff --git a/.github/actions/slack-boltjs-app/src/utils/github/default_branch.js b/.github/actions/slack-boltjs-app/src/utils/github/default_branch.js deleted file mode 100644 index 7ba49a0a476..00000000000 --- a/.github/actions/slack-boltjs-app/src/utils/github/default_branch.js +++ /dev/null @@ -1,11 +0,0 @@ -import request from "./request.js"; - -const default_branch = async function ({ api, app, token, say, msg }) { - const path = `/repos/${app}`; - const method = "GET"; - const data = null; - const out = await request({ api, path, method, token, data, say, msg }); - return JSON.parse(out)?.default_branch; -}; - -export default default_branch; diff --git a/.github/actions/slack-boltjs-app/src/utils/github/list_targets.js b/.github/actions/slack-boltjs-app/src/utils/github/list_targets.js deleted file mode 100644 index 1cf459027c9..00000000000 --- a/.github/actions/slack-boltjs-app/src/utils/github/list_targets.js +++ /dev/null @@ -1,13 +0,0 @@ -const list_targets = async ({ say }) => { - const deployTargets = process.env.GITHUB_TARGET_BRANCHES.split(","); - if (deployTargets.length === 0) { - return say( - "No targets branches defined. Set GITHUB_TARGET_BRANCHES first." - ); - } else { - var targets = "- " + deployTargets.join("\n- "); - return say(targets); - } -}; - -export default list_targets; diff --git a/.github/actions/slack-boltjs-app/src/utils/github/lock_target.js b/.github/actions/slack-boltjs-app/src/utils/github/lock_target.js deleted file mode 100644 index c7332647dc6..00000000000 --- a/.github/actions/slack-boltjs-app/src/utils/github/lock_target.js +++ /dev/null @@ -1,61 +0,0 @@ -const lock_target = async ({ localStorage, args, say, user }) => { - const action = args[2]; - const branch = args[3]; - - const deployTargets = process.env.GITHUB_TARGET_BRANCHES.split(","); - if (!deployTargets.includes(branch)) { - say( - `\"${branch}\" is not in available target branches. Use: <@bot name> gh-targets` - ); - return false; - } - - switch (action) { - case "add": - // Check if the branch already has a lock - if (localStorage.getItem(branch)) { - // If yes, say that the branch is already locked - await say( - `Branch ${branch} is already locked by <@${localStorage.getItem( - branch - )}>` - ); - } else { - // If no, add the lock and say that the branch is locked - localStorage.setItem(branch, user); - await say(`Locked branch ${branch}`); - } - break; - case "remove": - // Check if the branch has a lock - if (localStorage.getItem(branch)) { - // If yes, remove the lock and say that the lock is removed - await say( - `Removed <@${localStorage.getItem( - branch - )}>'s lock on branch ${branch}` - ); - localStorage.removeItem(branch); - } else { - // If no, say that the branch is not locked - await say(`Branch ${branch} is not locked`); - } - break; - case "show": - // Check if the branch has a lock - if (localStorage.getItem(branch)) { - // If yes, say who locked the branch - await say( - `Branch ${branch} locked by <@${localStorage.getItem(branch)}>` - ); - } else { - // If no, say that the branch is not locked - await say(`Branch ${branch} is not locked`); - } - break; - default: - await say("Invalid lock command"); - } -}; - -export default lock_target; diff --git a/.github/actions/slack-boltjs-app/src/utils/github/push.js b/.github/actions/slack-boltjs-app/src/utils/github/push.js deleted file mode 100644 index 3124d2aed65..00000000000 --- a/.github/actions/slack-boltjs-app/src/utils/github/push.js +++ /dev/null @@ -1,182 +0,0 @@ -import request from "./request.js"; -import https from "https"; - -const push = async ({ localStorage, args, api, respond, say, force, isCommand }) => { - branchPush(localStorage, args, api, force, respond, say, isCommand); -}; - -export default push; - -// Define a function to check the configuration for branch push -const branchPushCheckConfiguration = function ( - localStorage, - sourceBranch, - targetBranch, - app, - token, - respond -) { - // Get the deploy targets from the environment variable - const deployTargets = process.env.GITHUB_TARGET_BRANCHES.split(","); - - // Define an array of error messages and conditions - const errors = [ - { - message: "Missing configuration: GITHUB_TOKEN", - condition: !token, - }, - { - message: - "Missing configuration: [for :owner/:repo] or GITHUB_OWNER/GITHUB_REPO", - condition: !app, - }, - { - message: - "Missing <sourceBranch>: gh-deploy <sourceBranch> to <targetBranch>", - condition: !sourceBranch, - }, - { - message: - "Missing <targetBranch>: gh-deploy <sourceBranch> to <targetBranch>", - condition: !targetBranch, - }, - { - message: "Missing configuration: GITHUB_TARGET_BRANCHES", - condition: !process.env.GITHUB_TARGET_BRANCHES, - }, - { - message: `\"${targetBranch}\" is not in available target branches. Use: <@bot name> gh-targets`, - condition: !deployTargets.includes(targetBranch), - }, - ]; - - // Loop through the errors and respond if any condition is true - for (const error of errors) { - if (error.condition) { - respond(error.message); - return false; - } - } - - if (localStorage.getItem(targetBranch)) { - respond(`Branch ${targetBranch} is locked by <@${localStorage.getItem(targetBranch)}>`); - return false; - } - - // Return true if no errors are found - return true; -}; - -// Define constants -const token = process.env.GITHUB_TOKEN; -const app = process.env.GITHUB_REPO; -const port = 443; -const method = "PATCH"; -const headers = { - "User-Agent": "request", - "X-GitHub-Api-Version": "2022-11-28", -}; - -// Define a function to get the SHA of a branch -const getSHA = (api, branch) => { - return new Promise((resolve, reject) => { - // Set the options for the HTTPS request - const options = { - hostname: api, - port, - path: `/repos/${app}/git/refs/heads/${branch}`, - method: "GET", - headers, - }; - - // Make the HTTPS request - const req = https.request(options, (res) => { - let data = ""; - - // Concatenate the data chunks - res.on("data", (chunk) => { - data += chunk; - }); - - // Parse the JSON response and resolve the promise with the SHA - res.on("end", () => { - try { - const sha = JSON.parse(data).object.sha; - resolve(sha); - } catch (error) { - reject(error); - } - }); - }); - - // Handle errors and end the request - req.on("error", (error) => { - reject(error); - }); - req.end(); - }); -}; - -// Define a function to push a branch to another branch -const branchPush = async (localStorage, args, api, force, respond, say, isCommand) => { - // Get the source and target branches from the arguments - const sourceBranch = isCommand ? args[0] : args[2]; - const targetBranch = isCommand ? args[2] : args[4]; - - if ( - !branchPushCheckConfiguration( - localStorage, - sourceBranch, - targetBranch, - app, - token, - respond - ) - ) { - return; - } - - // Get the SHA of the source branch - try { - const sha = await getSHA(api, sourceBranch); - - // Prepare the data for the push request - const data = JSON.stringify({ - sha, - force, - }); - - // Set the path for the push request - const path = `/repos/${app}/git/refs/heads/${targetBranch}`; - - // Make the push request using the request module - const out = await request({ - api, - path, - method, - token, - data, - say, - msg: "", - }); - - // If the push request is successful, respond and say accordingly - if (out) { - const json = JSON.parse(out); - console.log(json); - respond( - `${ - force ? "Force pushed" : "Pushed" - } commit \"${sha}\" to branch \"${targetBranch}\"` - ); - say( - `\`deploy ${sourceBranch} to ${targetBranch} for ${app}\` triggered! :rocket:` - ); - } - } catch (error) { - // If there is an error, say that the branch was not found - say(`I failed to find branch \"${sourceBranch}\"!`); - } - - return true; -}; diff --git a/.github/actions/slack-boltjs-app/src/utils/github/request.js b/.github/actions/slack-boltjs-app/src/utils/github/request.js deleted file mode 100644 index faf1e373bb8..00000000000 --- a/.github/actions/slack-boltjs-app/src/utils/github/request.js +++ /dev/null @@ -1,44 +0,0 @@ -import https from "https"; - -const request = async ({ api, path, method, token, data, say, msg }) => { - const json = await httpsRequest(api, path, method, token, data, say, msg); - return json; -}; - -export default request; - -const httpsRequest = (api, path, method, token, data, say, msg) => { - return new Promise((resolve, reject) => { - const options = { - hostname: api, - port: 443, - path, - method, - headers: { - "User-Agent": "request", - Authorization: `token ${token}`, - "Content-Type": "application/json", - "X-GitHub-Api-Version": "2022-11-28", - }, - }; - const req = https.request(options, async (res) => { - let body = ""; - res.on("data", (chunk) => { - body += chunk; - }); - res.on("end", async () => { - resolve(body); - if (msg) { - await say(msg); - } - }); - }); - req.on("error", (err) => { - reject(err); - }); - if (data) { - req.write(data); - } - req.end(); - }); -}; diff --git a/.github/actions/slack-boltjs-app/src/utils/github/router.js b/.github/actions/slack-boltjs-app/src/utils/github/router.js deleted file mode 100644 index da71834e9ef..00000000000 --- a/.github/actions/slack-boltjs-app/src/utils/github/router.js +++ /dev/null @@ -1,65 +0,0 @@ -import push from "../../utils/github/push.js"; -import listTargets from "./list_targets.js"; -import lockTarget from "./lock_target.js"; -import runWorkflow from "./run_workflow.js"; -import nodeLs from "node-localstorage"; - -const { LocalStorage } = nodeLs; -const { GITHUB_API } = process.env; -const localStorage = new LocalStorage("src/.locks"); -const api = GITHUB_API || "api.github.com"; -const force = true; - -// Define a function to route the commands -const router = async ({ message, say }) => { - const { text, user } = message; - // Split the message text by spaces - const args = text.split(" "); - - // Check if there are inputs after --inputs flag - const inputsIndex = args.indexOf("--inputs"); - let inputs; - if (inputsIndex > -1) { - // Get the inputs from the message text - inputs = text.split("--inputs")[1].trim(); - // Remove the inputs from the args array - args.splice(inputsIndex); - } - - // Get the command from the second argument - const ghCommand = args[1].split("-")[1]; - - try { - // Execute the command based on a switch statement - switch (ghCommand) { - case "deploy": - await push({ - localStorage, - args, - api, - respond: say, - say, - force, - isCommand: false, - }); - break; - case "targets": - await listTargets({ say }); - break; - case "run": - await runWorkflow({ args, api, say, inputs }); - break; - case "lock": - await lockTarget({ localStorage, args, say, user }); - break; - default: - await say(`Invalid command :(: ${ghCommand}`); - } - } catch (error) { - // Handle errors and log them - await say(`gh ${ghCommand} failed with error: ${error}`); - console.error(error); - } -}; - -export default router; diff --git a/.github/actions/slack-boltjs-app/src/utils/github/run_workflow.js b/.github/actions/slack-boltjs-app/src/utils/github/run_workflow.js deleted file mode 100644 index 09881a97c92..00000000000 --- a/.github/actions/slack-boltjs-app/src/utils/github/run_workflow.js +++ /dev/null @@ -1,43 +0,0 @@ -import defaultBranch from "../../utils/github/default_branch.js"; -import request from "../../utils/github/request.js"; - -const run_workflow = async ({ args, api, say, inputs }) => { - const [workflowFile, app = process.env.GITHUB_REPO, branch] = args.slice(2); - const token = process.env.GITHUB_TOKEN; - // Use an object to store the request data - let data = { ref: branch || (await defaultBranch({ api, app, token, say })) }; - - if (inputs) { - // Use JSON.parse and JSON.stringify to convert the inputs to a valid JSON object - data.inputs = JSON.parse( - `{${inputs}}` - .replace(/([,\{] *)(\w+):/g, '$1"$2":') - .replace( - /([,\{] *"\w+":)(?! *-?[0-9\.]+[,\}])(?! *[\{\[])( *)([^,\}]*)/g, - '$1$2"$3"' - ) - ); - } - - const stringData = JSON.stringify(data); - const path = `/repos/${app}/actions/workflows/${workflowFile}/dispatches`; - const method = "POST"; - - const out = await request({ - api, - path, - method, - token, - data: stringData, - say, - }); - if (out) { - say(JSON.parse(out).message); - } else { - say( - `Triggered workflow \`${workflowFile}\` with \`${stringData}\` for \`${app}\`! :rocket:` - ); - } -}; - -export default run_workflow; diff --git a/.github/actions/slack-boltjs-app/src/views/app_home.js b/.github/actions/slack-boltjs-app/src/views/app_home.js deleted file mode 100644 index 11202a820d9..00000000000 --- a/.github/actions/slack-boltjs-app/src/views/app_home.js +++ /dev/null @@ -1,33 +0,0 @@ -const appHome = async ({ event, client }) => { - try { - /* view.publish is the method that your app uses to push a view to the Home tab */ - await client.views.publish({ - /* the user that opened your app's app home */ - user_id: event.user, - - /* the view object that appears in the app home*/ - view: { - type: "home", - callback_id: "home_view", - - /* body of the view */ - blocks: [ - { - type: "section", - text: { - type: "mrkdwn", - text: "Welcome :tada:", - }, - }, - { - type: "divider", - }, - ], - }, - }); - } catch (error) { - console.error(error); - } -}; - -export default appHome; diff --git a/.github/actions/stackoverflow_in_pg/.gitignore b/.github/actions/stackoverflow_in_pg/.gitignore deleted file mode 100755 index cbc82b7d9b1..00000000000 --- a/.github/actions/stackoverflow_in_pg/.gitignore +++ /dev/null @@ -1,145 +0,0 @@ -/Temp-Scripts/* -/Org-Project*/* -/Sensitive/* -/Work/* -/Work-*/* -/Sql-Queries-Office/* -/Sql-Queries-Work/* -/Sql-Queries-Personal/* -/Sql-Queries-Private/* -/Sql-Queries-Volatile/* -/Volatile/* -/Private/* -/Private/Private.ssmssqlproj -/Sensitive/Sensitive.ssmssqlproj -/Volatile/Volatile.ssmssqlproj -/Work/Work.ssmssqlproj - - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -.vscode -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject -.vscode - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ diff --git a/.github/actions/stackoverflow_in_pg/README.md b/.github/actions/stackoverflow_in_pg/README.md deleted file mode 100644 index 65af91c44b7..00000000000 --- a/.github/actions/stackoverflow_in_pg/README.md +++ /dev/null @@ -1,87 +0,0 @@ - -# Requirement - -## install postgres - -On ubuntu, [install postgres on ubuntu](install_pg/install_ubuntu.md)\ -On centos, [install postgres on centos](install_pg/install_centos.md) - -# Import Stackoverflow database in pg - - -Original source: http://www.bortzmeyer.org/stackoverflow-to-postgresql.html - -The social network Stackoverflow (https://stackoverflow.com/) regularly publishes a dump of its database under a Creative Commons free licence. We can find dump file here: - -Main download link: https://archive.org/download/stackexchange - -File download link: -https://archive.org/download/stackexchange/stackoverflow.com-Badges.7z -https://archive.org/download/stackexchange/stackoverflow.com-Comments.7z -https://archive.org/download/stackexchange/stackoverflow.com-PostHistory.7z -https://archive.org/download/stackexchange/stackoverflow.com-PostLinks.7z -https://archive.org/download/stackexchange/stackoverflow.com-Posts.7z -https://archive.org/download/stackexchange/stackoverflow.com-Tags.7z -https://archive.org/download/stackexchange/stackoverflow.com-Users.7z -https://archive.org/download/stackexchange/stackoverflow.com-Votes.7z - -Extract the XML file of each downloaded file. - -At the time of writing this document, the dump files are from June 2017 -Each XML file store a class of Stack Overflow objects: - -| Object | 7zip file size | XML file size | SQL file size (parsed xml) | Lines in pg table | Pg table size | -|---------------|:----------------: |:-------------:|:--------------------------: |:-----------------:|--------------:| -| Badges | 166 MB | 2.51 GB | 1.2 GB | 22 997 200 lines | 1 658 MB | -| Comments | 3.16 GB | 14.2 GB | 10.6 GB | 58 187 400 lines | 11 GB | -| PostHistory | 18.2 GB | 88.9 GB | 66.4 GB | 93 512 900 lines | 54 GB | -| PostLinks | 57 MB | 492 MB | 215 MB | 4 214 710 lines | 242 MB | -| Posts | 10.4 GB | 52.3 GB | 38.1 GB | 36 149 100 lines | 31 GB | -| Tags | 704 KB | 4.18 MB | 1.67 MB | 49 306 lines | 2 808 kB | -| Users | 284 MB | 2.07 GB | 753 MB | 7 246 590 lines | 773 MB | -| Votes | 757 MB | 11.7 GB | 5.23 GB | 128 369 000 lines | 5 422 MB | - -## Transform xml file to sql file - -The python_src directory contains a python file per xml file to parse. Copy these `*.py` files and `*.xml` dump files in same directory. -> [!NOTE] -> Following code has been tested with Python 3.11.5 with Postgres 14. - -To launch the parser -```bash -python so2pg-<xml_to_parse>.py <xml_file>.xml > <parsed_xml>.sql -``` -If you want test it, in python directory there is a subdirectory named sample_xml. -```bash -#change to directory python_src -#launch -python so2pg-badges.py sample_xml > badges.sql - -#you obtain a sql file ready to load to postgres -``` - -## Load sql file in pg database - -```bash -# connect to user postgres -sudo su - postgres -# create your database -createdb --encoding=UTF-8 StackOverflow -#create database table in database StackOverflow -psql -U postgres -d StackOverflow -f so-create.sql -``` -All you have to do now is to load the sql files. -You can load them in the order you want. -I disable all integrity constraints. -Move to the directory where the sql files are located and start the loads like this - -```bash -psql -U postgres -d StackOverflow -f badges.sql -psql -U postgres -d StackOverflow -f comments.sql -psql -U postgres -d StackOverflow -f posthistory.sql -psql -U postgres -d StackOverflow -f postlinks.sql -psql -U postgres -d StackOverflow -f posts.sql -psql -U postgres -d StackOverflow -f tags.sql -psql -U postgres -d StackOverflow -f users.sql -psql -U postgres -d StackOverflow -f votes.sql -``` diff --git a/.github/actions/stackoverflow_in_pg/install_pg/install_centos.md b/.github/actions/stackoverflow_in_pg/install_pg/install_centos.md deleted file mode 100644 index 1c5190abbfb..00000000000 --- a/.github/actions/stackoverflow_in_pg/install_pg/install_centos.md +++ /dev/null @@ -1,105 +0,0 @@ -# Install postgresql from source on Centos 7 - -For example, I want to install a postgresql 9.6.3 (pg) database on Centos 7 \ -Pg will be installed in /opt/pgsql/9.6 \ -My data (PGDATA) will be in /opt/pgsql/9.6/data \ -My Logs (PGLOG) will be in /opt/pgsql/9.6/logs - -### Step 1: Download the sources of postgresql -Download link: https://ftp.postgresql.org/pub/source/v9.6.3/postgresql-9.6.3.tar.bz2 - -### Step 2: Install the required pacquets -```bash -yum install -y bison-devel readline-devel zlib-devel openssl-devel -yum groupinstall -y 'Development Tools' -``` - -### Step 3: Creating the postgres user with for homedir /opt/pgsql -```bash -sudo useradd -d /opt/pgsql -m -r -s /bin/bash postgres -``` - -### Step 4: Move postgres sources to /opt/pgsql/src -```bash -sudo mkdir -p /opt/pgsql/src -sudo mv postgresql-9.6.3.tar.bz2 /opt/pgsql/src/postgresql-9.6.3.tar.bz2 -sudo chown -R postgres: postgres /opt/pgsql/ -``` -### Step 5: Connect with postgres user -```bash -sudo su - postgres -``` - -### Step 6: Export the PATH, LD\_LIBRARY, PGDATA, PGLOG variables to .bashrc_profile -```bash -#config postgres -export PATH = /opt/pgsql/9.6/bin: $PATH -export LD_LIBRARY_PATH = /opt/pgsql/9.6/lib: $LD_LIBRARY_PATH -export PGDATA = /opt/pgsql/9.6/data -export PDLOG = /opt/pgsql/9.6/data/serverlog -``` -> You can add these lines to your .bashrc_profile - -### Step 7: Unpack the postgresql source -```bash -cd src/ -tar -xvjf postgresql-9.6.3.tar.bz2 -``` - -### Step 8: Install pg from sources -```bash -cd postgresql-9.6.3/ -./configure --prefix /opt/pgsql/9.6 -make -make install -``` - -### Step 9: Initialize the pg database -```bash -initdb -D $PGDATA -U postgres -``` - -### Step 10: Service settings for start pg on boot - -```bash -#with an account with a root/sudoer -sudo cp ~postgres/src/postgresql-9.6.3/contrib/start-scripts/linux /etc/init.d/postgresql -sudo chmod +x /etc/init.d/postgresql -``` -Modify the prefix, PGDATA, PGUSER and PGLOG variables in /etc/init.d/postgresql: -```bash -# Installation prefix -prefix = /opt/pgsql/9.6 - -# Data directory -PGDATA = "/opt/pgsql/9.6/data" - -# Who to run the postmaster as, usually "postgres". (NOT "root") -PGUSER = postgres - -# Where to keep a log file -PGLOG = "$PGDATA/ServerLog" -``` - -### Step 11: Starting the service -```bash -sudo chkconfig postgresql -sudo service postgresql start -``` - -### Step 12: Test Connect to Base -```bash -sudo su - postgres -psql -``` -> If you add this -> `export PATH = /opt/pgsql/9.6/bin: $PATH` -> In your .bash_profile, you can run the psql command without having to connect to the user system postgres -> By running the following command: -> `Psql -U postgres` - -### Step 13 (optional): Installing pgadmin4 -```bash -sudo yum install https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm -sudo yum install -y pgadmin4 -``` \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/install_pg/install_ubuntu.md b/.github/actions/stackoverflow_in_pg/install_pg/install_ubuntu.md deleted file mode 100644 index 748c6225e37..00000000000 --- a/.github/actions/stackoverflow_in_pg/install_pg/install_ubuntu.md +++ /dev/null @@ -1,98 +0,0 @@ -# Install Postgresql from source on Ubuntu 16.04 - -For example, I want to install a postgresql 9.6.3 (pg) database on xubuntu 16.04. -Pg will be installed in /opt/pgsql/9.6 \ -My data (PGDATA) will be in /opt/pgsql/9.6/data \ -My Logs (PGLOG) will be in /opt/pgsql/9.6/logs - -### Step 1: Download the sources of postgresql -Download link: https://ftp.postgresql.org/pub/source/v9.6.3/postgresql-9.6.3.tar.bz2 - -### Step 2: Install the required packages -```Bash -sudo apt install -y build-essential libreadline-dev zlib1g-dev flex bison libxml2-dev libxslt-dev libssl-dev libsystemd-dev -``` - -### Step 3: Creating the postgres user with for homedir /opt/pgsql -```Bash -sudo useradd -d /opt/pgsql -m -r -s /bin/bash postgres -``` - -### Step 4: Move postgres sources to /opt/pgsql/src -```Bash -sudo mkdir -p /opt/pgsql/src -sudo mv postgresql-9.6.3.tar.bz2 /opt/pgsql/src/postgresql-9.6.3.tar.bz2 -sudo chown -R postgres:postgres /opt/pgsql/ -``` -### Step 5: Connect with postgres user -```Bash -sudo su - postgres -``` - -### Step 6: Export the PATH, LD\_LIBRARY, PGDATA, PGLOG variables to .bashrc -```Bash -#config postgres -export PATH=/opt/pgsql/9.6/bin:$PATH -export LD_LIBRARY_PATH=/opt/pgsql/9.6/lib:$LD_LIBRARY_PATH -export PGDATA=/opt/pgsql/9.6/data -export PDLOG=/opt/pgsql/9.6/data/serverlog -``` -> You can add these lines to your .bashrc - -### Step 7: Uncompress the postgresql source -```Bash -cd src/ -tar -xvjf postgresql-9.6.3.tar.bz2 -``` - -### Step 8: Install pg from sources -```Bash -cd postgresql-9.6.3/ -./configure --prefix /opt/pgsql/9.6 --with-systemd -make -make install -``` - -### Step 9: Initialize the pg database -```Bash -initdb -D $PGDATA -U postgres -``` - -### Step 10: Service settings for start pg on boot - -```Bash -#with an account with a root/sudoer -sudo cp ~postgres/src/postgresql-9.6.3/contrib/start-scripts/linux /etc/init.d/postgresql -sudo chmod +x /etc/init.d/postgresql -``` -Modify the prefix, PGDATA, PGUSER and PGLOG variables in /etc/init.d/postgresql: -```Bash -# Installation prefix -prefix=/opt/pgsql/9.6 - -# Data directory -PGDATA="/opt/pgsql/9.6/data" - -# Who to run the postmaster as, usually "postgres". (NOT "root") -PGUSER=postgres - -# Where to keep a log file -PGLOG="$PGDATA/serverLog" -``` - -### Step 11: Start the Service -```Bash -sudo update-rc.d postgresql defaults -sudo systemctl start postgresql -``` - -### Step 12: Test connection to Base -```Bash -sudo su - postgres -psql -``` -> If you add this -> `export PATH = / opt / pgsql / 9.6 / bin: $ PATH` -> In your .bashrc, you can run the psql command without having to log on to the user system postgres -> By running the following command: -> `Psql -U postgres` diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Badges.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Badges.xml deleted file mode 100644 index c0c5504bfd0..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Badges.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<badges> - <row Id="82946" UserId="3718" Name="Teacher" Date="2008-09-15T08:55:03.923" Class="3" TagBased="False" /> - <row Id="82947" UserId="994" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> - <row Id="82949" UserId="3893" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> - <row Id="82950" UserId="4591" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> - <row Id="82951" UserId="5196" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> - <row Id="82952" UserId="2635" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> - <row Id="82953" UserId="1113" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> - <row Id="82954" UserId="4182" Name="Teacher" Date="2008-09-15T08:55:03.957" Class="3" TagBased="False" /> -</badges> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Comments.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Comments.xml deleted file mode 100644 index 0e5a4666b5d..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Comments.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<comments> - <row Id="1" PostId="35314" Score="39" Text="not sure why this is getting downvoted -- it is correct! Double check it in your compiler if you don't believe him!" CreationDate="2008-09-06T08:07:10.730" UserId="1" /> - <row Id="2" PostId="35314" Score="8" Text="Yeah, I didn't believe it until I created a console app - but good lord! Why would they give you the rope to hang yourself! I hated that about VB.NET - the OrElse and AndAlso keywords!" CreationDate="2008-09-06T08:09:52.330" UserId="3" /> - <row Id="4" PostId="35195" Score="0" Text="I don't see an accepted answer now, I wonder how that got unaccepted. Incidentally, I would have marked an accepted answer based on the answers available at the time. Also, accepted doesn't mean Best :)" CreationDate="2008-09-06T08:42:16.980" UserId="380" /> - <row Id="9" PostId="47239" Score="0" Text="Jonathan: Wow! Thank you for all of that, you did an amazing amount of work!" CreationDate="2008-09-06T12:26:30.060" UserId="4550" /> - <row Id="10" PostId="45651" Score="6" Text="It will help if you give some details of which database you are using as techniques vary." CreationDate="2008-09-06T13:38:23.647" UserId="242" /> - <row Id="12" PostId="47428" Score="3" Text="One of the things that make a url user-friendly is 'discover-ability', meaning you can take a guess at url's simply from the address bar. http://i.love.pets.com/search/cats+dogs could easily lead to http://i.love.pets.com/search/pug+puppies etc" CreationDate="2008-09-06T13:51:47.843" UserId="4642" /> - <row Id="14" PostId="47481" Score="0" Text="I agree, both CodeRush and RefactorPro are visually impressive (most of which can be turned off BTW), but for navigating and refactoring Resharper is much better in my opinion of using both products." CreationDate="2008-09-06T14:15:46.897" UserId="4642" /> - <row Id="15" PostId="47373" Score="0" Text="Just wanted to mention that this is an excellent solution if you consider the problem to be linear (i.e. treating `A1B2` as a single number). I still think the problem is multi-dimensional, but I guess we'll just have to wait for the author to clarify :)" CreationDate="2008-09-06T14:30:40.217" UserId="2495" /> -</comments> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/PostHistory.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/PostHistory.xml deleted file mode 100644 index 153319661e8..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/PostHistory.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<posthistory> - <row Id="6" PostHistoryTypeId="2" PostId="7" RevisionGUID="c30df0f4-a2d9-426e-a2dd-2cc3aa4d9205" CreationDate="2008-07-31T22:17:57.883" UserId="9" Text="The explicit cast to double in the first answer isn't necessary - identifying the constant as 5000.0 (or as 5000d) is sufficient." /> - <row Id="12" PostHistoryTypeId="1" PostId="17" RevisionGUID="0421fb42-a29a-4cb2-84ba-a828725410f8" CreationDate="2008-08-01T05:09:55.993" UserId="2" Text="Binary Data in MYSQL" /> - <row Id="13" PostHistoryTypeId="3" PostId="17" RevisionGUID="0421fb42-a29a-4cb2-84ba-a828725410f8" CreationDate="2008-08-01T05:09:55.993" UserId="2" Text="<database><mysql>" /> - <row Id="14" PostHistoryTypeId="2" PostId="17" RevisionGUID="0421fb42-a29a-4cb2-84ba-a828725410f8" CreationDate="2008-08-01T05:09:55.993" UserId="2" Text="How do I store binary data in mysql?" /> - <row Id="16" PostHistoryTypeId="2" PostId="18" RevisionGUID="0cfdfa19-039f-4645-8a48-1c316543b98f" CreationDate="2008-08-01T05:12:44.193" UserDisplayName="phpguy" Text="For a table like this:

 CREATE TABLE binary_data (
 id INT(4) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 description CHAR(50),
 bin_data LONGBLOB,
 filename CHAR(50),
 filesize CHAR(50),
 filetype CHAR(50)
 );

Here is a PHP example:

 <?php
 
 // store.php3 - by Florian Dittmer <dittmer@gmx.net>
 // Example php script to demonstrate the storing of binary files into
 // an sql database. More information can be found at http://www.phpbuilder.com/
 ?>
 
 <html>
 <head><title>Store binary data into SQL Database</title></head>
 <body>
 
 <?php
 // code that will be executed if the form has been submitted:
 
 if ($submit) {
 
 // connect to the database
 // (you may have to adjust the hostname,username or password)
 
 MYSQL_CONNECT("localhost","root","password");
 mysql_select_db("binary_data");
 
 $data = addslashes(fread(fopen($form_data, "r"), filesize($form_data)));
 
 $result=MYSQL_QUERY("INSERT INTO binary_data (description,bin_data,filename,filesize,filetype) ".
 "VALUES ('$form_description','$data','$form_data_name','$form_data_size','$form_data_type')");
 
 $id= mysql_insert_id();
 print "<p>This file has the following Database ID: <b>$id</b>";
 
 MYSQL_CLOSE();
 
 } else {
 
 // else show the form to submit new data:
 ?>
 
 <form method="post" action="<?php echo $PHP_SELF; ?>" enctype="multipart/form-data">
 File Description:<br>
 <input type="text" name="form_description" size="40">
 <input type="hidden" name="MAX_FILE_SIZE" value="1000000">
 <br>File to upload/store in database:<br>
 <input type="file" name="form_data" size="40">
 <p><input type="submit" name="submit" value="submit">
 </form>
 
 <?php
 
 }
 
 ?>
 
 </body>
 </html>" /> - <row Id="17" PostHistoryTypeId="1" PostId="25" RevisionGUID="617afb21-e2be-477c-bc74-95e3f319bc5d" CreationDate="2008-08-01T12:13:50.207" UserId="23" Text="How to use the C socket API in C++" /> - <row Id="18" PostHistoryTypeId="3" PostId="25" RevisionGUID="617afb21-e2be-477c-bc74-95e3f319bc5d" CreationDate="2008-08-01T12:13:50.207" UserId="23" Text="<c++><c><sockets><zos>" /> - <row Id="19" PostHistoryTypeId="2" PostId="25" RevisionGUID="617afb21-e2be-477c-bc74-95e3f319bc5d" CreationDate="2008-08-01T12:13:50.207" UserId="23" Text="I've been having issues getting the C sockets API to work properly in C++. Specifically, although I am including sys/socket.h, I still get compile time errors telling me that AF_INET is not defined. Am I missing something obvious, or could this be related to the fact that I'm doing this coding on z/OS and my problems are much more complicated? ;)" /> -</posthistory> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/PostLinks.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/PostLinks.xml deleted file mode 100644 index 3f560d5c9c2..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/PostLinks.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<postlinks> - <row Id="19" CreationDate="2010-04-26T02:59:48.130" PostId="109" RelatedPostId="32412" LinkTypeId="1" /> - <row Id="37" CreationDate="2010-04-26T02:59:48.600" PostId="1970" RelatedPostId="617600" LinkTypeId="1" /> - <row Id="42" CreationDate="2010-04-26T02:59:48.647" PostId="2154" RelatedPostId="2451138" LinkTypeId="1" /> - <row Id="48" CreationDate="2010-04-26T02:59:48.740" PostId="2483" RelatedPostId="496096" LinkTypeId="1" /> - <row Id="52" CreationDate="2010-04-26T02:59:48.757" PostId="2572" RelatedPostId="209329" LinkTypeId="1" /> - <row Id="58" CreationDate="2010-04-26T02:59:48.943" PostId="3376" RelatedPostId="2187" LinkTypeId="1" /> - <row Id="59" CreationDate="2010-04-26T02:59:48.943" PostId="3376" RelatedPostId="18080" LinkTypeId="1" /> - <row Id="63" CreationDate="2010-04-26T02:59:49.083" PostId="3859" RelatedPostId="802573" LinkTypeId="1" /> -</postlinks> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Posts.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Posts.xml deleted file mode 100755 index 8b2235f3376..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Posts.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<posts> - <row Id="44480366" PostTypeId="1" CreationDate="2017-06-11T04:27:05.437" Score="0" ViewCount="2" Body="<p>Novice: Firstly, I am just looking for a simplified explanation of the error in the context of the code below (ofcourse, without the use of <strong>unsafe</strong> keyword. I wrote it to for demonstration purposes (of Call by Reference) only.</p>

<p>This is the function being called:</p>

<pre><code>public int Add(int *x, int* y)
{
 int sumOfXY = *(x) + *(y);
 return sumOfXY;
}
</code></pre>

<p>Here is the Main():</p>

<pre><code>static void Main(string[] args)
{
 int a, b, sum=0;
 Console.Write("Enter the value for a: ");
 a = int.Parse(Console.ReadLine());
 Console.Write("Enter the value for b: ");
 b = int.Parse(Console.ReadLine());
 sum = Add(&amp;a, &amp;b);
}
</code></pre>

<p>So I am getting an error as stated in the title.</p>
" OwnerUserId="5715945" LastActivityDate="2017-06-11T04:27:05.437" Title="Call by Reference: Pointers and fixed size buffers may only be used in an unsafe context" Tags="<c#><pass-by-reference><unsafe-pointers>" AnswerCount="0" CommentCount="0" /> - <row Id="44480367" PostTypeId="2" ParentId="44480222" CreationDate="2017-06-11T04:27:42.857" Score="0" Body="<p>Actually, I do not think Postgresql has this feature to store history of a table though I do not use Postgresql but it's similar to MySQL.You can use two tables to implement your feature. Here is an example(just an example that uses MySQL syntax):</p>

<pre><code>Table 1: 
CREATE TABLE `dog_now`(
 `id` int(11) AUTO_INCREMENT ,
 `dog_id` int(11) DEAFULT '0', -- dog id
 `weight` float DEFAULT '0',
 `owner` varchar(32) DEFAULT '',
 PRIMARY KEY(`id`),
 UNIQUE KEY `idx_dog_id`(`dog_id`)
)
</code></pre>

<p>Table 1 is used to store the current state of a dog.</p>

<pre><code>Table 2:
CREATE TABLE `dog_history`(
 `id` int(11) AUTO_INCREMENT,
 `dog_id` int(11) DEAFULT '0', -- dog id
 `weight` float DEFAULT '0',
 `owner` varchar(32) DEFAULT '',
 PRIMARY KEY (`id`),
 KEY `idx_dog_id`(`dog_id`)
)
</code></pre>

<p>Table 2 is used to store the history of a dog.</p>

<p>Now maybe you want to know how to store the data.
When you want to save the new dog data in database, please query the dog_now table to achieve the data by dog_id. And put the data into dog_history table, update new dog data in dog_now table.</p>
" OwnerUserId="3978491" LastActivityDate="2017-06-11T04:27:42.857" CommentCount="0" /> - <row Id="44480368" PostTypeId="2" ParentId="37219311" CreationDate="2017-06-11T04:28:14.400" Score="0" Body="<p>yes this is possible:</p>

<p><strong>Swift 3</strong></p>

<pre><code>protocol Thing {
 static func genericFunction()
}

//... in another file

var things:[Thing] = []

for thing in things {
 type(of: thing).genericFunction()
}
</code></pre>
" OwnerUserId="7263704" LastActivityDate="2017-06-11T04:28:14.400" CommentCount="0" /> - <row Id="44480369" PostTypeId="1" CreationDate="2017-06-11T04:28:17.503" Score="0" ViewCount="2" Body="<p>My code takes a image from letImageVIEW and tries to place the image on lets just call it imageviewB. The problem is that if there is no image on letImageVIEW the app crashes. How can I use like a check function to prevent my app crashing if there is no image on letImageVIEW. </p>

<pre><code> @IBAction func add(_ sender: Any) {


 let left:UIImage = letImageVIEW.image!
 left.draw(in: CGRect(x: newSize2.width/10.0,y: newSize2.height/8.9,width: newSize2.width/2.5,height: newSize2.height/1.29), blendMode:CGBlendMode.normal, alpha:1.0)
 }
</code></pre>
" OwnerUserId="8105388" LastActivityDate="2017-06-11T04:28:17.503" Title="Create check for button that will create a error (swift3)" Tags="<ios><button><swift3><compiler-errors><null>" AnswerCount="0" CommentCount="0" /> - <row Id="44480370" PostTypeId="2" ParentId="40339914" CreationDate="2017-06-11T04:28:44.900" Score="0" Body="<p>Please check the link below if this answer solves your query.</p>

<p><a href="https://stackoverflow.com/questions/34570064/gmail-fsockopen-ssl-operation-failed-error-with-codeigniter-and-xampp?answertab=votes#tab-top">GMail fsockopen(): SSL operation failed error with Codeigniter and XAMPP</a></p>
" OwnerUserId="4133851" LastActivityDate="2017-06-11T04:28:44.900" CommentCount="0" /> - <row Id="44480371" PostTypeId="2" ParentId="44480279" CreationDate="2017-06-11T04:28:51.170" Score="0" Body="<p>You have created table <code>songs</code> at first and then you have created table <code>albums</code>. When you try to add foreign key <code>album_id</code> at <code>songs</code> table, <code>albums</code> table hasn't been created so that you can't add foreign key to a table with out creation of that table.</p>

<p>So, What you need to is, Create <code>albums</code> table before <code>songs</code> table.</p>
" OwnerUserId="6708661" LastActivityDate="2017-06-11T04:28:51.170" CommentCount="0" /> - <row Id="44480372" PostTypeId="2" ParentId="44447790" CreationDate="2017-06-11T04:28:59.180" Score="0" Body="<p>Your problem is that the images in your banner are too wide to fit in most viewports, so they force the page to be wider.</p>

<p>There is no easy way to correct this purely with CSS without either causing the images to overlap each other or warping the images' aspect ratio.</p>

<p>This CSS will fix the horizontal scrolling, but it will squish the first and last images:</p>

<pre><code>.dittomall-banner img {
 width: 33%;
}
</code></pre>

<p>To truly fix things so that your images can maintain their original aspect ratio and also not cause the page to be too wide, you should edit the images so that they are cropped tightly, then use a <code>background-color</code> on <code>.dittomall-banner</code> that's the same as the background color in the first and third images. Then, center the images within the banner.</p>

<p>Assuming you crop the images so they are all equally sized (e.g., 300px x 100px), your final HTML and CSS could look like this:</p>

<p>HTML:</p>

<pre><code>&lt;div class="dittomall-banner"&gt;
 &lt;div class="banner-inner-wrapper"&gt;
 &lt;img src="http://www.dittomall.com/theme/dittomall/img/banner1.png" alt="banner1"&gt;
 &lt;img src="http://www.dittomall.com/theme/dittomall/img/banner2.png" alt="banner2"&gt;
 &lt;img src="http://www.dittomall.com/theme/dittomall/img/banner3.png" alt="banner3"&gt;
 &lt;/div&gt;
&lt;/div&gt;
</code></pre>

<p>CSS:</p>

<pre><code>.dittomall-banner {
 display: flex;
 background-color: #ed8c7b;
}
.banner-inner-wrapper {
 margin: 0 auto;
}
</code></pre>
" OwnerUserId="6656062" LastActivityDate="2017-06-11T04:28:59.180" CommentCount="0" /> - <row Id="44480373" PostTypeId="1" CreationDate="2017-06-11T04:28:59.997" Score="0" ViewCount="1" Body="<p>I defined a type something like this:</p>

<pre><code>type Similarity = (String, Int) =&gt; Boolean
</code></pre>

<p>Now I created a function that expects this type as an argument to method like this:</p>

<pre><code>def calculate(param: Similarity) = {
 println("hi")
 }
</code></pre>

<p>My question is how can I pass arguments to calculate function? for instance if I want to pass a string and a number?</p>
" OwnerUserId="3839735" LastActivityDate="2017-06-11T04:28:59.997" Title="How to pass type argument to method" Tags="<scala>" AnswerCount="0" CommentCount="0" /> - <row Id="44480374" PostTypeId="2" ParentId="44480319" CreationDate="2017-06-11T04:29:07.157" Score="0" Body="<p>You can do this way,</p>

<pre><code>private void finalizeButton_Click(object sender, EventArgs e)
 {
 foreach (var cmbObj in cartComboBox.Items)
 {
 if (prices.Keys.Contains(cmbObj.ToString()))
 {
 cartListView.Items.Add(cmbObj.ToString());

 }
 }

 }
</code></pre>
" OwnerUserId="1749403" LastActivityDate="2017-06-11T04:29:07.157" CommentCount="0" /> -</posts> diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Tags.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Tags.xml deleted file mode 100644 index 7b6dfcc6918..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Tags.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<tags> - <row Id="1" TagName=".net" Count="253350" ExcerptPostId="3624959" WikiPostId="3607476" /> - <row Id="2" TagName="html" Count="660738" ExcerptPostId="3673183" WikiPostId="3673182" /> - <row Id="3" TagName="javascript" Count="1404363" ExcerptPostId="3624960" WikiPostId="3607052" /> - <row Id="4" TagName="css" Count="474072" ExcerptPostId="3644670" WikiPostId="3644669" /> - <row Id="5" TagName="php" Count="1085188" ExcerptPostId="3624936" WikiPostId="3607050" /> - <row Id="8" TagName="c" Count="252720" ExcerptPostId="3624961" WikiPostId="3607013" /> - <row Id="9" TagName="c#" Count="1102833" ExcerptPostId="3624962" WikiPostId="3607007" /> - <row Id="10" TagName="c++" Count="518248" ExcerptPostId="3624963" WikiPostId="3606997" /> -</tags> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Users.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Users.xml deleted file mode 100644 index 0dc967c79b7..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Users.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<users> - <row Id="-1" Reputation="1" CreationDate="2008-07-31T00:00:00.000" DisplayName="Community" LastAccessDate="2008-08-26T00:16:53.810" WebsiteUrl="http://meta.stackexchange.com/" Location="on the server farm" AboutMe="<p>Hi, I'm not really a person.</p>

<p>I'm a background process that helps keep this site clean!</p>

<p>I do things like</p>

<ul>
<li>Randomly poke old unanswered questions every hour so they get some attention</li>
<li>Own community questions and answers so nobody gets unnecessary reputation from them</li>
<li>Own downvotes on spam/evil posts that get permanently deleted</li>
<li>Own suggested edits from anonymous users</li>
<li><a href="http://meta.stackexchange.com/a/92006">Remove abandoned questions</a></li>
</ul>
" Views="649" UpVotes="210564" DownVotes="829321" AccountId="-1" /> - <row Id="1" Reputation="40983" CreationDate="2008-07-31T14:22:31.287" DisplayName="Jeff Atwood" LastAccessDate="2017-06-10T03:51:00.423" WebsiteUrl="http://www.codinghorror.com/blog/" Location="El Cerrito, CA" AboutMe="<p><a href="http://www.codinghorror.com/blog/archives/001169.html" rel="nofollow">Stack Overflow Valued Associate #00001</a></p>

<p>Wondering how our software development process works? <a href="http://www.youtube.com/watch?v=08xQLGWTSag" rel="nofollow">Take a look!</a></p>

<p>Find me <a href="http://twitter.com/codinghorror" rel="nofollow">on twitter</a>, or <a href="http://www.codinghorror.com/blog" rel="nofollow">read my blog</a>. Don't say I didn't warn you <em>because I totally did</em>.</p>

<p>However, <a href="http://www.codinghorror.com/blog/2012/02/farewell-stack-exchange.html" rel="nofollow">I no longer work at Stack Exchange, Inc</a>. I'll miss you all. Well, <em>some</em> of you, anyway. :)</p>
" Views="266244" UpVotes="3341" DownVotes="1307" ProfileImageUrl="https://www.gravatar.com/avatar/51d623f33f8b83095db84ff35e15dbe8?s=128&amp;d=identicon&amp;r=PG" Age="47" AccountId="1" /> - <row Id="2" Reputation="3005" CreationDate="2008-07-31T14:22:31.287" DisplayName="Geoff Dalgas" LastAccessDate="2017-06-10T15:11:17.273" WebsiteUrl="http://stackoverflow.com" Location="Corvallis, OR, United States" AboutMe="<p>Developer on the Stack Overflow team. Find me on</p>

<p><a href="http://www.twitter.com/SuperDalgas" rel="nofollow">Twitter</a>
<br><br>
<a href="http://blog.stackoverflow.com/2009/05/welcome-stack-overflow-valued-associate-00003/">Stack Overflow Valued Associate #00003</a></p>
" Views="21519" UpVotes="639" DownVotes="88" Age="40" AccountId="2" /> - <row Id="3" Reputation="12887" CreationDate="2008-07-31T14:22:31.287" DisplayName="Jarrod Dixon" LastAccessDate="2017-06-11T02:09:54.423" WebsiteUrl="http://jarroddixon.com" Location="Raleigh, NC, United States" AboutMe="<p><a href="http://blog.stackoverflow.com/2009/01/welcome-stack-overflow-valued-associate-00002/">Developer on the Stack Overflow team</a>.</p>

<p>Was dubbed <strong>SALTY SAILOR</strong> by Jeff Atwood, as filth and flarn would oft-times fly when dealing with a particularly nasty bug!</p>

<ul>
<li>Twitter me: <a href="http://twitter.com/jarrod_dixon" rel="nofollow noreferrer">jarrod_dixon</a></li>
<li>Email me: jarrod.m.dixon@gmail.com</li>
</ul>
" Views="22600" UpVotes="6747" DownVotes="100" Age="38" AccountId="3" /> - <row Id="4" Reputation="27228" CreationDate="2008-07-31T14:22:31.317" DisplayName="Joel Spolsky" LastAccessDate="2017-06-08T18:19:58.113" WebsiteUrl="http://www.joelonsoftware.com/" Location="New York, NY" AboutMe="<p>I am:</p>

<ul>
<li>the co-founder and CEO of <a href="http://stackexchange.com">Stack Exchange</a></li>
<li>the co-founder of <a href="http://www.fogcreek.com" rel="nofollow">Fog Creek Software</a></li>
<li>the creator and chairman of the board of <a href="http://trello.com" rel="nofollow">Trello</a></li>
<li>owner of Taco, the most famous Siberian Husky on the Upper West Side.</li>
</ul>

<p>You can find me on Twitter (as <a href="http://twitter.com/spolsky" rel="nofollow">@spolsky</a>) or on my rarely-updated blog, <a href="http://joelonsoftware.com" rel="nofollow">Joel on Software</a>.</p>
" Views="69136" UpVotes="785" DownVotes="96" ProfileImageUrl="https://i.stack.imgur.com/C5gBG.jpg?s=128&g=1" AccountId="4" /> - <row Id="5" Reputation="36565" CreationDate="2008-07-31T14:22:31.317" DisplayName="Jon Galloway" LastAccessDate="2017-05-20T19:07:51.353" WebsiteUrl="http://weblogs.asp.net/jgalloway/" Location="San Diego, CA" AboutMe="<p>Technical Evangelist at Microsoft, specializing in ASP.NET MVC.</p>

<p>I don't use this site anymore because the moderators close or delete far too many of the useful questions.</p>
" Views="10398" UpVotes="778" DownVotes="34" Age="47" AccountId="5" /> - <row Id="8" Reputation="942" CreationDate="2008-07-31T21:33:24.057" DisplayName="Eggs McLaren" LastAccessDate="2012-10-15T22:00:45.510" WebsiteUrl="" Location="" AboutMe="<p>This is a puppet test account I use to validate "regular user" stuff on the site</p>
<p>-- <a href="http://stackoverflow.com/users/1/jeff-atwood" rel="nofollow">Jeff Atwood</a>" Views="5576" UpVotes="12" DownVotes="9" AccountId="6" /> - <row Id="9" Reputation="12364" CreationDate="2008-07-31T21:35:26.517" DisplayName="Kevin Dente" LastAccessDate="2017-06-07T19:05:51.593" WebsiteUrl="http://weblogs.asp.net/kdente" Location="Oakland, CA" AboutMe="<p>Independent software engineer</p>
" Views="4244" UpVotes="45" DownVotes="4" Age="46" AccountId="7" /> -</users> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Votes.xml b/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Votes.xml deleted file mode 100644 index 4d09bde6005..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/sample_xml/Votes.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<votes> - <row Id="1" PostId="1" VoteTypeId="2" CreationDate="2008-07-31T00:00:00.000" /> - <row Id="2" PostId="3" VoteTypeId="2" CreationDate="2008-07-31T00:00:00.000" /> - <row Id="3" PostId="2" VoteTypeId="2" CreationDate="2008-07-31T00:00:00.000" /> - <row Id="4" PostId="4" VoteTypeId="2" CreationDate="2008-07-31T00:00:00.000" /> - <row Id="5" PostId="6" VoteTypeId="2" CreationDate="2008-07-31T00:00:00.000" /> - <row Id="6" PostId="7" VoteTypeId="2" CreationDate="2008-07-31T00:00:00.000" /> - <row Id="7" PostId="13" VoteTypeId="2" CreationDate="2008-08-01T00:00:00.000" /> - <row Id="9" PostId="4" VoteTypeId="2" CreationDate="2008-08-01T00:00:00.000" /> -</votes> \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-badges.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-badges.py deleted file mode 100644 index 20139ca1780..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/so2pg-badges.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python - -import xml.etree.cElementTree as ElementTree -import os -import sys -import re - -db = "so" -encoding = "UTF-8" - -def escape(str): - str = re.sub("\s", " ", str) - # \ are special for Python strings *and* for regexps. Hence the - # multiple escaping. Here,we just replace every \ by \\ for - # PostgreSQL - str = re.sub("\\\\", "\\\\\\\\", str) - return str - -def tag_parse(str): - index = 0 - while index < len(str): - if str[index] == '<': - try: - end_tag = str[index:].index('>') - yield str[(index+1):(index+end_tag)] - index += end_tag + 1 - except ValueError: - raise Exception("Tag parsing error in \"%s\"" % str); - else: - raise Exception("Tag parsing error in \"%s\"" % str); - -if len(sys.argv) != 2: - raise Exception("Usage: %s so-files-directory" % sys.argv[0]) - -#os.chdir(sys.argv[1]) - -filename = "Badges.xml" -badges = ElementTree.iterparse(filename) -print ("COPY Badges (id, userid, name, date, badgeclass, tagbased) FROM stdin;") - -for event, badge in badges: - if event == "end" and badge.tag == "row": - id = int(badge.attrib["Id"]) - - userid = int(badge.attrib["UserId"]) - - name = escape(badge.attrib["Name"]) - - date = escape(badge.attrib["Date"]) - - badgeclass = badge.attrib["Class"] - - tagbased = badge.attrib["TagBased"] - - print ("%i\t%s\t%s\t%s\t%s\t%s" % (id, userid, name.encode(encoding), date, badgeclass, tagbased)) - badge.clear() - -print ("\.") - diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-comments.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-comments.py deleted file mode 100644 index 11e07bed35e..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/so2pg-comments.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python - -import xml.etree.cElementTree as ElementTree -import os -import sys -import re - -db = "so" -encoding = "UTF-8" - -def escape(str): - str = re.sub("\s", " ", str) - # \ are special for Python strings *and* for regexps. Hence the - # multiple escaping. Here,we just replace every \ by \\ for - # PostgreSQL - str = re.sub("\\\\", "\\\\\\\\", str) - return str - -def tag_parse(str): - index = 0 - while index < len(str): - if str[index] == '<': - try: - end_tag = str[index:].index('>') - yield str[(index+1):(index+end_tag)] - index += end_tag + 1 - except ValueError: - raise Exception("Tag parsing error in \"%s\"" % str); - else: - raise Exception("Tag parsing error in \"%s\"" % str); - -if len(sys.argv) != 2: - raise Exception("Usage: %s so-files-directory" % sys.argv[0]) - -#os.chdir(sys.argv[1]) - -filename = "Comments.xml" -comments = ElementTree.iterparse(filename) -print ("COPY Comments (id, postid, score, text, creation, userid) FROM stdin;") - -for event, comment in comments: - if event == "end" and comment.tag == "row": - id = int(comment.attrib["Id"]) - - postid = int(comment.attrib["PostId"]) - - score = int(comment.attrib["Score"]) - - text = escape(comment.attrib["Text"]) - - creation = comment.attrib["CreationDate"] - - if "UserId" in comment.attrib: - userid = int(comment.attrib["UserId"]) - else: - userid = -1 - - print ("%i\t%s\t%s\t%s\t%s\t%s" % (id, postid, score, text.encode(encoding), creation, userid)) - comment.clear() - -print ("\.") - diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-posthistory.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-posthistory.py deleted file mode 100644 index 10f14b54aaa..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/so2pg-posthistory.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python - -import xml.etree.cElementTree as ElementTree -import os -import sys -import re - -db = "so" -encoding = "UTF-8" - -def escape(str): - str = re.sub("\s", " ", str) - # \ are special for Python strings *and* for regexps. Hence the - # multiple escaping. Here,we just replace every \ by \\ for - # PostgreSQL - str = re.sub("\\\\", "\\\\\\\\", str) - return str - -def tag_parse(str): - index = 0 - while index < len(str): - if str[index] == '<': - try: - end_tag = str[index:].index('>') - yield str[(index+1):(index+end_tag)] - index += end_tag + 1 - except ValueError: - raise Exception("Tag parsing error in \"%s\"" % str); - else: - raise Exception("Tag parsing error in \"%s\"" % str); - -if len(sys.argv) != 2: - raise Exception("Usage: %s so-files-directory" % sys.argv[0]) - -#os.chdir(sys.argv[1]) - -filename = "PostHistory.xml" -postHistory = ElementTree.iterparse(filename) -tags = {} -tag_id = 1 -print ("COPY posthistory (id, type, postid, revisionguid, creation, userid, userdisplaymame, text) FROM stdin;") - -for event, post in postHistory: - if event == "end" and post.tag == "row": - id = int(post.attrib["Id"]) - - type = int(post.attrib["PostHistoryTypeId"]) - - postid = int(post.attrib["PostId"]) - - revisionguid = post.attrib["RevisionGUID"] - - creation = post.attrib["CreationDate"] - - #if post.attrib.has_key("UserId"): - if "UserId" in post.attrib: - userid = int(post.attrib["UserId"]) - else: - userid = -1 - - #if post.attrib.has_key("UserDisplayName"): - if "UserDisplayName" in post.attrib: - userdisplaymame = escape(post.attrib["UserDisplayName"]) - else: - userdisplaymame = "\n" - - #if post.attrib.has_key("Text"): - if "Text" in post.attrib: - text = escape(post.attrib["Text"]) - else: - text = "\n" - - print ("%i\t%s\t%s\t%s\t%s\t%s\t%s\t%s" % (id, type, postid, revisionguid, creation, userid, userdisplaymame.encode(encoding), text.encode(encoding))) - post.clear() - -print ("\.") - diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-postlinks.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-postlinks.py deleted file mode 100644 index 438aae5681a..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/so2pg-postlinks.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python - -import xml.etree.cElementTree as ElementTree -import os -import sys -import re - -db = "so" -encoding = "UTF-8" - -def escape(str): - str = re.sub("\s", " ", str) - # \ are special for Python strings *and* for regexps. Hence the - # multiple escaping. Here,we just replace every \ by \\ for - # PostgreSQL - str = re.sub("\\\\", "\\\\\\\\", str) - return str - -def tag_parse(str): - index = 0 - while index < len(str): - if str[index] == '<': - try: - end_tag = str[index:].index('>') - yield str[(index+1):(index+end_tag)] - index += end_tag + 1 - except ValueError: - raise Exception("Tag parsing error in \"%s\"" % str); - else: - raise Exception("Tag parsing error in \"%s\"" % str); - -if len(sys.argv) != 2: - raise Exception("Usage: %s so-files-directory" % sys.argv[0]) - -#os.chdir(sys.argv[1]) - -filename = "PostLinks.xml" -postlinks = ElementTree.iterparse(filename) -print ("COPY postlinks (id, creation, postid, relatedpostid, linktypeid) FROM stdin;") -for event, postlink in postlinks: - if event == "end" and postlink.tag == "row": - id = int(postlink.attrib["Id"]) - - creation = postlink.attrib["CreationDate"] - - postid = int(postlink.attrib["PostId"]) - - #if postlink.attrib.has_key("RelatedPostId"): - if "RelatedPostId" in postlink.attrib: - relatedpostid = postlink.attrib["RelatedPostId"] - else: - relatedpostid = "\n" - - if "LinkTypeId" in postlink.attrib: - #if postlink.attrib.has_key("LinkTypeId"): - linktypeid = postlink.attrib["LinkTypeId"] - else: - linktypeid = "\n" - - print ("%i\t%s\t%s\t%s\t%s" % (id, creation, postid, relatedpostid, linktypeid)) - postlink.clear() -print ("\.") \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-posts.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-posts.py deleted file mode 100644 index 748fca1186f..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/so2pg-posts.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env python - -import xml.etree.cElementTree as ElementTree -import os -import sys -import re - -db = "so" -encoding = "UTF-8" - -def escape(str): - str = re.sub("\s", " ", str) - # \ are special for Python strings *and* for regexps. Hence the - # multiple escaping. Here,we just replace every \ by \\ for - # PostgreSQL - str = re.sub("\\\\", "\\\\\\\\", str) - return str - -def tag_parse(str): - index = 0 - while index < len(str): - if str[index] == '<': - try: - end_tag = str[index:].index('>') - yield str[(index+1):(index+end_tag)] - index += end_tag + 1 - except ValueError: - raise Exception("Tag parsing error in \"%s\"" % str); - else: - raise Exception("Tag parsing error in \"%s\"" % str); - -if len(sys.argv) != 2: - raise Exception("Usage: %s so-files-directory" % sys.argv[0]) - -#os.chdir(sys.argv[1]) - -filename = "Posts.xml" -posts = ElementTree.iterparse(filename) -tags = {} -tag_id = 1 -print ("COPY posts (id, type, creation, score, viewcount, title, body, userid, lastactivity, tags, answercount, commentcount) FROM stdin;") - -for event, post in posts: - if event == "end" and post.tag == "row": - id = int(post.attrib["Id"]) - - #if post.attrib.has_key("PostTypeId"): - if "PostTypeId" in post.attrib: - type = int(post.attrib["PostTypeId"]) - else: - type = "\n" - - creation = post.attrib["CreationDate"] - - #if post.attrib.has_key("Score"): - if "Score" in post.attrib: - score = int(post.attrib["Score"]) - else: - score = "-1" - - #if post.attrib.has_key("ViewCount"): - if "ViewCount" in post.attrib: - viewcount = int(post.attrib["ViewCount"]) - else: - viewcount = "-1" - - #if post.attrib.has_key("Title"): - if "Title" in post.attrib: - title = escape(post.attrib["Title"]) - else: - title = "\n" - - #if post.attrib.has_key("Body"): - if "Body" in post.attrib: - body = escape(post.attrib["Body"]) - else: - body = "\n" - - #if post.attrib.has_key("OwnerUserId"): - if "OwnerUserId" in post.attrib: - owner = post.attrib["OwnerUserId"] - else: - owner = "-1" - - #if post.attrib.has_key("LastActivityDate"): - if "LastActivityDate" in post.attrib: - lastactivity = post.attrib["LastActivityDate"] - else: - lastactivity = "\n" - - #if post.attrib.has_key("Tags"): - if "Tags" in post.attrib: - tags = escape(post.attrib["Tags"]) - else: - tags = "\n" - - #if post.attrib.has_key("AnswerCount"): - if "AnswerCount" in post.attrib: - answercount = int(post.attrib["AnswerCount"]) - else: - answercount = "-1" - - #if post.attrib.has_key("CommentCount"): - if "CommentCount" in post.attrib: - commentcount = int(post.attrib["CommentCount"]) - else: - commentcount = "-1" - - print ("%i\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" % (id, type, creation, score, viewcount, title.encode(encoding), body.encode(encoding), owner, lastactivity, tags.encode(encoding), answercount, commentcount)) - post.clear() - -print ("\.") - diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-tags.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-tags.py deleted file mode 100644 index 09dd44423f1..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/so2pg-tags.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python - -import xml.etree.cElementTree as ElementTree -import os -import sys -import re - -db = "so" -encoding = "UTF-8" - -def escape(str): - str = re.sub("\s", " ", str) - # \ are special for Python strings *and* for regexps. Hence the - # multiple escaping. Here,we just replace every \ by \\ for - # PostgreSQL - str = re.sub("\\\\", "\\\\\\\\", str) - return str - -def tag_parse(str): - index = 0 - while index < len(str): - if str[index] == '<': - try: - end_tag = str[index:].index('>') - yield str[(index+1):(index+end_tag)] - index += end_tag + 1 - except ValueError: - raise Exception("Tag parsing error in \"%s\"" % str); - else: - raise Exception("Tag parsing error in \"%s\"" % str); - -if len(sys.argv) != 2: - raise Exception("Usage: %s so-files-directory" % sys.argv[0]) - -#os.chdir(sys.argv[1]) - -filename = "Tags.xml" -tags = ElementTree.iterparse(filename) -print ("COPY tags (id, name, count, excerptpost, wikipost) FROM stdin;") -for event, tag in tags: - if event == "end" and tag.tag == "row": - id = int(tag.attrib["Id"]) - - name = tag.attrib["TagName"] - - count = int(tag.attrib["Count"]) - - #if tag.attrib.has_key("ExcerptPostId"): - if "ExcerptPostId" in tag.attrib: - excerptpost = int(tag.attrib["ExcerptPostId"]) - else: - excerptpost = int("-1") - - #if tag.attrib.has_key("WikiPostId"): - if "WikiPostId" in tag.attrib: - wikipost = int(tag.attrib["WikiPostId"]) - else: - #wikipost = "\n" - wikipost = int("-1") - - print ("%i\t%s\t%d\t%d\t%d" % (id, name, count, excerptpost, wikipost)) - tag.clear() -print ("\.") \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-users.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-users.py deleted file mode 100644 index 314cdbda010..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/so2pg-users.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env python - -import xml.etree.cElementTree as ElementTree -import os -import sys -import re - -db = "so" -encoding = "UTF-8" - -def escape(str): - str = re.sub("\s", " ", str) - # \ are special for Python strings *and* for regexps. Hence the - # multiple escaping. Here,we just replace every \ by \\ for - # PostgreSQL - str = re.sub("\\\\", "\\\\\\\\", str) - return str - -def tag_parse(str): - index = 0 - while index < len(str): - if str[index] == '<': - try: - end_tag = str[index:].index('>') - yield str[(index+1):(index+end_tag)] - index += end_tag + 1 - except ValueError: - raise Exception("Tag parsing error in \"%s\"" % str); - else: - raise Exception("Tag parsing error in \"%s\"" % str); - -if len(sys.argv) != 2: - raise Exception("Usage: %s so-files-directory" % sys.argv[0]) - -#os.chdir(sys.argv[1]) - -filename = "Users.xml" -users = ElementTree.iterparse(filename) -print ("COPY users (id, reputation, creation, name, lastaccess, website, location, aboutme, views, upvotes, downvotes, age) FROM stdin;") -for event, user in users: - if event == "end" and user.tag == "row": - id = int(user.attrib["Id"]) - - reputation = int(user.attrib["Reputation"]) - - creation = user.attrib["CreationDate"] - - #if user.attrib.has_key("DisplayName"): # Yes, some users have no name, for instance 155 :-( - if "DisplayName" in user.attrib: - name = escape(user.attrib["DisplayName"]) - else: - name = "\n" - - #if user.attrib.has_key("LastAccessDate"): - if "LastAccessDate" in user.attrib: - lastaccess = escape(user.attrib["LastAccessDate"]) - else: - lastaccess = "\n" - - #if user.attrib.has_key("WebsiteUrl"): - if "WebsiteUrl" in user.attrib: - website = escape(user.attrib["WebsiteUrl"]) - else: - website = "\n" - - #if user.attrib.has_key("Location"): - if "Location" in user.attrib: - location = escape(user.attrib["Location"]) - else: - location = "\n" - - #if user.attrib.has_key("AboutMe"): - if "AboutMe" in user.attrib: - aboutme = escape(user.attrib["AboutMe"]) - else: - aboutme = "\n" - - #if user.attrib.has_key("Views"): - if "Views" in user.attrib: - views = int(user.attrib["Views"]) - else: - views = 0 - - #if user.attrib.has_key("UpVotes"): - if "UpVotes" in user.attrib: - upvotes = int(user.attrib["UpVotes"]) - else: - upvotes = 0 - - #if user.attrib.has_key("DownVotes"): - if "DownVotes" in user.attrib: - downvotes = int(user.attrib["DownVotes"]) - else: - downvotes = 0 - - #if user.attrib.has_key("Age"): - if "Age" in user.attrib: - age = int(user.attrib["Age"]) - else: - age = -1 - - print ("%i\t%i\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" % (id, reputation, creation, name.encode(encoding), lastaccess, website.encode(encoding), location.encode(encoding), aboutme.encode(encoding), views, upvotes, downvotes, age)) - user.clear() -print ("\.") \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/python_src/so2pg-votes.py b/.github/actions/stackoverflow_in_pg/python_src/so2pg-votes.py deleted file mode 100644 index 5b615eca280..00000000000 --- a/.github/actions/stackoverflow_in_pg/python_src/so2pg-votes.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python - -import xml.etree.cElementTree as ElementTree -import os -import sys -import re - -db = "so" -encoding = "UTF-8" - -def escape(str): - str = re.sub("\s", " ", str) - # \ are special for Python strings *and* for regexps. Hence the - # multiple escaping. Here,we just replace every \ by \\ for - # PostgreSQL - str = re.sub("\\\\", "\\\\\\\\", str) - return str - -def tag_parse(str): - index = 0 - while index < len(str): - if str[index] == '<': - try: - end_tag = str[index:].index('>') - yield str[(index+1):(index+end_tag)] - index += end_tag + 1 - except ValueError: - raise Exception("Tag parsing error in \"%s\"" % str); - else: - raise Exception("Tag parsing error in \"%s\"" % str); - -if len(sys.argv) != 2: - raise Exception("Usage: %s so-files-directory" % sys.argv[0]) - -#os.chdir(sys.argv[1]) - -filename = "Votes.xml" -votes = ElementTree.iterparse(filename) -print ("COPY votes (id, type, postid, creation) FROM stdin;") -for event, vote in votes: - if event == "end" and vote.tag == "row": - id = int(vote.attrib["Id"]) - - type = int(vote.attrib["VoteTypeId"]) - - postid = vote.attrib["PostId"] - - creation = vote.attrib["CreationDate"] - - print ("%i\t%s\t%s\t%s" % (id, type, postid, creation)) - vote.clear() -print ("\.") \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/scripts/backup-restore.sh b/.github/actions/stackoverflow_in_pg/scripts/backup-restore.sh deleted file mode 100644 index 899f8fb968d..00000000000 --- a/.github/actions/stackoverflow_in_pg/scripts/backup-restore.sh +++ /dev/null @@ -1,20 +0,0 @@ -# Save password -export PGPASSWORD='<password>' - -# Backup Database -pg_dump --help - -pg_dump -Fc -h localhost -U postgres DBA -f DBA.dump -pg_dump -Fc -h localhost -U postgres StackOverflow -f StackOverflow-Backup-20231214.dump -Z 9 - - -# Restore Database -pg_restore --help - -# connect, and create a database -psql -h localhost -d postgres -U postgres -create database DBA2 with owner = 'postgres'; - -# Restore database content from dump -pg_restore -d DBA2 -h localhost -U postgres DBA.dump - diff --git a/.github/actions/stackoverflow_in_pg/scripts/convert-xml-2-sql.sh b/.github/actions/stackoverflow_in_pg/scripts/convert-xml-2-sql.sh deleted file mode 100644 index 957666dc9cb..00000000000 --- a/.github/actions/stackoverflow_in_pg/scripts/convert-xml-2-sql.sh +++ /dev/null @@ -1,25 +0,0 @@ -# (base) saanvi@ryzen9:/hyperactive/StackOverflow-Dumps$ - -# done -python so2pg-badges.py /hyperactive/StackOverflow-Dumps/Badges.xml > badges.sql - -# done -python so2pg-comments.py /hyperactive/StackOverflow-Dumps/Comments.xml > comments.sql - -# done -python so2pg-posthistory.py /hyperactive/StackOverflow-Dumps/PostsHistory.xml > posthistory.sql - -# done -python so2pg-postlinks.py /hyperactive/StackOverflow-Dumps/PostLinks.xml > postlinks.sql - -# done -python so2pg-posts.py /hyperactive/StackOverflow-Dumps/Posts.xml > posts.sql - -# done -python so2pg-tags.py /hyperactive/StackOverflow-Dumps/Tags.xml > tags.sql - -# done -python so2pg-users.py /hyperactive/StackOverflow-Dumps/Users.xml > users.sql - -# done -python so2pg-votes.py /hyperactive/StackOverflow-Dumps/Votes.xml > votes.sql diff --git a/.github/actions/stackoverflow_in_pg/scripts/push-2-pg.sh b/.github/actions/stackoverflow_in_pg/scripts/push-2-pg.sh deleted file mode 100644 index 29e383c38f1..00000000000 --- a/.github/actions/stackoverflow_in_pg/scripts/push-2-pg.sh +++ /dev/null @@ -1,22 +0,0 @@ -# Below worked for me -export PGPASSWORD='<password>' -psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./badges.sql # done - -psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./comments.sql # done -psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./posthistory.sql # done - -psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./postlinks.sql # done - -psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./posts.sql # done -psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./users.sql # done -psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./tags.sql # done -psql -h localhost -d StackOverflow -U postgres -p 5432 -a -q -f ./votes.sql # done - - -psql -U postgres -d StackOverflow -f comments.sql -psql -U postgres -d StackOverflow -f posthistory.sql -psql -U postgres -d StackOverflow -f postlinks.sql -psql -U postgres -d StackOverflow -f posts.sql -psql -U postgres -d StackOverflow -f tags.sql -psql -U postgres -d StackOverflow -f users.sql -psql -U postgres -d StackOverflow -f votes.sql \ No newline at end of file diff --git a/.github/actions/stackoverflow_in_pg/so-create.sql b/.github/actions/stackoverflow_in_pg/so-create.sql deleted file mode 100755 index 43991379e90..00000000000 --- a/.github/actions/stackoverflow_in_pg/so-create.sql +++ /dev/null @@ -1,94 +0,0 @@ -ALTER TABLE Comments DROP CONSTRAINT comments_id_fkey; -DROP TABLE Votes; -DROP TABLE Tags; -DROP TABLE Posts; -DROP TABLE Comments; -DROP TABLE Users; -DROP TABLE PostLinks; -DROP TABLE PostHistory; -DROP TABLE Badges; - - - -CREATE TABLE Users ( - id INTEGER UNIQUE NOT NULL, --Id - reputation INTEGER NOT NULL, --Reputation - creation TIMESTAMP NOT NULL, --CreationDate - name TEXT, --DisplayName Yes, can be null some times - lastaccess TIMESTAMP, --LastAccessDate - website TEXT, --WebsiteUrl - location TEXT, --Location - aboutme TEXT, --AboutMe - views INTEGER, --Views - upvotes INTEGER, --upvotes - downvotes INTEGER, --downvotes - age INTEGER --age -); - -CREATE TABLE Comments ( - id INTEGER UNIQUE NOT NULL, --Id - postid INTEGER NOT NULL, --PostId - score INTEGER, --Score - text TEXT, --Text - creation TIMESTAMP NOT NULL, --CreationDate - userid INTEGER --UserId -); - -CREATE TABLE Posts ( - id INTEGER UNIQUE NOT NULL, --Id - type INTEGER NOT NULL, --PostTypeId - creation TIMESTAMP NOT NULL, --CreationDate - score INTEGER, --Score - viewcount INTEGER, --ViewCount - title TEXT, --Title - body TEXT, --Body - userid INTEGER, --OwnerUserId - lastactivity TIMESTAMP, --LastActivityDate - tags TEXT, --Tags - answercount INTEGER, --AnswerCount - commentcount INTEGER --CommentCount - ); - -CREATE TABLE Tags ( - id INTEGER UNIQUE NOT NULL, --Id - name TEXT UNIQUE NOT NULL, --TagName - count INTEGER, --Count - excerptpost INTEGER, --ExcerptPostId - wikipost INTEGER --WikiPostId -); - -CREATE TABLE Votes ( - id INTEGER UNIQUE NOT NULL, --Id - type INTEGER NOT NULL, --VoteTypeId - postid INTEGER NOT NULL, --PostId - creation DATE NOT NULL --CreationDate -); - -CREATE TABLE PostLinks ( - id INTEGER UNIQUE NOT NULL, --Id - creation TIMESTAMP NOT NULL, --CreationDate - postid INTEGER, --PostId - relatedpostid INTEGER, --RelatedPostId - linktypeid INTEGER --LinkTypeId -); - -CREATE TABLE PostHistory ( - id INTEGER UNIQUE NOT NULL, --Id - type INTEGER, --PostHistoryTypeId - postid INTEGER, --PostId - revisionguid TEXT, --RevisionGUID - creation TIMESTAMP NOT NULL, --CreationDate - userid INTEGER, --UserId - userdisplaymame TEXT, --UserDisplayName - text TEXT --Text -); - -CREATE TABLE Badges ( - id INTEGER UNIQUE NOT NULL, --Id - userid INTEGER, --UserId - name TEXT, --Name - date TIMESTAMP, --Date - badgeclass INTEGER, --Class - tagbased TEXT --TagBased -); - diff --git a/.github/actions/terraform-stats/.github/ISSUE_TEMPLATE/bug_report.md b/.github/actions/terraform-stats/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index dd84ea7824f..00000000000 --- a/.github/actions/terraform-stats/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/actions/terraform-stats/.github/ISSUE_TEMPLATE/feature_request.md b/.github/actions/terraform-stats/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index bbcbbe7d615..00000000000 --- a/.github/actions/terraform-stats/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/actions/terraform-stats/.github/dependabot.yml b/.github/actions/terraform-stats/.github/dependabot.yml deleted file mode 100644 index 48dfcf9324f..00000000000 --- a/.github/actions/terraform-stats/.github/dependabot.yml +++ /dev/null @@ -1,10 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" - - package-ecosystem: "terraform" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" diff --git a/.github/actions/terraform-stats/.github/workflows/test_action.yml b/.github/actions/terraform-stats/.github/workflows/test_action.yml deleted file mode 100644 index 8164d226ea1..00000000000 --- a/.github/actions/terraform-stats/.github/workflows/test_action.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Test action - -on: - pull_request: - branches: - - main - -jobs: - test-action: - runs-on: ubuntu-latest - env: - tf-dir: "./terraform" - steps: - - name: Check out repository - uses: actions/checkout@v4 - - name: BEFORE APPLY - Use local action - uses: ./ - id: stats1 - with: - terraform-directory: ${{ env.tf-dir }} - add-args: -target=docker_image.nginx -target=docker_container.nginx - upload-plan: true - upload-retention-days: 0 - plan-file: tf_stats_plan.bin - terraform-version: 1.1.9 - - name: BEFORE APPLY - Get outputs - if: steps.stats1.outputs.change-count > 0 - run: | - echo "terraform-version: ${{ steps.stats1.outputs.terraform-version }}" - echo "drift-count: ${{ steps.stats1.outputs.drift-count }}" - echo "resource-drifts: ${{ steps.stats1.outputs.resource-drifts }}" - echo "change-count: ${{ steps.stats1.outputs.change-count }}" - echo "change-percent: ${{ steps.stats1.outputs.change-percent }}" - echo "resource-changes: ${{ steps.stats1.outputs.resource-changes }}" - - name: Terraform apply - run: terraform -chdir=${{ env.tf-dir }} apply -auto-approve - - name: AFTER APPLY - Use local action - uses: ./ - id: stats2 - with: - terraform-directory: ${{ env.tf-dir }} - - name: AFTER APPLY - Get outputs - run: | - echo "terraform-version: ${{ steps.stats2.outputs.terraform-version }}" - echo "drift-count: ${{ steps.stats2.outputs.drift-count }}" - echo "resource-drifts: ${{ steps.stats2.outputs.resource-drifts }}" - echo "change-count: ${{ steps.stats2.outputs.change-count }}" - echo "change-percent: ${{ steps.stats2.outputs.change-percent }}" - echo "resource-changes: ${{ steps.stats2.outputs.resource-changes }}" diff --git a/.github/actions/terraform-stats/.gitignore b/.github/actions/terraform-stats/.gitignore deleted file mode 100644 index 670bd5d7e6c..00000000000 --- a/.github/actions/terraform-stats/.gitignore +++ /dev/null @@ -1 +0,0 @@ -terraform/* \ No newline at end of file diff --git a/.github/actions/terraform-stats/CODE_OF_CONDUCT.md b/.github/actions/terraform-stats/CODE_OF_CONDUCT.md deleted file mode 100644 index 100fe08efe8..00000000000 --- a/.github/actions/terraform-stats/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,128 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -josiah0601@gmail.com. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. diff --git a/.github/actions/terraform-stats/LICENSE b/.github/actions/terraform-stats/LICENSE deleted file mode 100644 index 1c50494ad20..00000000000 --- a/.github/actions/terraform-stats/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Josiah Siegel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.github/actions/terraform-stats/README.md b/.github/actions/terraform-stats/README.md deleted file mode 100644 index b96eb108ad8..00000000000 --- a/.github/actions/terraform-stats/README.md +++ /dev/null @@ -1,110 +0,0 @@ -# Terraform Stats - -[](https://github.com/CDCgov/terraform-stats/actions/workflows/test_action.yml) - -## Synopsis - -Output the following statistics for the Terraform environment: -1. Terraform version -2. Drift count - * "Drift" refers to changes made outside of Terraform and does not necessary match any resources listed for changes. -3. Resource drifts -4. Change count - * "Change" refers to change actions that Terraform plans to use to move from the prior state to a new state. -5. Change percent - * Percentage of changes to total resources. -6. Resource changes - -## Usage - -```yml -- name: Terraform stats - uses: .github/actions/terraform-stats - id: stats - with: - terraform-directory: ${{ env.tf-dir }} - terraform-version: 1.1.9 -- name: Get outputs - run: | - echo "terraform-version: ${{ steps.stats.outputs.terraform-version }}" - echo "drift-count: ${{ steps.stats.outputs.drift-count }}" - echo "resource-drifts: ${{ steps.stats.outputs.resource-drifts }}" - echo "change-count: ${{ steps.stats.outputs.change-count }}" - echo "change-percent: ${{ steps.stats.outputs.change-percent }}" - echo "resource-changes: ${{ steps.stats.outputs.resource-changes }}" -``` - -## Workflow summary - -### :construction: Terraform Stats :construction: - -* change-count: 2 -* change-percent: 100 -* resource-changes: -```json -[ - { - "address": "docker_container.nginx", - "changes": [ - "create" - ] - }, - { - "address": "docker_image.nginx", - "changes": [ - "create" - ] - } -] -``` - -## Inputs - -```yml -inputs: - terraform-directory: - description: Terraform commands will run in this location. - required: true - default: "./terraform" - include-no-op: - description: "\"no-op\" refers to the before and after Terraform changes are identical as a value will only be known after apply." - required: true - default: false - add-args: - description: Pass additional arguments to Terraform plan. - required: true - default: "" - upload-plan: - description: Upload plan file. true or false - required: true - default: false - upload-retention-days: - description: Number of days to keep uploaded plan. - required: true - default: 7 - plan-file: - description: Name of plan file. - required: true - default: tf__stats__plan.bin - terraform-version: - description: Specify a specific version of Terraform - required: true - default: latest -``` - -## Outputs -```yml -outputs: - terraform-version: - description: 'Terraform version' - drift-count: - description: 'Count of drifts' - resource-drifts: - description: 'JSON output of resource drifts' - change-count: - description: 'Count of changes' - change-percent: - description: 'Percentage of changes to total resources' - resource-changes: - description: 'JSON output of resource changes' -``` diff --git a/.github/actions/terraform-stats/action.yml b/.github/actions/terraform-stats/action.yml deleted file mode 100644 index bfe0b53976e..00000000000 --- a/.github/actions/terraform-stats/action.yml +++ /dev/null @@ -1,108 +0,0 @@ -# action.yml -name: 'Generate Terraform statistics' -description: 'Output Terraform stats for drift and pending changes' -branding: - icon: 'bar-chart' - color: 'purple' -inputs: - terraform-directory: - description: Terraform commands will run in this location. - required: true - default: "./terraform" - include-no-op: - description: "\"no-op\" refers to the before and after Terraform changes are identical as a value will only be known after apply." - required: true - default: false - add-args: - description: Pass additional arguments to Terraform plan. - required: true - default: "" - upload-plan: - description: Upload plan file. true or false - required: true - default: false - upload-retention-days: - description: Number of days to keep uploaded plan. - required: true - default: 7 - plan-file: - description: Name of plan file. - required: true - default: tf__stats__plan.bin - terraform-version: - description: Specify a specific version of Terraform - required: true - default: latest - -outputs: - terraform-version: - description: 'Terraform version' - value: ${{ steps.local-action.outputs.terraform-version }} - drift-count: - description: 'Count of drifts' - value: ${{ steps.local-action.outputs.drift-count }} - resource-drifts: - description: 'JSON output of resource drifts' - value: ${{ steps.local-action.outputs.resource-drifts }} - change-count: - description: 'Count of changes' - value: ${{ steps.local-action.outputs.change-count }} - resource-changes: - description: 'JSON output of resource changes' - value: ${{ steps.local-action.outputs.resource-changes }} - change-percent: - description: 'Percentage of changes to total resources' - value: ${{ steps.local-action.outputs.change-percent }} - -runs: - using: "composite" - steps: - - name: Use specific version of Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: ${{ inputs.terraform-version }} - terraform_wrapper: false - - name: Run Terraform stats - id: local-action - run: | - ${{ github.action_path }}/lib/tf_stats.sh \ - "${{ inputs.terraform-directory }}" \ - ${{ inputs.include-no-op }} \ - "${{ inputs.add-args }}" \ - "${{ inputs.plan-file }}" - shell: bash - - - name: Upload Artifact - if: inputs.upload-plan == 'true' - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ inputs.plan-file }} - path: "${{ inputs.terraform-directory }}/${{ inputs.plan-file }}" - retention-days: ${{ inputs.upload-retention-days }} - - - name: Create summary - if: | - steps.local-action.outputs.change-count > 0 || - steps.local-action.outputs.drift-count > 0 - run: | - echo "### :construction: Terraform Stats :construction:" >> $GITHUB_STEP_SUMMARY - if [[ ${{ steps.local-action.outputs.change-count }} > 0 ]]; then - resource_changes=$(echo "${{ steps.local-action.outputs.resource-changes }}" | jq .) - echo " - * change-count: ${{ steps.local-action.outputs.change-count }} - * change-percent: ${{ steps.local-action.outputs.change-percent }} - * resource-changes: - \`\`\`json - $resource_changes - \`\`\`" >> $GITHUB_STEP_SUMMARY - fi - if [[ ${{ steps.local-action.outputs.drift-count }} > 0 ]]; then - resource_drifts=$(echo "${{ steps.local-action.outputs.resource-drifts }}" | jq .) - echo " - * drift-count: ${{ steps.local-action.outputs.drift-count }} - * resource-drift: - \`\`\`json - $resource_drifts - \`\`\`" >> $GITHUB_STEP_SUMMARY - fi - shell: bash diff --git a/.github/actions/terraform-stats/lib/tf_stats.sh b/.github/actions/terraform-stats/lib/tf_stats.sh deleted file mode 100644 index 74eb7431ed9..00000000000 --- a/.github/actions/terraform-stats/lib/tf_stats.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -tf_dir=$1 -#For ["no-op"], the before and -#after values are identical. The "after" value will be incomplete if there -#are values within it that won't be known until after apply. -include_no_op=$2 -add_args=$3 -plan_file=$4 - -# Define a function to run terraform plan with common arguments -tf_plan() { - terraform -chdir=$tf_dir plan $add_args -input=false -no-color -lock-timeout=120s -out=$plan_file "$@" -} - -# Try to run terraform plan and init if needed -if ! tf_plan &>/dev/null; then - terraform -chdir=$tf_dir init >/dev/null || exit 1 - tf_plan >/dev/null || exit 1 -fi - -# Get the plan output in text and json formats -PLAN_TXT=$( terraform -chdir=$tf_dir show -no-color $plan_file ) -PLAN_JSON=$( terraform -chdir=$tf_dir show -no-color -json $plan_file ) - -# Define a function to parse the plan json with jq -parse_plan_json() { - echo $PLAN_JSON | jq "$@" -} - -# Define a function to make output friendly -make_output_friendly() { - local output=$1 - output="${output//'%'/'%25'}" - output="${output//$'\n'/'%0A'}" - output="${output//$'\r'/'%0D'}" - output="${output//'"'/'\"'}" - output="${output//'\\"'/'\\\"'}" - echo $output -} - -# Define a function to write the output to the github output file -write_output() { - local key=$1 - local value=$2 - echo "$key=$(make_output_friendly $value)" >> $GITHUB_OUTPUT -} - -# Get the terraform version from the plan json -VERSION=$(parse_plan_json .terraform_version) - -# Get the resource drift from the plan json -DRIFT=$(parse_plan_json .resource_drift) -DRIFT_COUNT=$(echo $DRIFT | jq length) -DRIFTED_RESOURCES=$(echo $DRIFT | jq -c '[.[] | {address: .address, changes: .change.actions}]') - -# Get the resource changes from the plan json -CHANGES=$(parse_plan_json .resource_changes) -if [[ $include_no_op = true ]]; then - CHANGES_FILTERED=$CHANGES -else - CHANGES_FILTERED=$(echo $CHANGES | jq -c '[.[] | {address: .address, changes: .change.actions} | select( .changes[] != "no-op")]') -fi -CHANGE_COUNT=$(echo $CHANGES_FILTERED | jq length) - -# Get the total resources and percent changed from the plan json -TOTAL_RESOURCES=$(parse_plan_json .planned_values.root_module) -TOTAL_ROOT=$(echo $TOTAL_RESOURCES | jq -c .resources | jq length) -TOTAL_CHILD=$(echo $TOTAL_RESOURCES | jq -c .child_modules | jq -c '[.[]?.resources | length] | add') -TOTAL_COUNT=$(( TOTAL_ROOT + TOTAL_CHILD )) -CHANGE_PERC=$(echo "scale=0 ; $CHANGE_COUNT / $TOTAL_COUNT * 100" | bc) - -# Write the output to the github output file -write_output "terraform-version" "$VERSION" -write_output "change-percent" "$CHANGE_PERC" -write_output "drift-count" "$DRIFT_COUNT" -write_output "change-count" "$CHANGE_COUNT" -write_output "resource-drifts" "$DRIFTED_RESOURCES" -write_output "resource-changes" "$CHANGES_FILTERED" diff --git a/.github/actions/terraform-templates/.devcontainer/Dockerfile b/.github/actions/terraform-templates/.devcontainer/Dockerfile deleted file mode 100644 index b1d4e8b77cb..00000000000 --- a/.github/actions/terraform-templates/.devcontainer/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM mcr.microsoft.com/azure-dev-cli-apps:latest - -#RUN sudo echo "nameserver 8.8.8.8" >> /etc/resolv.conf -#CMD [ "sleep", "infinity" ] \ No newline at end of file diff --git a/.github/actions/terraform-templates/.devcontainer/devcontainer.json b/.github/actions/terraform-templates/.devcontainer/devcontainer.json deleted file mode 100644 index 7ef941d35cf..00000000000 --- a/.github/actions/terraform-templates/.devcontainer/devcontainer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "azure-dev-cli-apps", - //"build": { "dockerfile": "Dockerfile" }, - //"image": "mcr.microsoft.com/azure-dev-cli-apps:latest", - "dockerComposeFile": "docker-compose.yml", - "service": "app", - "workspaceFolder": "/workspace", - "remoteEnv": { - "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" - }, - "customizations": { - "vscode": { - "extensions": [ - "ms-vscode-remote.remote-containers", - "github.codespaces", - "hashicorp.terraform", - "stateful.runme" - ] - } - }, - "otherPortsAttributes": { - "onAutoForward": "silent" - } -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/.devcontainer/docker-compose.yml b/.github/actions/terraform-templates/.devcontainer/docker-compose.yml deleted file mode 100644 index 83095a62cd3..00000000000 --- a/.github/actions/terraform-templates/.devcontainer/docker-compose.yml +++ /dev/null @@ -1,19 +0,0 @@ -version: "3.3" -services: - app: - build: - context: . - dockerfile: Dockerfile - volumes: - # Forwards the local Docker socket to the container. - - /var/run/docker.sock:/var/run/docker-host.sock - # Update this to wherever you want VS Code to mount the folder of your project - - ..:/workspace:cached - network_mode: bridge - dns: - - 1.1.1.1 - - 1.0.0.1 - - # Overrides default command so things don't shut down after the process ends. - #entrypoint: /usr/local/share/docker-init.sh - command: sleep infinity \ No newline at end of file diff --git a/.github/actions/terraform-templates/.devops/adf-build-and-release.yml b/.github/actions/terraform-templates/.devops/adf-build-and-release.yml deleted file mode 100644 index c67967f32bc..00000000000 --- a/.github/actions/terraform-templates/.devops/adf-build-and-release.yml +++ /dev/null @@ -1,83 +0,0 @@ -trigger: - - main # Collaboration branch - #- develop # Development branch - #- feature/* # Feature branches - -pool: - name: 'Default' - -variables: - #- group: azure-service-connections # Use variable groups for sensitive values - - name: ServiceConnection - value: 'demo-rg' - - name: keyVaultName - value: 'kv-42e-demo-primary' - - name: sourceDataFactoryName - value: 'df-42e-demo-primary' - - name: deployDataFactoryName - value: 'df-42e-demo-secondary' - - name: deploymentResourceGroupName - value: 'demo' - -stages: -- stage: Build - displayName: Build stage - jobs: - - job: Build - displayName: Build job - pool: - name: 'Default' - steps: - - task: UseNode@1 - inputs: - version: '18.x' - displayName: 'Install Node.js' - - - task: Npm@1 - inputs: - command: 'install' - workingDir: '$(Build.Repository.LocalPath)/.devops' - verbose: true - displayName: 'Install npm package' - - - task: Npm@1 - inputs: - command: 'custom' - workingDir: '$(Build.Repository.LocalPath)/.devops' - customCommand: 'run build export $(Build.Repository.LocalPath)/adf/$(SourceDataFactoryName) /subscriptions/ff5f335c-3727-493e-9dc0-0aad424b456d/resourceGroups/demo/providers/Microsoft.DataFactory/factories/$(SourceDataFactoryName) "ArmTemplate"' - displayName: 'Validate and Generate ARM template' - - - publish: '$(Build.Repository.LocalPath)/.devops/ArmTemplate/' - displayName: 'Publish ARM templates' - artifact: drop - -- stage: Deploy - displayName: Deploy to Environment - jobs: - - deployment: DeployDev - displayName: Deploy to Development - condition: succeeded() - #and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')) # Conditional execution for development branch - environment: Development - pool: - name: 'Default' - strategy: - runOnce: - deploy: - steps: - - template: adf-release-tasks.yml - parameters: - environment: 'Development' - azureSubscription: '$(ServiceConnection)' - keyVaultName: $(KeyVaultName) - sourceDataFactoryName: $(sourceDataFactoryName) - deployDataFactoryName: $(deployDataFactoryName) - deploymentResourceGroupName: $(deploymentResourceGroupName) - -# - deployment: DeployProd -# displayName: Deploy to Production -# condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) # Conditional execution for main branch -# environment: Production -# pool: -# name: 'Default' - diff --git a/.github/actions/terraform-templates/.devops/adf-release-tasks.yml b/.github/actions/terraform-templates/.devops/adf-release-tasks.yml deleted file mode 100644 index c07ba987704..00000000000 --- a/.github/actions/terraform-templates/.devops/adf-release-tasks.yml +++ /dev/null @@ -1,57 +0,0 @@ -parameters: - environment: '' - azureSubscription: '' - keyVaultName: '' - sourceDataFactoryName: '' - deployDataFactoryName: '' - deploymentResourceGroupName: '' - - -steps: -- task: AzureKeyVault@1 - inputs: - azureSubscription: '${{ parameters.azureSubscription }}' - KeyVaultName: '${{ parameters.KeyVaultName }}' - SecretsFilter: '*' - RunAsPreJob: true - -- download: current - artifact: drop - -- task: AzurePowerShell@5 - displayName: Stop Triggers - inputs: - azureSubscription: '${{ parameters.azureSubscription }}' - ScriptType: 'InlineScript' - Inline: - $triggersADF = Get-AzDataFactoryV2Trigger -DataFactoryName - "${{ parameters.DeployDataFactoryName }}" -ResourceGroupName "${{ parameters.DeploymentResourceGroupName }}"; - $triggersADF | ForEach-Object { Stop-AzDataFactoryV2Trigger - –ResourceGroupName "${{ parameters.DeploymentResourceGroupName }}" -DataFactoryName - "${{ parameters.DeployDataFactoryName }}" -Name $_.name -Force } - azurePowerShellVersion: 'LatestVersion' -- task: AzurePowerShell@5 - displayName: Deploy ADF - inputs: - azureSubscription: '${{ parameters.azureSubscription }}' - ScriptType: 'InlineScript' - Inline: - 'New-AzResourceGroupDeployment - -ResourceGroupName "${{ parameters.DeploymentResourceGroupName }}" - -TemplateParameterFile "$(Pipeline.Workspace)/drop/ARMTemplateParametersForFactory.json" - -TemplateFile "$(Pipeline.Workspace)/drop/ARMTemplateForFactory.json" - -factoryName "${{ parameters.DeployDataFactoryName }}" - #<parameter-overridden> : <value-to-be-overridden> there are parameters in arm template and overriden by key vault secrets - #<parameter-overridden> : <value-to-be-overridden> - -Mode "Incremental"' - azurePowerShellVersion: 'LatestVersion' -- task: AzurePowerShell@5 - displayName: Restart Triggers - inputs: - azureSubscription: '${{ parameters.azureSubscription }}' - ScriptType: 'InlineScript' - Inline: - $triggersADF = Get-AzDataFactoryV2Trigger -DataFactoryName "${{ parameters.DeployDataFactoryName }}" -ResourceGroupName "${{ parameters.DeploymentResourceGroupName }}"; - $triggersADF | ForEach-Object { Start-AzDataFactoryV2Trigger -ResourceGroupName "${{ parameters.DeploymentResourceGroupName }}" -DataFactoryName "${{ parameters.DeployDataFactoryName }}" -Name $_.name -Force } - azurePowerShellVersion: 'LatestVersion' - \ No newline at end of file diff --git a/.github/actions/terraform-templates/.devops/db-restore.yml b/.github/actions/terraform-templates/.devops/db-restore.yml deleted file mode 100644 index b0e5066a1ca..00000000000 --- a/.github/actions/terraform-templates/.devops/db-restore.yml +++ /dev/null @@ -1,73 +0,0 @@ -trigger: none - -pool: - name: Default - -variables: - - group: demo-db-credentials - -stages: - -- stage: Backup - displayName: Backup Database - condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/pull/'))) - jobs: - - deployment: BackupDatabase - displayName: Backup Database - environment: demo - pool: - name: Default - strategy: - runOnce: - deploy: - steps: - - task: SqlAzureDacpacDeployment@1 - inputs: - azureSubscription: 'demo-rg' - AuthenticationType: 'server' - ServerName: 'sqlserver-$(Uid)-demo-primary.database.windows.net' - DatabaseName: 'trialdb' - SqlUsername: '$(SqlUsername)' - SqlPassword: '$(SqlPassword)' - deployType: 'DacpacTask' - DeploymentAction: 'Export' - IpDetectionMethod: 'AutoDetect' - BacpacFile: '$(Build.ArtifactStagingDirectory)\trialdb.bacpac' - - - task: PublishBuildArtifacts@1 - inputs: - PathtoPublish: 'GeneratedOutputFiles\trialdb.bacpac' - ArtifactName: 'drop' - -- stage: Restore - dependsOn: Backup - displayName: Restore Database - condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/pull/'))) - jobs: - - deployment: RestoreDatabase - displayName: Restore Database - environment: demo - pool: - name: Default - strategy: - runOnce: - deploy: - steps: - - task: DownloadBuildArtifacts@1 - inputs: - buildType: 'current' - artifactName: 'drop' - downloadPath: '$(System.ArtifactsDirectory)' - - - task: SqlAzureDacpacDeployment@1 - inputs: - azureSubscription: 'demo-rg' - AuthenticationType: 'server' - ServerName: 'sqlserver-$(Uid)-demo-secondary.database.windows.net' - DatabaseName: 'trialdb' - SqlUsername: '$(SqlUsername)' - SqlPassword: '$(SqlPassword)' - deployType: 'DacpacTask' - DeploymentAction: 'Import' - IpDetectionMethod: 'AutoDetect' - BacpacFile: '$(Build.ArtifactStagingDirectory)\drop\trialdb.bacpac' \ No newline at end of file diff --git a/.github/actions/terraform-templates/.github/dependabot.yml b/.github/actions/terraform-templates/.github/dependabot.yml deleted file mode 100644 index f33a02cd16e..00000000000 --- a/.github/actions/terraform-templates/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for more information: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates -# https://containers.dev/guide/dependabot - -version: 2 -updates: - - package-ecosystem: "devcontainers" - directory: "/" - schedule: - interval: weekly diff --git a/.github/actions/terraform-templates/.gitignore b/.github/actions/terraform-templates/.gitignore deleted file mode 100644 index 374ca3b4f58..00000000000 --- a/.github/actions/terraform-templates/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# Local .terraform directories -**/.terraform/* - -# .tfstate files -*.tfstate -*.tfstate.* - -# Crash log files -crash.log -crash.*.log - -# Exclude all .tfvars files, which are likely to contain sensitive data, such as -# password, private keys, and other secrets. These should not be part of version -# control as they are data points which are potentially sensitive and subject -# to change depending on the environment. -*secrets.auto.tfvars -*secrets.auto.tfvars.json - -# Ignore override files as they are usually used to override resources locally and so -# are not checked in -override.tf -override.tf.json -*_override.tf -*_override.tf.json - -# Include override files you do wish to add to version control using negated pattern -# !example_override.tf - -# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan -# example: *tfplan* - -# Ignore CLI configuration files -.terraformrc -terraform.rc - -**/.terraform.lock.hcl diff --git a/.github/actions/terraform-templates/.scripts/data/psql_table_massive.sh b/.github/actions/terraform-templates/.scripts/data/psql_table_massive.sh deleted file mode 100755 index 30ed034c070..00000000000 --- a/.github/actions/terraform-templates/.scripts/data/psql_table_massive.sh +++ /dev/null @@ -1,35 +0,0 @@ -sudo apt update -sudo apt install -y p7zip-full - -cd /mnt/storage/ -echo "restart script if slow download speed (<5Mbps):" -curl -L https://archive.org/download/stackexchange/stackoverflow.com-PostHistory.7z --output PostHistory.7z -7z x PostHistory.7z - -python stackoverflow_in_pg/python_src/so2pg-posthistory.py PostHistory.xml > posthistory.sql - -echo -e "Create table and insert data:\n==================" -cat << EOF -CREATE TABLE PostHistory ( - id INTEGER UNIQUE NOT NULL, - type INTEGER, - postid INTEGER, - revisionguid TEXT, - creation TIMESTAMP NOT NULL, - userid INTEGER, - userdisplaymame TEXT, - text TEXT -); - -export PGPASSWORD="<password>" -host="<cluster name>.postgres.cosmos.azure.com" -user="citus" -db="citus" -schema="public" -psql \ --h $host \ --d $db \ --U $user \ --f /mnt/storage/posthistory.sql -EOF -echo -e "==================" diff --git a/.github/actions/terraform-templates/.scripts/data/psql_table_tiny.sh b/.github/actions/terraform-templates/.scripts/data/psql_table_tiny.sh deleted file mode 100755 index efafbc60482..00000000000 --- a/.github/actions/terraform-templates/.scripts/data/psql_table_tiny.sh +++ /dev/null @@ -1,32 +0,0 @@ -sudo apt update -sudo apt install -y p7zip-full - -cd /mnt/storage/ -echo "restart script if slow download speed (<5Mbps):" -curl -L https://archive.org/download/stackexchange/stackoverflow.com-Tags.7z --output Tags.7z -7z x Tags.7z - -python stackoverflow_in_pg/python_src/so2pg-tags.py Tags.xml > tags.sql - -echo -e "Create table and insert data:\n==================" -cat << EOF -CREATE TABLE Tags ( - id INTEGER UNIQUE NOT NULL, - name TEXT UNIQUE NOT NULL, - count INTEGER, - excerptpost INTEGER, - wikipost INTEGER -); - -export PGPASSWORD="<password>" -host="<cluster name>.postgres.cosmos.azure.com" -user="citus" -db="citus" -schema="public" -psql \ --h $host \ --d $db \ --U $user \ --f /mnt/storage/tags.sql -EOF -echo -e "==================" diff --git a/.github/actions/terraform-templates/.scripts/destroy_template.sh b/.github/actions/terraform-templates/.scripts/destroy_template.sh deleted file mode 100755 index f476492bf8b..00000000000 --- a/.github/actions/terraform-templates/.scripts/destroy_template.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -set -e - -# Function to check if a directory exists -check_directory() { - if [ ! -d "$1" ]; then - echo "Error: The specified template directory '$1' does not exist." - exit 1 - fi -} - -# Function to check if a file exists -check_file() { - if [ ! -f "$1" ]; then - echo "Error: The file '$1' does not exist." - exit 1 - fi -} - -# Function to check if a value is empty -check_empty() { - if [ -z "$1" ]; then - echo "Error: No value provided for $2. Please enter a valid value." - exit 1 - fi -} - -# Prompt for the path to the environment directory -read -p "Enter the template environment (01): " env_path - -# Check if env_path is empty -check_empty "$env_path" "env_path" - -# Set the environment directory path -env_dir="azure/env/$env_path" - -# Check if the environment directory exists -check_directory "$env_dir" - -# Set the JSON file path -json_file="$env_dir/_override.tf.json" - -# Check if the JSON file exists -check_file "$json_file" - -# Read values from the JSON file -storage_account_name=$(grep -oP '(?<="storage_account_name": ")[^"]*' "$json_file") -resource_group_name=$(grep -oP '(?<="resource_group_name": ")[^"]*' "$json_file") -container_name=$(grep -oP '(?<="container_name": ")[^"]*' "$json_file") -state_file_key=$(grep -oP '(?<="key": ")[^"]*' "$json_file") - -# Check if the storage account exists -if ! az storage account show --name "$storage_account_name" --resource-group "$resource_group_name" >/dev/null 2>&1; then - echo "Storage account '$storage_account_name' does not exist. Skipping all actions." - exit 0 -fi - -# Check if the container exists -if ! az storage container exists --account-name "$storage_account_name" --name "$container_name" --query "exists" -o tsv --only-show-errors | grep -q "^true$"; then - echo "Container '$container_name' does not exist in storage account '$storage_account_name'. Skipping Terraform destroy." -else - # Check if the state file exists in the container - if az storage blob exists --account-name "$storage_account_name" --container-name "$container_name" --name "$state_file_key" --query "exists" -o tsv --only-show-errors | grep -q "^true$"; then - # Run terraform destroy if the state file exists - terraform -chdir="$env_dir" destroy -auto-approve - else - echo "Skipping terraform destroy as the state file does not exist in the container." - fi -fi - -# Construct the az storage account delete command -delete_command="az storage account delete --name $storage_account_name --resource-group $resource_group_name --yes" - -# Print the command -echo "Running command: $delete_command" - -# Run the command -eval "$delete_command" diff --git a/.github/actions/terraform-templates/.scripts/provision_template.sh b/.github/actions/terraform-templates/.scripts/provision_template.sh deleted file mode 100755 index 066254db1aa..00000000000 --- a/.github/actions/terraform-templates/.scripts/provision_template.sh +++ /dev/null @@ -1,203 +0,0 @@ -#!/bin/bash - -set -e - -# Configuration -DEFAULT_TEMPLATE_ENVIRONMENT="01" -DEFAULT_RESOURCE_GROUP_NAME="myRG" -DEFAULT_ENVIRONMENT_NICKNAME="demo" -DEFAULT_LOCATION="eastus" -DEFAULT_OWNER_EMAIL="owner@example.com" -CONTAINER_NAME="terraformstate" -STATE_FILE_KEY="terraform.tfstate" -USE_AZUREAD_AUTH=false - -parse_arguments() { - while [[ $# -gt 0 ]]; do - case "$1" in - --template-environment|-t) - template_environment="$2" - shift 2 - ;; - --resource-group|-r) - resource_group_name="$2" - shift 2 - ;; - --environment-nickname|-e) - environment_nickname="$2" - shift 2 - ;; - --location|-l) - location="$2" - shift 2 - ;; - --owner-email|-o) - owner_email="$2" - shift 2 - ;; - *) - echo "Unknown argument: $1" - exit 1 - ;; - esac - done -} - -# Parse command-line arguments -parse_arguments "$@" - -# Function to prompt for user input with a default value -prompt_with_default() { - local prompt=$1 - local default_value=$2 - read -p "$prompt [$default_value]: " value - echo "${value:-$default_value}" -} - -# Function to check if a directory exists -check_directory_exists() { - local directory=$1 - if [ ! -d "$directory" ]; then - echo "Error: The specified template directory '$directory' does not exist." - exit 1 - fi -} - -# Function to check if a resource group exists -check_resource_group_exists() { - local resource_group=$1 - if ! az group show --name "$resource_group" >/dev/null 2>&1; then - echo "Error: The specified resource group '$resource_group' does not exist." - echo "Please create the resource group or provide an existing one." - exit 1 - fi -} - -# Function to generate the _override.tf.json file -generate_override_file() { - local json_file="azure/env/$template_environment/_override.tf.json" - cat > "$json_file" <<EOL -{ - "terraform": { - "backend": { - "azurerm": { - "resource_group_name": "$resource_group_name", - "storage_account_name": "$storage_account_name", - "container_name": "$CONTAINER_NAME", - "key": "$STATE_FILE_KEY", - "use_azuread_auth": $USE_AZUREAD_AUTH - } - } - }, - "variable": { - "environment": { - "default": "$environment_nickname" - }, - "uid": { - "default": "$random_suffix" - }, - "location": { - "default": "$location" - }, - "owner_email": { - "default": "$owner_email" - }, - "resource_group": { - "default": "$resource_group_name" - }, - "owner_object_id": { - "default": "$owner_object_id" - } - } -} -EOL - - if [ ! -f "$json_file" ]; then - echo "Error: The JSON file '$json_file' was not created!" - exit 1 - fi -} - -# Function to create the storage account -create_storage_account() { - if ! az storage account create --allow-blob-public-access false --name "$storage_account_name" --resource-group "$resource_group_name" --location "$location" --sku 'Standard_LRS' -o 'none'; then - echo -e "Error: Failed to create the storage account '$storage_account_name'.\n" - echo "Tips to resolve:" - echo "- Re-authenticate with 'az login'." - echo "- Ensure that the resource group '$resource_group_name' exists and you have the necessary permissions to create resources in it." - echo "- Verify that the storage account name '$storage_account_name' is unique and meets the naming requirements." - echo "- Check if the location '$location' is valid and supported for storage account creation." - echo "- Make sure you have the required Azure CLI version and are authenticated with the correct Azure subscription." - exit 1 - fi -} - -# Function to create the storage container -create_storage_container() { - if ! az storage container create --account-name "$storage_account_name" -n "$CONTAINER_NAME" --only-show-errors -o 'none'; then - echo -e "Error: Failed to create the storage container '$CONTAINER_NAME' in the storage account '$storage_account_name'.\n" - echo "Tips to resolve:" - echo "- Re-authenticate with 'az login'." - echo "- Ensure that the storage account '$storage_account_name' exists and you have the necessary permissions to create containers in it." - echo "- Verify that the container name '$CONTAINER_NAME' is valid and meets the naming requirements." - echo "- Check if you have the required Azure CLI version and are authenticated with the correct Azure subscription." - echo "- If error 'SubscriptionNotFound', manually create a single resource (other than a Resource Group) if non-exist." - exit 1 - fi -} - -# Main script logic -# Check if owner_object_id is populated, fail the script if it is empty -if [ -z "$owner_object_id" ]; then - echo "Error: owner_object_id is empty. Please make sure it is populated before running the script." - exit 1 -fi -echo -e "Owner object ID: $owner_object_id\n" - -# Prompt for user inputs - -# Check if the template environment directory exists -if [ -z "$template_environment" ]; then - template_environment=$(prompt_with_default "Enter the template environment" "$DEFAULT_TEMPLATE_ENVIRONMENT") -fi -check_directory_exists "azure/env/$template_environment" -export template_path="azure/env/$template_environment" -echo -e "Using template path: $template_path\n" - -# Check if the resource group exists -if [ -z "$resource_group_name" ]; then - resource_group_name=$(prompt_with_default "Enter the resource group name" "$DEFAULT_RESOURCE_GROUP_NAME") -fi -check_resource_group_exists "$resource_group_name" - -if [ -z "$environment_nickname" ]; then - environment_nickname=$(prompt_with_default "Enter the environment nickname" "$DEFAULT_ENVIRONMENT_NICKNAME") -fi -environment_nickname=$(echo "$environment_nickname" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]') - -if [ -z "$location" ]; then - location=$(prompt_with_default "Enter the location" "$DEFAULT_LOCATION") -fi - -if [ -z "$owner_email" ]; then - owner_email=$(prompt_with_default "Enter the owner email" "$DEFAULT_OWNER_EMAIL") -fi - -# Generate a random alphanumeric string of length 3 -random_suffix=$(echo $(date +%s%N) | sha256sum | tr '[:upper:]' '[:lower:]' | head -c 3) - -# Set the storage account name -storage_account_name="sa${environment_nickname}${random_suffix}" -echo -e "Using storage account name: $storage_account_name\n" - -# Generate the _override.tf.json file -generate_override_file - -# Create the storage account -create_storage_account - -# Wait for the storage account to be fully provisioned -sleep 5 - -# Create the storage container -create_storage_container diff --git a/.github/actions/terraform-templates/.scripts/utils/psql_install_16.sh b/.github/actions/terraform-templates/.scripts/utils/psql_install_16.sh deleted file mode 100755 index fcea28a1e14..00000000000 --- a/.github/actions/terraform-templates/.scripts/utils/psql_install_16.sh +++ /dev/null @@ -1,8 +0,0 @@ -apt update -apt install -y sudo -sudo apt install -y curl ca-certificates -sudo install -d '/usr/share/postgresql-common/pgdg' -sudo curl -o '/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc' --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc -sudo sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' -sudo apt update -sudo apt install -y postgresql-16 \ No newline at end of file diff --git a/.github/actions/terraform-templates/.scripts/utils/ssh_key_auth.sh b/.github/actions/terraform-templates/.scripts/utils/ssh_key_auth.sh deleted file mode 100755 index ea1b1237b9e..00000000000 --- a/.github/actions/terraform-templates/.scripts/utils/ssh_key_auth.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -set -e - -DEFAULT_KEY_PATH="$HOME" - -prompt() { - local prompt=$1 - read -p "$prompt: " value - echo "${value}" -} - -prompt_with_default() { - local prompt=$1 - local default_value=$2 - read -p "$prompt [$default_value]: " value - echo "${value:-$default_value}" -} - -remote_user=$(prompt "Enter remote username") -remote_hostname=$(prompt "Enter remote hostname") -remote_port=$(prompt "Enter remote port") - -key_path=$(prompt_with_default "Enter the key path" "$DEFAULT_KEY_PATH") - -echo "y" | ssh-keygen -t ed25519 -b 4096 -f $key_path/.ssh/id_ed25519 -N "" - -USER_AT_HOST="${remote_user}@${remote_hostname}" -PUBKEYPATH="$key_path/.ssh/id_ed25519.pub" - -pubKey=$(cat "$PUBKEYPATH") -ssh -p $remote_port "$USER_AT_HOST" "mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo '$pubKey' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys" diff --git a/.github/actions/terraform-templates/.scripts/utils/whats_running.sh b/.github/actions/terraform-templates/.scripts/utils/whats_running.sh deleted file mode 100755 index 2ac05d66407..00000000000 --- a/.github/actions/terraform-templates/.scripts/utils/whats_running.sh +++ /dev/null @@ -1 +0,0 @@ -ps aux diff --git a/.github/actions/terraform-templates/README.md b/.github/actions/terraform-templates/README.md deleted file mode 100644 index ffeefc512d5..00000000000 --- a/.github/actions/terraform-templates/README.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -runme: - id: 01HVWMC7W5WGR5QF3H8KNK4JP4 - version: v3 ---- - -# Terraform Templates - -## Introduction - -This repository contains Terraform templates for deploying various resources in Azure. It is designed to help beginners get started with provisioning infrastructure using Terraform. - -## Prerequisites - -- Azure CLI installed -- Azure account with sufficient permissions -- Terraform installed - -## Getting Started - -> I recommend running in Codespaces or Dev Container in VS Code: -> -> [](https://codespaces.new/JosiahSiegel/terraform-templates) - -### 1. Login to Azure - -```sh {"id":"01HVWMC7W5WGR5QF3H80KE8XAZ"} -az login -``` - -or - -```sh {"id":"01HVWPXVEB7ZFQETZD308G7HXP"} -az login --use-device-code -``` - -### 2. Export Owner Object ID - -Export an owner object ID to a variable by running the following command: - -```sh {"id":"01HVWMC7W5WGR5QF3H83QR73CK"} -read -p "Enter AD user display name [Smith, Joe]: " display_name -owner_object_id=$(az ad user list --filter "displayname eq '$display_name'" --query '[0].id' -o tsv) -export owner_object_id=$owner_object_id -echo "Object Id: $owner_object_id" -``` - -### 3. Create Terraform Backend - -Create the Terraform state backend file and storage account for a template by running the following script: - -```sh {"id":"01HVWMC7W5WGR5QF3H85WFTTP6"} -source ./.scripts/provision_template.sh -``` - -> Optional: set `template_path=azure/env/<template>` if backend already exists: - -```sh {"id":"01HW67SE89X56V69ASEFGZB6AX"} -read -p "Enter the template environment: " template -export template_path="azure/env/${template:-_}" -if [ ! -d "$template_path" ]; then -echo "Error: The specified template directory '$template_path' does not exist." -else -echo "Set template environment '$template_path'!" -fi -``` - -### 4. Initialize Terraform - -Initialize Terraform using the desired template path (`azure/env/<template>`) by running: - -```sh {"id":"01HVWMC7W5WGR5QF3H89NDQK07"} -terraform -chdir=$template_path init -reconfigure -``` - -Replace 01 with the appropriate template directory. - -### 5. Plan Terraform Deployment - -Generate an execution plan for the Terraform deployment by running: - -```sh {"id":"01HVWMC7W5WGR5QF3H8BHW8T14"} -terraform -chdir=$template_path plan -``` - -### 6. Apply Terraform Deployment - -```sh {"id":"01HVWMC7W5WGR5QF3H8DQ9WBBE"} -terraform -chdir=$template_path apply -``` - -If a login failure occurs with the error "Login failed for user ''", try running `az logout` and then `az login` to resolve the issue. - -### 7. Destroy the Terraform deployment and terraform storage account - -```sh {"id":"01HVWMC7W5WGR5QF3H8GNPS01Z"} -./.scripts/destroy_template.sh -``` - -## Available Templates - -### Template 01 - -|Azure Resource|Purpose| -|---|---| -|API Management|Securely share and manage REST endpoints| -|Logic App|Make database data available via REST endpoint| -|Data Factory|Ingest APIs and store data in a database and storage account| -|Key Vault|Manage secrets| -|Storage Account|Store results from ingested APIs| -|Azure SQL DB|Store data from ingested APIs and expose via Logic App and API Management| - -### Template 02 - -|Azure Resource|Purpose| -|---|---| -|Key Vault|Manage secrets| -|Azure SQL DB|Store data| - -### Template 03 - -|Azure Resource|Purpose| -|---|---| -|CosmosDB for PostgreSQL|| -|VNet|Private network| -|Private Endpoint|CosmosDB traffic over vnet| -|Key Vault|CosmosDB password| -|Container Instance|Run DB backup/restore to file share| -|Storage Account|File share storage for container instance| - -### Template 04 - -|Azure Resource|Purpose| -|---|---| -pending...|| - -### Template 05 - -|Azure Resource|Purpose| -|---|---| -|Container Instance|Linux container with file share mount| -|Storage Account|File share storage for container instance| - -### Template 06 - -|Azure Resource|Purpose| -|---|---| -|Container Instance|Windows container| - -## Troubleshooting - -1. If login failure during plan or apply, try running `az logout` then `az login` -2. If you encounter any issues, please check the Terraform state backend and `_override.tf.json` files and storage account created in step 3 -3. If error "SubscriptionNotFound", manually create a single resource (other than a Resource Group) if non-exist - -## Contributing - -Feel free to submit pull requests or open issues if you have any suggestions or improvements for these Terraform templates. - -### Bonus - - * Run scripts and enter terminal for container instance - -```sh {"id":"01HWZY6RPNNYHM0MN1F6MF9QZB"} -az container exec --name cinst-dev --resource-group demo --container-name cinst-dev --exec-command "/bin/bash -c /app/repo1/terraform-templates/.scripts/utils/psql_install_16.sh" -``` - -```sh {"id":"01HWY6JGNS1CTNGATQE5KMKNCR"} -az container exec --name cinst-dev --resource-group demo --container-name cinst-dev --exec-command "/bin/bash" -``` - -* Add existing public SSH key to container instance - -```sh -ssh-copy-id -p 2222 -i ~/.ssh/id_ed25519.pub app@cinst-dev.eastus.azurecontainer.io -``` diff --git a/.github/actions/terraform-templates/azure/env/01/config.tf b/.github/actions/terraform-templates/azure/env/01/config.tf deleted file mode 100644 index adf3cd66be2..00000000000 --- a/.github/actions/terraform-templates/azure/env/01/config.tf +++ /dev/null @@ -1,35 +0,0 @@ -terraform { - required_version = ">= 1.7.5" - required_providers { - azurerm = { - source = "hashicorp/azurerm" - version = "3.99.0" - } - azuread = { - source = "hashicorp/azuread" - version = "2.48.0" - } - mssql = { - source = "betr-io/mssql" - version = "~> 0.3" - } - azapi = { - source = "Azure/azapi" - version = "1.12.1" - } - } -} - -provider "azurerm" { - # This is only required when the User, Service Principal, or Identity running Terraform lacks the permissions to register Azure Resource Providers. - skip_provider_registration = false - features { - template_deployment { - delete_nested_items_during_deletion = true - } - key_vault { - recover_soft_deleted_key_vaults = true - purge_soft_delete_on_destroy = true - } - } -} diff --git a/.github/actions/terraform-templates/azure/env/01/data.tf b/.github/actions/terraform-templates/azure/env/01/data.tf deleted file mode 100644 index b62ec85569e..00000000000 --- a/.github/actions/terraform-templates/azure/env/01/data.tf +++ /dev/null @@ -1,10 +0,0 @@ -data "azuread_users" "owner" { - object_ids = [var.owner_object_id] -} - -data "azurerm_resource_group" "default" { - name = var.resource_group -} - -data "azurerm_subscription" "default" { -} diff --git a/.github/actions/terraform-templates/azure/env/01/main.tf b/.github/actions/terraform-templates/azure/env/01/main.tf deleted file mode 100644 index f6fa83888b2..00000000000 --- a/.github/actions/terraform-templates/azure/env/01/main.tf +++ /dev/null @@ -1,62 +0,0 @@ -module "init" { - source = "../../modules/init/v1" - common = local.common - dev_roles = local.dev_roles -} - -module "logic_app" { - source = "../../modules/logic_app" - common = local.common - storage_account = module.storage_account.meta - workflow_file = "workflow.json" - sql_server_fqdn = module.azure_db.fqdn - db_name = module.azure_db.db_name - - depends_on = [module.init] -} - -module "azure_db" { - source = "../../modules/azure_mssql/v1" - common = local.common - logic_app_ids = module.logic_app.ids - logic_app_name = module.logic_app.name - - depends_on = [module.init] -} - -module "storage_account" { - source = "../../modules/storage_account/v1" - common = local.common - logic_app = module.logic_app.ids - - depends_on = [module.init] -} - -module "api_management" { - source = "../../modules/api_management" - common = local.common - logic_app_endpoint = module.logic_app.endpoint - - depends_on = [module.init] -} - -module "data_factory" { - source = "../../modules/data_factory/v1" - common = local.common - storage_account = module.storage_account.meta - key_vault = module.key_vault["primary"].meta - secrets = module.key_vault["primary"].secrets - - depends_on = [module.init] -} - -module "key_vault" { - for_each = local.key_vaults - - source = "../../modules/key_vault" - common = local.common - secrets = each.value.secrets - key = each.key - - depends_on = [module.init] -} diff --git a/.github/actions/terraform-templates/azure/env/01/~locals.tf b/.github/actions/terraform-templates/azure/env/01/~locals.tf deleted file mode 100644 index 8ab2c1ce2e8..00000000000 --- a/.github/actions/terraform-templates/azure/env/01/~locals.tf +++ /dev/null @@ -1,39 +0,0 @@ -variable "environment" {} -variable "owner_object_id" {} -variable "owner_email" {} -variable "uid" {} -variable "location" {} -variable "resource_group" {} - -locals { - env = var.environment - common = { - env = local.env - uid = var.uid - subscription = data.azurerm_subscription.default - tenant_id = data.azurerm_subscription.default.tenant_id - location = var.location - resource_group = data.azurerm_resource_group.default - owner_email = var.owner_email - owner = data.azuread_users.owner.users[0] - } - key_vaults = { - primary = { - secrets = { - "${local.env}-SECRET" = { - value = "PLACEHOLDER_${local.env}-SECRET" - } - "${local.env}-KEY" = { - value = "PLACEHOLDER_${local.env}-KEY" - } - "${local.env}-API-ENDPOINT" = { - value = "PLACEHOLDER_${local.env}-API-ENDPOINT" - } - "${local.env}-API-URL" = { - value = "PLACEHOLDER_${local.env}-API-URL" - } - } - } - } - dev_roles = ["Contributor", "Storage Table Data Contributor", "Storage Blob Data Contributor", "Key Vault Administrator"] -} diff --git a/.github/actions/terraform-templates/azure/env/02/config.tf b/.github/actions/terraform-templates/azure/env/02/config.tf deleted file mode 100644 index 48ba68ca1a0..00000000000 --- a/.github/actions/terraform-templates/azure/env/02/config.tf +++ /dev/null @@ -1,35 +0,0 @@ -terraform { - required_version = ">= 1.7.5" - required_providers { - azurerm = { - source = "hashicorp/azurerm" - version = "3.99.0" - } - azuread = { - source = "hashicorp/azuread" - version = "2.48.0" - } - random = { - source = "hashicorp/random" - version = "3.6.1" - } - azapi = { - source = "Azure/azapi" - version = "1.12.1" - } - } -} - -provider "azurerm" { - # This is only required when the User, Service Principal, or Identity running Terraform lacks the permissions to register Azure Resource Providers. - skip_provider_registration = false - features { - template_deployment { - delete_nested_items_during_deletion = true - } - key_vault { - recover_soft_deleted_key_vaults = true - purge_soft_delete_on_destroy = true - } - } -} diff --git a/.github/actions/terraform-templates/azure/env/02/data.tf b/.github/actions/terraform-templates/azure/env/02/data.tf deleted file mode 100644 index 6f095f4479a..00000000000 --- a/.github/actions/terraform-templates/azure/env/02/data.tf +++ /dev/null @@ -1,15 +0,0 @@ -data "azuread_users" "owner" { - object_ids = [var.owner_object_id] -} - -data "azurerm_resource_group" "default" { - name = var.resource_group -} - -data "azurerm_subscription" "default" { -} - -resource "random_password" "sql_password" { - length = 16 - special = true -} diff --git a/.github/actions/terraform-templates/azure/env/02/main.tf b/.github/actions/terraform-templates/azure/env/02/main.tf deleted file mode 100644 index edeaa50763c..00000000000 --- a/.github/actions/terraform-templates/azure/env/02/main.tf +++ /dev/null @@ -1,42 +0,0 @@ -module "init" { - source = "../../modules/init/v1" - common = local.common - dev_roles = local.dev_roles -} - -module "key_vault" { - for_each = local.key_vaults - - source = "../../modules/key_vault" - common = local.common - secrets = each.value.secrets - key = each.key - - depends_on = [module.init] -} - -module "azure_mssql" { - for_each = local.mssql_instances - - source = "../../modules/azure_mssql/v2" - common = local.common - key = each.key - sqladmins = module.init.sqladmins - databases = each.value.databases - epool = each.value.epool - - admin_username = local.key_vaults.primary.secrets.SqlUsername.value - admin_password = local.key_vaults.primary.secrets.SqlPassword.value - - depends_on = [module.init, module.key_vault] -} - -module "data_factory" { - for_each = local.data_factories - - source = "../../modules/data_factory/v2" - common = local.common - roles = each.value.roles - key = each.key - -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/env/02/~locals.tf b/.github/actions/terraform-templates/azure/env/02/~locals.tf deleted file mode 100644 index 9849525a882..00000000000 --- a/.github/actions/terraform-templates/azure/env/02/~locals.tf +++ /dev/null @@ -1,64 +0,0 @@ -variable "environment" {} -variable "owner_object_id" {} -variable "owner_email" {} -variable "uid" {} -variable "location" {} -variable "resource_group" {} - -locals { - env = var.environment - common = { - env = local.env - uid = var.uid - subscription = data.azurerm_subscription.default - tenant_id = data.azurerm_subscription.default.tenant_id - location = var.location - resource_group = data.azurerm_resource_group.default - owner_email = var.owner_email - owner = data.azuread_users.owner.users[0] - } - mssql_instances = { - primary = { - databases = ["trialdb", "admindb"] - epool = true - }, - secondary = { - databases = ["trialdb"] - epool = false - }, - demo = { - databases = [] - epool = true - } - } - key_vaults = { - primary = { - secrets = { - SqlUsername = { - value = "${local.env}admin" - } - SqlPassword = { - value = random_password.sql_password.result - } - Uid = { - value = var.uid - } - } - } - } - data_factories = { - dev = { - roles = toset(["Storage Blob Data Contributor", "Key Vault Reader", "Key Vault Secrets User"]) - } - qa = { - roles = toset(["Key Vault Reader", "Key Vault Secrets User"]) - } - uat = { - roles = toset(["Key Vault Reader", "Key Vault Secrets User"]) - } - prod = { - roles = toset(["Key Vault Reader", "Key Vault Secrets User"]) - } - } - dev_roles = ["Contributor", "Storage Table Data Contributor", "Storage Blob Data Contributor", "Key Vault Administrator"] -} diff --git a/.github/actions/terraform-templates/azure/env/03/README.md b/.github/actions/terraform-templates/azure/env/03/README.md deleted file mode 100644 index a7f80d68c2e..00000000000 --- a/.github/actions/terraform-templates/azure/env/03/README.md +++ /dev/null @@ -1,142 +0,0 @@ -# [PostgreSQL/Citus Migrate Production Data](https://docs.citusdata.com/en/stable/develop/migration_data.html#migrate-production-data) - -## [Big Database Migration](https://docs.citusdata.com/en/stable/develop/migration_data_big.html#big-database-migration) - -> ⚠️Recommends support request⚠️ - -## [Small Database Migration](https://docs.citusdata.com/en/stable/develop/migration_data_small.html#small-database-migration) - -> No support request - -### 1. Set variables - -```sh {"id":"01HXCRAJCEMCY0AF3403N8NGVJ"} -export PGPASSWORD="<password>" - -host="<cluster name>.postgres.cosmos.azure.com" -user="citus" -db="citus" -schema="public" -``` - -<details> - <summary>Optional: create test table</summary> - -```sql {"id":"01HXCRAJCEMCY0AF3405CYZXX0"} -create table public.test (name varchar) -insert into public.test (name) values ('test') -``` - -</details> - -### 2. Dump schema - -```sh {"id":"01HXCRAJCEMCY0AF3406ARMBC1"} -pg_dump \ ---format=plain \ ---no-owner \ ---schema-only \ ---file=/mnt/storage/$schema.sql \ ---schema=$schema \ -postgres://$user@$host:5432/$db -``` - -### 3. Dump data - -```sh {"id":"01HXCRAJCEMCY0AF3407N1BHNM"} -pg_dump \ --Z 0 \ ---format=custom \ ---no-owner \ ---data-only \ ---file=/mnt/storage/${schema}data.dump \ ---schema=$schema \ -postgres://$user@$host:5432/$db -``` - -<details> - <summary>Dump slow?</summary> - -### Table bloats and vacuuming - -High `dead_pct` (dead tubles percentage) indicates the table may need to be vacuumed: - -```sql {"id":"01HXCRAJCEMCY0AF340AX8TQQ7"} -select -schemaname, -relname as table_name, -n_dead_tup, -n_live_tup, -round(n_dead_tup::float/n_live_tup::float*100) dead_pct, -autovacuum_count, -last_vacuum, -last_autovacuum, -last_autoanalyze, -last_analyze -from pg_stat_all_tables -where n_live_tup >0 -order by round(n_dead_tup::float/n_live_tup::float*100) desc; -``` - -Vacuum: - -```sql {"id":"01HXCRAJCEMCY0AF340CJ0QBCK"} -vacuum(analyze, verbose) <table_name> -``` - -### Reduce network bottlenecks - -For Azure, create a [Container Instance](https://azure.microsoft.com/en-us/products/container-instances) with a mounted storage account file share. Install a version of PostgreSQL that matches the target server. Open the IP of the container instance on the PostgreSQL server. Run `pg_dump` on the container instance with the file targeting the storage account mount. - -</details> - -<details> - <summary>Optional: drop test table</summary> - -```sql {"id":"01HXCRAJCEMCY0AF340DZNSZJ4"} -drop table public.test; -``` - -</details> - -### 4. Restore schema - -```sh {"id":"01HXCRAJCEMCY0AF340HGJYP00"} -psql \ --h $host \ --d $db \ --U $user \ --f /mnt/storage/$schema.sql -``` - -### 5. (Citus) Run configuration functions - -* Run your [create_distributed_table](https://docs.citusdata.com/en/stable/develop/api_udf.html#create-distributed-table) and [create_reference_table](https://docs.citusdata.com/en/stable/develop/api_udf.html#create-reference-table) statements. If you get an error about foreign keys, it’s generally due to the order of operations. Drop foreign keys before distributing tables and then re-add them. -* Put the application into maintenance mode, and disable any other writes to the old database. - -### 6. Restore data - -```sh {"id":"01HXCRAJCEMCY0AF340HXZ9BJY"} -pg_restore \ ---host=$host \ ---dbname=$db \ ---username=$user \ -/mnt/storage/${schema}data.dump -``` - -<details> - <summary>Example: SQL file restore</summary> - -```sh {"id":"01HXCRAJCEMCY0AF340NX40Q7C"} -psql "host=$host port=5432 dbname=$db user=$user sslmode=require password=$PGPASSWORD" < /mnt/storage/posthistory.sql -``` - -</details> -<details> - <summary>Optional: select test table</summary> - -```sql {"id":"01HXCRAJCEMCY0AF340QGP2J3K"} -select * from public.test limit 1; -``` - -</details> \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/env/03/config.tf b/.github/actions/terraform-templates/azure/env/03/config.tf deleted file mode 100644 index 48ba68ca1a0..00000000000 --- a/.github/actions/terraform-templates/azure/env/03/config.tf +++ /dev/null @@ -1,35 +0,0 @@ -terraform { - required_version = ">= 1.7.5" - required_providers { - azurerm = { - source = "hashicorp/azurerm" - version = "3.99.0" - } - azuread = { - source = "hashicorp/azuread" - version = "2.48.0" - } - random = { - source = "hashicorp/random" - version = "3.6.1" - } - azapi = { - source = "Azure/azapi" - version = "1.12.1" - } - } -} - -provider "azurerm" { - # This is only required when the User, Service Principal, or Identity running Terraform lacks the permissions to register Azure Resource Providers. - skip_provider_registration = false - features { - template_deployment { - delete_nested_items_during_deletion = true - } - key_vault { - recover_soft_deleted_key_vaults = true - purge_soft_delete_on_destroy = true - } - } -} diff --git a/.github/actions/terraform-templates/azure/env/03/data.tf b/.github/actions/terraform-templates/azure/env/03/data.tf deleted file mode 100644 index e533dda2eb0..00000000000 --- a/.github/actions/terraform-templates/azure/env/03/data.tf +++ /dev/null @@ -1,18 +0,0 @@ -data "azuread_users" "owner" { - object_ids = [var.owner_object_id] -} - -data "azurerm_resource_group" "default" { - name = var.resource_group -} - -data "azurerm_subscription" "default" { -} - -resource "random_password" "sql_password" { - length = 16 - special = false - upper = true - lower = true - numeric = true -} diff --git a/.github/actions/terraform-templates/azure/env/03/main.tf b/.github/actions/terraform-templates/azure/env/03/main.tf deleted file mode 100644 index 7efaf1bdffd..00000000000 --- a/.github/actions/terraform-templates/azure/env/03/main.tf +++ /dev/null @@ -1,104 +0,0 @@ -module "init" { - source = "../../modules/init/v1" - common = local.common - dev_roles = local.dev_roles -} - -module "key_vault" { - for_each = local.key_vaults - - source = "../../modules/key_vault" - common = local.common - secrets = each.value.secrets - key = each.key - - depends_on = [module.init] -} - -module "cosmosdb_postgresql" { - for_each = local.cosmosdb_postgresql - - source = "../../modules/cosmosdb_postgresql/v1" - common = local.common - key = each.key - admin_password = local.key_vaults.primary.secrets.postgresPass.value - - depends_on = [module.init] -} - -module "vnet" { - for_each = local.vnets - - source = "../../modules/vnet" - common = local.common - address_space = each.value.address_space - subnets = each.value.subnets - key = each.key - - depends_on = [module.init] -} - -module "dns_zone" { - for_each = local.dns_zones - - source = "../../modules/dns_zone" - common = local.common - dns_name = each.value.name - dns_links = each.value.vnet_links - key = each.key - - vnets = { for vnet in module.vnet : vnet.meta.name => vnet.meta.id } - - depends_on = [module.init, module.vnet] -} - -module "private_endpoint" { - for_each = local.private_endpoints - - source = "../../modules/private_endpoint" - common = local.common - key = each.key - subresource_names = each.value.subresource_names - is_manual_connection = each.value.is_manual_connection - - subnet_id = module.vnet[each.value.vnet_key].subnets[each.value.subnet_key].id - dns_zone_ids = [lookup({ for zone in module.dns_zone : zone.meta.name => zone.meta.id }, each.value.dns_zone_key, "")] - resource_id = lookup({ for psql in module.cosmosdb_postgresql : psql.meta.name => psql.meta.id }, "cluster-${local.common.uid}-${local.common.env}-${each.value.resource_id_key}", "") - - depends_on = [module.init, module.vnet, module.dns_zone, module.cosmosdb_postgresql] -} - -module "storage_account" { - for_each = local.storage_accounts - - source = "../../modules/storage_account/v2" - common = local.common - key = each.key - account_tier = each.value.account_tier - account_kind = each.value.account_kind - - depends_on = [module.init] -} - -module "container_instance" { - for_each = local.container_instances - - source = "../../modules/container_instance" - common = local.common - key = each.key - storage_account = module.storage_account[each.value.storage_account_key].meta - image = each.value.image - cpu_cores = each.value.cpu_cores - mem_gb = each.value.mem_gb - commands = each.value.commands - shares = each.value.shares - repos = each.value.repos - exec = each.value.exec - os_type = each.value.os_type - - depends_on = [module.init, module.storage_account, module.vnet, module.key_vault] -} - -output "cinst_exec" { - value = module.container_instance[*] -} diff --git a/.github/actions/terraform-templates/azure/env/03/~locals.tf b/.github/actions/terraform-templates/azure/env/03/~locals.tf deleted file mode 100644 index f1f279a8787..00000000000 --- a/.github/actions/terraform-templates/azure/env/03/~locals.tf +++ /dev/null @@ -1,102 +0,0 @@ -variable "environment" {} -variable "owner_object_id" {} -variable "owner_email" {} -variable "uid" {} -variable "location" {} -variable "resource_group" {} - -locals { - env = var.environment - common = { - env = local.env - uid = var.uid - subscription = data.azurerm_subscription.default - tenant_id = data.azurerm_subscription.default.tenant_id - location = var.location - resource_group = data.azurerm_resource_group.default - owner_email = var.owner_email - owner = data.azuread_users.owner.users[0] - } - key_vaults = { - primary = { - secrets = { - postgresUser = { - value = "${local.env}admin" - } - postgresPass = { - value = random_password.sql_password.result - } - Uid = { - value = var.uid - } - } - } - } - cosmosdb_postgresql = { - dev = {} - } - vnets = { - primary = { - address_space = { - value = ["10.5.0.0/16"] - } - subnets = { - service = { - address_prefixes = { - value = ["10.5.1.0/24"] - } - link_service_policies = true - endpoint_policies = false - } - endpoint = { - address_prefixes = { - value = ["10.5.2.0/24"] - } - link_service_policies = false - endpoint_policies = true - } - } - } - } - dns_zones = { - postgreshsc = { - name = "privatelink.postgreshsc.database.azure.com" - vnet_links = { - primary = { - registration_enabled = false - vnet_key = "primary" - } - } - } - } - private_endpoints = { - cosmosdb_postgresql = { - vnet_key = "primary" - subnet_key = "endpoint" - dns_zone_key = "privatelink.postgreshsc.database.azure.com" - resource_id_key = "dev" - subresource_names = ["coordinator"] - is_manual_connection = false - } - } - storage_accounts = { - dev = { - account_tier = "Premium" //Standard, Premium - account_kind = "FileStorage" //StorageV2, FileStorage, BlockBlobStorage, BlobStorage - } - } - container_instances = { - dev = { - storage_account_key = "dev" - os_type = "Linux" - image = "mcr.microsoft.com/azure-dev-cli-apps:latest" - cpu_cores = 4 - mem_gb = 16 - commands = ["/bin/bash", "-c", "sleep infinity"] - exec = "/bin/bash" - shares = { storage = { mount_path = "/mnt/storage", gb = 1000, tier = "Premium" } } //TransactionOptimized, Premium, Hot, Cool - repos = { terraform-templates = { url = "https://github.com/JosiahSiegel/terraform-templates.git", mount_path = "/app/repo1" }, so2pg = { url = "https://github.com/JosiahSiegel/stackoverflow_in_pg.git", mount_path = "/app/repo2" } } - } - } - dev_roles = ["Contributor", "Storage Table Data Contributor", "Storage Blob Data Contributor", "Key Vault Administrator", "Storage File Data Privileged Contributor", "Storage File Data SMB Share Elevated Contributor"] -} diff --git a/.github/actions/terraform-templates/azure/env/05/config.tf b/.github/actions/terraform-templates/azure/env/05/config.tf deleted file mode 100644 index 90b2ad6b054..00000000000 --- a/.github/actions/terraform-templates/azure/env/05/config.tf +++ /dev/null @@ -1,31 +0,0 @@ -terraform { - required_version = ">= 1.7.5" - required_providers { - azurerm = { - source = "hashicorp/azurerm" - version = "3.99.0" - } - azuread = { - source = "hashicorp/azuread" - version = "2.48.0" - } - random = { - source = "hashicorp/random" - version = "3.6.1" - } - } -} - -provider "azurerm" { - # This is only required when the User, Service Principal, or Identity running Terraform lacks the permissions to register Azure Resource Providers. - skip_provider_registration = false - features { - template_deployment { - delete_nested_items_during_deletion = true - } - key_vault { - recover_soft_deleted_key_vaults = true - purge_soft_delete_on_destroy = true - } - } -} diff --git a/.github/actions/terraform-templates/azure/env/05/data.tf b/.github/actions/terraform-templates/azure/env/05/data.tf deleted file mode 100644 index 8252edfe813..00000000000 --- a/.github/actions/terraform-templates/azure/env/05/data.tf +++ /dev/null @@ -1,18 +0,0 @@ -data "azuread_users" "owner" { - object_ids = [var.owner_object_id] -} - -data "azurerm_resource_group" "default" { - name = var.resource_group -} - -data "azurerm_subscription" "default" { -} - -resource "random_password" "user_password" { - length = 8 - special = false - upper = true - lower = true - numeric = true -} diff --git a/.github/actions/terraform-templates/azure/env/05/main.tf b/.github/actions/terraform-templates/azure/env/05/main.tf deleted file mode 100644 index 6d3e180a1cb..00000000000 --- a/.github/actions/terraform-templates/azure/env/05/main.tf +++ /dev/null @@ -1,41 +0,0 @@ -module "init" { - source = "../../modules/init/v2" - common = local.common - dev_roles = local.dev_roles -} - -module "storage_account" { - for_each = local.storage_accounts - - source = "../../modules/storage_account/v2" - common = local.common - key = each.key - account_tier = each.value.account_tier - account_kind = each.value.account_kind - - depends_on = [module.init] -} - -module "container_instance" { - for_each = local.container_instances - - source = "../../modules/container_instance" - common = local.common - key = each.key - storage_account = module.storage_account[each.value.storage_account_key].meta - image = each.value.image - cpu_cores = each.value.cpu_cores - mem_gb = each.value.mem_gb - commands = each.value.commands - shares = each.value.shares - repos = each.value.repos - exec = each.value.exec - os_type = each.value.os_type - user_password = each.value.user_password - - depends_on = [module.init, module.storage_account] -} - -output "cinst_exec" { - value = module.container_instance[*] -} diff --git a/.github/actions/terraform-templates/azure/env/05/~locals.tf b/.github/actions/terraform-templates/azure/env/05/~locals.tf deleted file mode 100644 index 0414c5b4840..00000000000 --- a/.github/actions/terraform-templates/azure/env/05/~locals.tf +++ /dev/null @@ -1,41 +0,0 @@ -variable "environment" {} -variable "owner_object_id" {} -variable "owner_email" {} -variable "uid" {} -variable "location" {} -variable "resource_group" {} - -locals { - env = var.environment - common = { - env = local.env - uid = var.uid - subscription = data.azurerm_subscription.default - tenant_id = data.azurerm_subscription.default.tenant_id - location = var.location - resource_group = data.azurerm_resource_group.default - owner_email = var.owner_email - owner = data.azuread_users.owner.users[0] - } - storage_accounts = { - standard = { - account_tier = "Standard" //Standard, Premium - account_kind = "StorageV2" //StorageV2, FileStorage, BlockBlobStorage, BlobStorage - } - } - container_instances = { - standard = { - storage_account_key = "standard" - os_type = "Linux" - image = "ghcr.io/josiahsiegel/dev-image:latest" - cpu_cores = 4 - mem_gb = 8 - commands = [] - exec = "/bin/bash" - shares = { storage = { mount_path = "/mnt/storage", gb = 500, tier = "TransactionOptimized" } } //TransactionOptimized, Premium, Hot, Cool - repos = { terraform-templates = { url = "https://github.com/JosiahSiegel/terraform-templates.git", mount_path = "/mnt/storage/repo1" } } - user_password = random_password.user_password.result - } - } - dev_roles = ["Contributor", "Storage File Data Privileged Contributor", "Storage File Data SMB Share Elevated Contributor"] -} diff --git a/.github/actions/terraform-templates/azure/env/06/config.tf b/.github/actions/terraform-templates/azure/env/06/config.tf deleted file mode 100644 index f67ddac36e9..00000000000 --- a/.github/actions/terraform-templates/azure/env/06/config.tf +++ /dev/null @@ -1,27 +0,0 @@ -terraform { - required_version = ">= 1.7.5" - required_providers { - azurerm = { - source = "hashicorp/azurerm" - version = "3.99.0" - } - azuread = { - source = "hashicorp/azuread" - version = "2.48.0" - } - } -} - -provider "azurerm" { - # This is only required when the User, Service Principal, or Identity running Terraform lacks the permissions to register Azure Resource Providers. - skip_provider_registration = false - features { - template_deployment { - delete_nested_items_during_deletion = true - } - key_vault { - recover_soft_deleted_key_vaults = true - purge_soft_delete_on_destroy = true - } - } -} diff --git a/.github/actions/terraform-templates/azure/env/06/data.tf b/.github/actions/terraform-templates/azure/env/06/data.tf deleted file mode 100644 index b62ec85569e..00000000000 --- a/.github/actions/terraform-templates/azure/env/06/data.tf +++ /dev/null @@ -1,10 +0,0 @@ -data "azuread_users" "owner" { - object_ids = [var.owner_object_id] -} - -data "azurerm_resource_group" "default" { - name = var.resource_group -} - -data "azurerm_subscription" "default" { -} diff --git a/.github/actions/terraform-templates/azure/env/06/main.tf b/.github/actions/terraform-templates/azure/env/06/main.tf deleted file mode 100644 index dbb2306b745..00000000000 --- a/.github/actions/terraform-templates/azure/env/06/main.tf +++ /dev/null @@ -1,27 +0,0 @@ -module "init" { - source = "../../modules/init/v2" - common = local.common - dev_roles = local.dev_roles -} - -module "container_instance" { - for_each = local.container_instances - - source = "../../modules/container_instance" - common = local.common - key = each.key - image = each.value.image - cpu_cores = each.value.cpu_cores - mem_gb = each.value.mem_gb - commands = each.value.commands - shares = each.value.shares - repos = each.value.repos - exec = each.value.exec - os_type = each.value.os_type - - depends_on = [module.init] -} - -output "cinst_exec" { - value = module.container_instance[*] -} diff --git a/.github/actions/terraform-templates/azure/env/06/~locals.tf b/.github/actions/terraform-templates/azure/env/06/~locals.tf deleted file mode 100644 index 2ac4d693baf..00000000000 --- a/.github/actions/terraform-templates/azure/env/06/~locals.tf +++ /dev/null @@ -1,35 +0,0 @@ -variable "environment" {} -variable "owner_object_id" {} -variable "owner_email" {} -variable "uid" {} -variable "location" {} -variable "resource_group" {} - -locals { - env = var.environment - common = { - env = local.env - uid = var.uid - subscription = data.azurerm_subscription.default - tenant_id = data.azurerm_subscription.default.tenant_id - location = var.location - resource_group = data.azurerm_resource_group.default - owner_email = var.owner_email - owner = data.azuread_users.owner.users[0] - } - storage_accounts = { - } - container_instances = { - windev = { - os_type = "Windows" - image = "mcr.microsoft.com/windows:1809" - cpu_cores = 2 - mem_gb = 4 - commands = ["cmd.exe", "/c", "ping -t localhost > NUL"] - exec = "" - shares = {} - repos = {} - } - } - dev_roles = [] -} diff --git a/.github/actions/terraform-templates/azure/env/07/config.tf b/.github/actions/terraform-templates/azure/env/07/config.tf deleted file mode 100644 index f67ddac36e9..00000000000 --- a/.github/actions/terraform-templates/azure/env/07/config.tf +++ /dev/null @@ -1,27 +0,0 @@ -terraform { - required_version = ">= 1.7.5" - required_providers { - azurerm = { - source = "hashicorp/azurerm" - version = "3.99.0" - } - azuread = { - source = "hashicorp/azuread" - version = "2.48.0" - } - } -} - -provider "azurerm" { - # This is only required when the User, Service Principal, or Identity running Terraform lacks the permissions to register Azure Resource Providers. - skip_provider_registration = false - features { - template_deployment { - delete_nested_items_during_deletion = true - } - key_vault { - recover_soft_deleted_key_vaults = true - purge_soft_delete_on_destroy = true - } - } -} diff --git a/.github/actions/terraform-templates/azure/env/07/data.tf b/.github/actions/terraform-templates/azure/env/07/data.tf deleted file mode 100644 index b62ec85569e..00000000000 --- a/.github/actions/terraform-templates/azure/env/07/data.tf +++ /dev/null @@ -1,10 +0,0 @@ -data "azuread_users" "owner" { - object_ids = [var.owner_object_id] -} - -data "azurerm_resource_group" "default" { - name = var.resource_group -} - -data "azurerm_subscription" "default" { -} diff --git a/.github/actions/terraform-templates/azure/env/07/main.tf b/.github/actions/terraform-templates/azure/env/07/main.tf deleted file mode 100644 index 610475ff7fa..00000000000 --- a/.github/actions/terraform-templates/azure/env/07/main.tf +++ /dev/null @@ -1,31 +0,0 @@ -module "init" { - source = "../../modules/init/v2" - common = local.common - dev_roles = local.dev_roles -} - -module "azure_ad" { - source = "../../modules/azure_ad/v2" - - common = local.common - active_directory_domain_name = "${local.azure_ad.key}.local" - active_directory_netbios_name = local.azure_ad.key - prefix = local.azure_ad.key - - depends_on = [module.init] -} - - -module "mssql_vm" { - for_each = local.mssql_vms - - source = "../../modules/mssql_vm" - common = local.common - key = each.key - ad_dns_ips = module.azure_ad.dns_ips - domain_name = "${local.azure_ad.key}.local" - ad_username = module.azure_ad.domain_admin_username - ad_password = module.azure_ad.domain_admin_password - - depends_on = [module.init, module.azure_ad] -} diff --git a/.github/actions/terraform-templates/azure/env/07/~locals.tf b/.github/actions/terraform-templates/azure/env/07/~locals.tf deleted file mode 100644 index bcc937e5774..00000000000 --- a/.github/actions/terraform-templates/azure/env/07/~locals.tf +++ /dev/null @@ -1,42 +0,0 @@ -variable "environment" {} -variable "owner_object_id" {} -variable "owner_email" {} -variable "uid" {} -variable "location" {} -variable "resource_group" {} - -locals { - env = var.environment - common = { - env = local.env - uid = var.uid - subscription = data.azurerm_subscription.default - tenant_id = data.azurerm_subscription.default.tenant_id - location = var.location - resource_group = data.azurerm_resource_group.default - owner_email = var.owner_email - owner = data.azuread_users.owner.users[0] - } - storage_accounts = { - } - mssql_vms = { - dev = {} - } - azure_ad = { - key = var.uid - domain_name = "josiah0601gmail.onmicrosoft.com" - } - container_instances = { - windev = { - os_type = "Windows" - image = "mcr.microsoft.com/windows:1809" - cpu_cores = 2 - mem_gb = 4 - commands = ["cmd.exe", "/c", "ping -t localhost > NUL"] - exec = "" - shares = {} - repos = {} - } - } - dev_roles = [] -} diff --git a/.github/actions/terraform-templates/azure/modules/api_management/api_default.tf b/.github/actions/terraform-templates/azure/modules/api_management/api_default.tf deleted file mode 100644 index 0d08d881896..00000000000 --- a/.github/actions/terraform-templates/azure/modules/api_management/api_default.tf +++ /dev/null @@ -1,83 +0,0 @@ -resource "azurerm_api_management_api" "default" { - name = "${var.common.env}-api" - resource_group_name = var.common.resource_group.name - api_management_name = azurerm_api_management.default.name - revision = "1" - display_name = "${var.common.env} API" - path = var.common.env - protocols = ["https"] -} - -resource "azurerm_api_management_product" "default" { - product_id = "${var.common.env}-product" - api_management_name = azurerm_api_management.default.name - resource_group_name = var.common.resource_group.name - display_name = "${var.common.env} Product" - subscription_required = true - subscriptions_limit = 10 - approval_required = true - published = true -} - -resource "azurerm_api_management_product_api" "default" { - api_name = azurerm_api_management_api.default.name - product_id = azurerm_api_management_product.default.product_id - api_management_name = azurerm_api_management.default.name - resource_group_name = var.common.resource_group.name -} - -resource "azurerm_api_management_api_operation" "default_fetch" { - operation_id = "${var.common.env}-fetch" - api_name = azurerm_api_management_api.default.name - api_management_name = azurerm_api_management.default.name - resource_group_name = var.common.resource_group.name - display_name = "${var.common.env} fetch db" - method = "GET" - url_template = "/db" - description = "This can only be done by the logged in user." - - response { - status_code = 200 - } -} - -resource "azurerm_api_management_api_operation" "default_insert" { - operation_id = "${var.common.env}-insert" - api_name = azurerm_api_management_api.default.name - api_management_name = azurerm_api_management.default.name - resource_group_name = var.common.resource_group.name - display_name = "${var.common.env} insert db" - method = "POST" - url_template = "/db" - description = "This can only be done by the logged in user." - - response { - status_code = 200 - } -} - -resource "azurerm_api_management_backend" "default" { - name = "${var.common.env}-la-db-backend" - resource_group_name = var.common.resource_group.name - api_management_name = azurerm_api_management.default.name - protocol = "http" - url = var.logic_app_endpoint -} - -resource "azurerm_api_management_api_operation_policy" "default" { - api_name = azurerm_api_management_api.default.name - operation_id = azurerm_api_management_api_operation.default_fetch.operation_id - api_management_name = azurerm_api_management.default.name - resource_group_name = var.common.resource_group.name - xml_content = <<XML -<policies> - <inbound> - <base /> - <set-backend-service backend-id="${azurerm_api_management_backend.default.name}" /> - <set-method>POST</set-method> - <rewrite-uri template="?" /> - <set-header name="Ocp-Apim-Subscription-Key" exists-action="delete" /> - </inbound> -</policies> -XML -} diff --git a/.github/actions/terraform-templates/azure/modules/api_management/main.tf b/.github/actions/terraform-templates/azure/modules/api_management/main.tf deleted file mode 100644 index f8fe2bb88d6..00000000000 --- a/.github/actions/terraform-templates/azure/modules/api_management/main.tf +++ /dev/null @@ -1,13 +0,0 @@ -resource "azurerm_api_management" "default" { - name = "apim-${var.common.uid}-${var.common.env}" - resource_group_name = var.common.resource_group.name - location = var.common.location - publisher_name = var.common.env - publisher_email = var.common.owner_email - - sku_name = "Consumption_0" - - identity { - type = "SystemAssigned" - } -} diff --git a/.github/actions/terraform-templates/azure/modules/api_management/~inputs.tf b/.github/actions/terraform-templates/azure/modules/api_management/~inputs.tf deleted file mode 100644 index 5e02f48586a..00000000000 --- a/.github/actions/terraform-templates/azure/modules/api_management/~inputs.tf +++ /dev/null @@ -1,4 +0,0 @@ -variable "common" {} -variable "logic_app_endpoint" { - sensitive = true -} diff --git a/.github/actions/terraform-templates/azure/modules/api_management/~outputs.tf b/.github/actions/terraform-templates/azure/modules/api_management/~outputs.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v1/main.tf b/.github/actions/terraform-templates/azure/modules/azure_ad/v1/main.tf deleted file mode 100644 index 0c0c8d25c36..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_ad/v1/main.tf +++ /dev/null @@ -1,131 +0,0 @@ -# https://schnerring.net/blog/set-up-azure-active-directory-domain-services-aadds-with-terraform-updated/ -# service principal for Domain Controller Services -resource "azuread_service_principal" "aadds" { - client_id = "2565bd9d-da50-47d4-8b85-4c97f669dc36" -} - -# Microsoft.AAD Resource Provider Registration -resource "azurerm_resource_provider_registration" "aadds" { - name = "Microsoft.AAD" -} - -# DC Admin Group and User -resource "azuread_group" "dc_admins" { - display_name = "AAD DC Administrators" - description = "AADDS Administrators" - members = [azuread_user.dc_admin.object_id] - security_enabled = true -} - -resource "random_password" "dc_admin" { - length = 64 -} - -resource "azuread_user" "dc_admin" { - user_principal_name = "dc-admin@${var.domain_name}" - display_name = "AADDS DC Administrator" - password = random_password.dc_admin.result -} - -# Resource Group for Azure Active Directory Domain Services (AADDS) -resource "azurerm_resource_group" "aadds" { - name = "aadds-${var.key}-rg" - location = "East US" -} - -# Network Resources -resource "azurerm_virtual_network" "aadds" { - name = "aadds-vnet" - location = azurerm_resource_group.aadds.location - resource_group_name = azurerm_resource_group.aadds.name - address_space = ["10.0.0.0/16"] -} - -resource "azurerm_subnet" "aadds" { - name = "aadds-snet" - resource_group_name = azurerm_resource_group.aadds.name - virtual_network_name = azurerm_virtual_network.aadds.name - address_prefixes = ["10.0.0.0/24"] -} - -resource "azurerm_network_security_group" "aadds" { - name = "aadds-nsg" - location = azurerm_resource_group.aadds.location - resource_group_name = azurerm_resource_group.aadds.name - - security_rule { - name = "AllowRD" - priority = 201 - direction = "Inbound" - access = "Allow" - protocol = "Tcp" - source_port_range = "*" - destination_port_range = "3389" - source_address_prefix = "CorpNetSaw" - destination_address_prefix = "*" - } - - security_rule { - name = "AllowPSRemoting" - priority = 301 - direction = "Inbound" - access = "Allow" - protocol = "Tcp" - source_port_range = "*" - destination_port_range = "5986" - source_address_prefix = "AzureActiveDirectoryDomainServices" - destination_address_prefix = "*" - } - - /* - security_rule { - name = "AllowLDAPS" - priority = 401 - direction = "Inbound" - access = "Allow" - protocol = "Tcp" - source_port_range = "*" - destination_port_range = "636" - source_address_prefix = "<Authorized LDAPS IPs>" - destination_address_prefix = "*" - } - */ -} - -resource azurerm_subnet_network_security_group_association "aadds" { - subnet_id = azurerm_subnet.aadds.id - network_security_group_id = azurerm_network_security_group.aadds.id -} - -# AADDS Managed Domain -resource "azurerm_active_directory_domain_service" "aadds" { - name = "aadds" - location = azurerm_resource_group.aadds.location - resource_group_name = azurerm_resource_group.aadds.name - domain_configuration_type = "FullySynced" - - domain_name = var.domain_name - sku = "Standard" - - initial_replica_set { - subnet_id = azurerm_subnet.aadds.id - } - - notifications { - additional_recipients = ["josiah0601@gmail.com"] - notify_dc_admins = true - notify_global_admins = true - } - - security { - sync_kerberos_passwords = true - sync_ntlm_passwords = true - sync_on_prem_passwords = true - } - - depends_on = [ - azuread_service_principal.aadds, - azurerm_resource_provider_registration.aadds, - azurerm_subnet_network_security_group_association.aadds, - ] -} diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v1/~inputs.tf b/.github/actions/terraform-templates/azure/modules/azure_ad/v1/~inputs.tf deleted file mode 100644 index 76558c828dd..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_ad/v1/~inputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -variable "common" {} -variable "key" {} -variable "domain_name" {} diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v1/~outputs.tf b/.github/actions/terraform-templates/azure/modules/azure_ad/v1/~outputs.tf deleted file mode 100644 index 3eaefa0b195..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_ad/v1/~outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "dns_ips" { - value = azurerm_active_directory_domain_service.aadds.initial_replica_set.0.domain_controller_ip_addresses -} diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/files/FirstLogonCommands.xml b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/files/FirstLogonCommands.xml deleted file mode 100644 index 41a339dc946..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/files/FirstLogonCommands.xml +++ /dev/null @@ -1,22 +0,0 @@ -<!-- - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: MPL-2.0 ---> - -<FirstLogonCommands> - <SynchronousCommand> - <CommandLine>cmd /c "mkdir C:\terraform"</CommandLine> - <Description>Create the Terraform working directory</Description> - <Order>11</Order> - </SynchronousCommand> - <SynchronousCommand> - <CommandLine>cmd /c "copy C:\AzureData\CustomData.bin C:\terraform\winrm.ps1"</CommandLine> - <Description>Move the CustomData file to the working directory</Description> - <Order>12</Order> - </SynchronousCommand> - <SynchronousCommand> - <CommandLine>powershell.exe -sta -ExecutionPolicy Unrestricted -file C:\terraform\winrm.ps1</CommandLine> - <Description>Execute the WinRM enabling script</Description> - <Order>13</Order> - </SynchronousCommand> -</FirstLogonCommands> \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/files/winrm.ps1 b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/files/winrm.ps1 deleted file mode 100644 index afdf95de2c0..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/files/winrm.ps1 +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -$Cert = New-SelfSignedCertificate -DnsName $RemoteHostName, $ComputerName ` - -CertStoreLocation "cert:\LocalMachine\My" ` - -FriendlyName "Test WinRM Cert" - -$Cert | Out-String - -$Thumbprint = $Cert.Thumbprint - -Write-Host "Enable HTTPS in WinRM" -$WinRmHttps = "@{Hostname=`"$RemoteHostName`"; CertificateThumbprint=`"$Thumbprint`"}" -winrm create winrm/config/Listener?Address=*+Transport=HTTPS $WinRmHttps - -Write-Host "Set Basic Auth in WinRM" -$WinRmBasic = "@{Basic=`"true`"}" -winrm set winrm/config/service/Auth $WinRmBasic - -Write-Host "Open Firewall Port" -netsh advfirewall firewall add rule name="Windows Remote Management (HTTPS-In)" dir=in action=allow protocol=TCP localport=5985 \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/main.tf b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/main.tf deleted file mode 100644 index 5d326661bc1..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/main.tf +++ /dev/null @@ -1,93 +0,0 @@ -# https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples/virtual-machines/windows/vm-joined-to-active-directory - -resource "random_password" "domain_password" { - length = 16 - special = false - upper = true - lower = true - numeric = true -} - -resource "azurerm_virtual_network" "example" { - name = join("-", [var.prefix, "network"]) - location = var.common.location - address_space = ["10.0.0.0/16"] - resource_group_name = var.common.resource_group.name - dns_servers = ["10.0.1.4", "8.8.8.8"] -} - -resource "azurerm_subnet" "domain-controllers" { - name = "domain-controllers" - address_prefixes = ["10.0.1.0/24"] - resource_group_name = var.common.resource_group.name - virtual_network_name = azurerm_virtual_network.example.name -} - -resource "azurerm_subnet" "domain-members" { - name = "domain-members" - address_prefixes = ["10.0.2.0/24"] - resource_group_name = var.common.resource_group.name - virtual_network_name = azurerm_virtual_network.example.name -} - - -resource "azurerm_network_interface" "dc_nic" { - name = join("-", [var.prefix, "dc-primary"]) - location = var.common.location - resource_group_name = var.common.resource_group.name - ip_configuration { - name = "primary" - private_ip_address_allocation = "Static" - private_ip_address = "10.0.1.4" - subnet_id = azurerm_subnet.domain-controllers.id - } -} - -resource "azurerm_windows_virtual_machine" "domain-controller" { - name = local.virtual_machine_name - resource_group_name = var.common.resource_group.name - location = var.common.location - size = "Standard_B2s" - admin_username = "${var.prefix}admin" - admin_password = random_password.domain_password.result - custom_data = local.custom_data - - network_interface_ids = [ - azurerm_network_interface.dc_nic.id, - ] - - os_disk { - caching = "ReadWrite" - storage_account_type = "Standard_LRS" - } - - source_image_reference { - publisher = "MicrosoftWindowsServer" - offer = "WindowsServer" - sku = "2016-Datacenter" - version = "latest" - } - - additional_unattend_content { - content = local.auto_logon_data - setting = "AutoLogon" - } - - additional_unattend_content { - content = local.first_logon_data - setting = "FirstLogonCommands" - } -} - -resource "azurerm_virtual_machine_extension" "create-ad-forest" { - name = "create-active-directory-forest" - virtual_machine_id = azurerm_windows_virtual_machine.domain-controller.id - publisher = "Microsoft.Compute" - type = "CustomScriptExtension" - type_handler_version = "1.9" - settings = <<SETTINGS - { - "commandToExecute": "powershell.exe -Command \"${local.powershell_command}\"" - } -SETTINGS -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/~inputs.tf b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/~inputs.tf deleted file mode 100644 index 50a788aaee7..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/~inputs.tf +++ /dev/null @@ -1,31 +0,0 @@ -variable "common" {} - -variable "prefix" { - description = "The prefix which should be used for all resources in this example" -} - -variable "active_directory_domain_name" { - description = "the domain name for Active Directory, for example `consoto.local`" -} - -variable "active_directory_netbios_name" { - description = "The netbios name of the Active Directory domain, for example `consoto`" -} - -locals { - virtual_machine_name = join("-", [var.prefix, "dc"]) - virtual_machine_fqdn = join(".", [local.virtual_machine_name, var.active_directory_domain_name]) - auto_logon_data = "<AutoLogon><Password><Value>${random_password.domain_password.result}</Value></Password><Enabled>true</Enabled><LogonCount>1</LogonCount><Username>${var.prefix}admin</Username></AutoLogon>" - first_logon_data = file("${path.module}/files/FirstLogonCommands.xml") - custom_data_params = "Param($RemoteHostName = \"${local.virtual_machine_fqdn}\", $ComputerName = \"${local.virtual_machine_name}\")" - custom_data = base64encode(join(" ", [local.custom_data_params, file("${path.module}/files/winrm.ps1")])) - - import_command = "Import-Module ADDSDeployment" - password_command = "$password = ConvertTo-SecureString ${random_password.domain_password.result} -AsPlainText -Force" - install_ad_command = "Add-WindowsFeature -name ad-domain-services -IncludeManagementTools" - configure_ad_command = "Install-ADDSForest -CreateDnsDelegation:$false -DomainMode Win2012R2 -DomainName ${var.active_directory_domain_name} -DomainNetbiosName ${var.active_directory_netbios_name} -ForestMode Win2012R2 -InstallDns:$true -SafeModeAdministratorPassword $password -Force:$true" - shutdown_command = "shutdown -r -t 10" - exit_code_hack = "exit 0" - powershell_command = "${local.import_command}; ${local.password_command}; ${local.install_ad_command}; ${local.configure_ad_command}; ${local.shutdown_command}; ${local.exit_code_hack}" - -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/~outputs.tf b/.github/actions/terraform-templates/azure/modules/azure_ad/v2/~outputs.tf deleted file mode 100644 index c712a0268cd..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_ad/v2/~outputs.tf +++ /dev/null @@ -1,12 +0,0 @@ -output "dns_ips" { - value = ["10.0.1.4", "8.8.8.8"]//azurerm_active_directory_domain_service.aadds.initial_replica_set.0.domain_controller_ip_addresses -} - -output "domain_admin_username" { - value = "${var.prefix}admin" -} - -output "domain_admin_password" { - value = random_password.domain_password.result -} - diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/main.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/main.tf deleted file mode 100644 index 8fc930a74d3..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/main.tf +++ /dev/null @@ -1,84 +0,0 @@ -data "http" "icanhazip" { - url = "http://icanhazip.com" -} - -locals { - public_ip = chomp(data.http.icanhazip.response_body) -} - -resource "azuread_group" "sqladmins" { - display_name = "sqladmins-${var.common.env}" - owners = [var.common.owner.object_id] - security_enabled = true - - members = [ - var.common.owner.object_id, - /* more users */ - ] -} - -# Create an Azure SQL Server -resource "azurerm_mssql_server" "default" { - name = "sqlserver-${var.common.uid}-${var.common.env}" - resource_group_name = var.common.resource_group.name - location = var.common.location - version = "12.0" - minimum_tls_version = "1.2" - - azuread_administrator { - login_username = azuread_group.sqladmins.display_name - object_id = azuread_group.sqladmins.object_id - azuread_authentication_only = true - } - - identity { - type = "SystemAssigned" - } -} - -# Create an Azure SQL Database -resource "azurerm_mssql_database" "default" { - name = "db-${var.common.env}" - server_id = azurerm_mssql_server.default.id - collation = "SQL_Latin1_General_CP1_CI_AS" - license_type = "LicenseIncluded" - max_size_gb = 10 - read_scale = false - sku_name = "S0" - zone_redundant = false - enclave_type = "VBS" - storage_account_type = "Local" - #auto_pause_delay_in_minutes = 10 - - # prevent the possibility of accidental data loss - lifecycle { - #prevent_destroy = true - } -} - -resource "mssql_user" "la" { - server { - host = azurerm_mssql_server.default.fully_qualified_domain_name - azuread_default_chain_auth {} - } - - database = azurerm_mssql_database.default.name - username = var.logic_app_name - roles = ["db_datareader", "db_datawriter"] - - depends_on = [azurerm_mssql_firewall_rule.default] -} - -resource "azurerm_mssql_firewall_rule" "default" { - name = "terraform-firewall-rule" - server_id = azurerm_mssql_server.default.id - start_ip_address = local.public_ip - end_ip_address = local.public_ip -} - -resource "azurerm_mssql_firewall_rule" "azure_access" { - name = "allow-access-from-azure" - server_id = azurerm_mssql_server.default.id - start_ip_address = "0.0.0.0" - end_ip_address = "0.0.0.0" -} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~inputs.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~inputs.tf deleted file mode 100644 index 1357cab6441..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~inputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -variable "common" {} -variable "logic_app_ids" {} -variable "logic_app_name" {} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~outputs.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~outputs.tf deleted file mode 100644 index 50b94d9bbc9..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~outputs.tf +++ /dev/null @@ -1,11 +0,0 @@ -output "id" { - value = azurerm_mssql_server.default.identity[0].principal_id -} - -output "fqdn" { - value = azurerm_mssql_server.default.fully_qualified_domain_name -} - -output "db_name" { - value = azurerm_mssql_database.default.name -} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~providers.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~providers.tf deleted file mode 100644 index 74f3280d715..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_mssql/v1/~providers.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - mssql = { - version = "~> 0.3" - source = "betr-io/mssql" - } - } -} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/databases.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/databases.tf deleted file mode 100644 index 460f2f6232e..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/databases.tf +++ /dev/null @@ -1,22 +0,0 @@ -# Create an Azure SQL Database -resource "azurerm_mssql_database" "default" { - for_each = toset(var.databases) - - name = each.value - server_id = azurerm_mssql_server.default.id - collation = "SQL_Latin1_General_CP1_CI_AS" - license_type = "LicenseIncluded" - read_scale = false - zone_redundant = false - enclave_type = "VBS" - storage_account_type = "Local" - #auto_pause_delay_in_minutes = 10 - - sku_name = try(var.epool) == true ? "ElasticPool" : "S0" - elastic_pool_id = try(var.epool) == true ? azurerm_mssql_elasticpool.default["default"].id : null - - # prevent the possibility of accidental data loss - lifecycle { - #prevent_destroy = true - } -} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/elastic_pool.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/elastic_pool.tf deleted file mode 100644 index a099515fb25..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/elastic_pool.tf +++ /dev/null @@ -1,22 +0,0 @@ -resource "azurerm_mssql_elasticpool" "default" { - for_each = try(var.epool) == true ? toset(["default"]) : [] - - name = "epool-${var.common.uid}-${var.common.env}-${var.key}" - resource_group_name = var.common.resource_group.name - location = var.common.location - server_name = azurerm_mssql_server.default.name - license_type = "LicenseIncluded" - enclave_type = "VBS" - max_size_gb = 4.8828125 - - sku { - name = "BasicPool" - tier = "Basic" - capacity = 50 - } - - per_database_settings { - min_capacity = 0 - max_capacity = 5 - } -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/main.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/main.tf deleted file mode 100644 index a3dde048639..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/main.tf +++ /dev/null @@ -1,43 +0,0 @@ -data "http" "icanhazip" { - url = "http://icanhazip.com" -} - -locals { - public_ip = chomp(data.http.icanhazip.response_body) -} - -# Create an Azure SQL Server -resource "azurerm_mssql_server" "default" { - name = "sqlserver-${var.common.uid}-${var.common.env}-${var.key}" - resource_group_name = var.common.resource_group.name - location = var.common.location - version = "12.0" - minimum_tls_version = "1.2" - - administrator_login = var.admin_username - administrator_login_password = var.admin_password - - azuread_administrator { - login_username = var.sqladmins.display_name - object_id = var.sqladmins.object_id - azuread_authentication_only = false - } - - identity { - type = "SystemAssigned" - } -} - -resource "azurerm_mssql_firewall_rule" "default" { - name = "terraform-firewall-rule" - server_id = azurerm_mssql_server.default.id - start_ip_address = local.public_ip - end_ip_address = local.public_ip -} - -resource "azurerm_mssql_firewall_rule" "azure_access" { - name = "allow-access-from-azure" - server_id = azurerm_mssql_server.default.id - start_ip_address = "0.0.0.0" - end_ip_address = "0.0.0.0" -} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/~inputs.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/~inputs.tf deleted file mode 100644 index e7dfef678a6..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/~inputs.tf +++ /dev/null @@ -1,11 +0,0 @@ -variable "common" {} -variable "key" {} -variable "sqladmins" {} -variable "databases" { - type = list(string) -} -variable "epool" {} -variable "admin_username" {} -variable "admin_password" { - sensitive = true -} diff --git a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/~outputs.tf b/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/~outputs.tf deleted file mode 100644 index ed913fbd2d3..00000000000 --- a/.github/actions/terraform-templates/azure/modules/azure_mssql/v2/~outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -output "id" { - value = azurerm_mssql_server.default.identity[0].principal_id -} - -output "fqdn" { - value = azurerm_mssql_server.default.fully_qualified_domain_name -} diff --git a/.github/actions/terraform-templates/azure/modules/cloudshell/main.tf b/.github/actions/terraform-templates/azure/modules/cloudshell/main.tf deleted file mode 100644 index 15bcd4295f7..00000000000 --- a/.github/actions/terraform-templates/azure/modules/cloudshell/main.tf +++ /dev/null @@ -1,162 +0,0 @@ -#================ Existing Resources ================ -data "azurerm_resource_group" "existing-rg" { - name = var.existing-vnet-resource-group -} - -data "azurerm_virtual_network" "existing-vnet" { - name = var.existing-vnet-name - resource_group_name = data.azurerm_resource_group.existing-rg.name -} - -#================ Subnets ================ -resource "azurerm_subnet" "container-subnet" { - name = var.container-subnet-name - address_prefixes = var.container-subnet-prefix - resource_group_name = data.azurerm_resource_group.existing-rg.name - virtual_network_name = data.azurerm_virtual_network.existing-vnet.name - service_endpoints = ["Microsoft.Storage"] - delegation { - name = "delegation" - service_delegation { - actions = ["Microsoft.Network/virtualNetworks/subnets/action"] - name = "Microsoft.ContainerInstance/containerGroups" - } - } -} - -resource "azurerm_subnet" "relay-subnet" { - name = var.relay-subnet-name - address_prefixes = var.relay-subnet-prefix - resource_group_name = data.azurerm_resource_group.existing-rg.name - virtual_network_name = data.azurerm_virtual_network.existing-vnet.name - enforce_private_link_endpoint_network_policies = true #true = Disable; false = Enable - enforce_private_link_service_network_policies = false #true = Disable; false = Enable - depends_on = [ - azurerm_subnet.container-subnet - ] -} - -#================ Network Profile ================ -resource "azurerm_network_profile" "network-profile" { - name = "aci-networkProfile-${data.azurerm_resource_group.existing-rg.location}" - resource_group_name = data.azurerm_resource_group.existing-rg.name - location = data.azurerm_resource_group.existing-rg.location - container_network_interface { - name = "eth-${azurerm_subnet.container-subnet.name}" - ip_configuration { - name = "ipconfig-${azurerm_subnet.container-subnet.name}" - subnet_id = azurerm_subnet.container-subnet.id - } - } - tags = var.tags - lifecycle { ignore_changes = [tags] } - depends_on = [ - azurerm_subnet.container-subnet - ] -} - -#================ Relay Namespace ================ -resource "azurerm_relay_namespace" "relay-namespace" { - name = var.relay-namespace-name # must be unique - resource_group_name = data.azurerm_resource_group.existing-rg.name - location = data.azurerm_resource_group.existing-rg.location - sku_name = "Standard" - tags = var.tags - lifecycle { ignore_changes = [tags] } -} - -#================ Role Assignments ================ -resource "random_uuid" "network" { -} - -resource "random_uuid" "contributor" { -} - -data "azurerm_role_definition" "networkRoleDefinition" { - role_definition_id = "4d97b98b-1d4f-4787-a291-c67834d212e7" -} - -data "azurerm_role_definition" "contributorRoleDefinition" { - role_definition_id = "b24988ac-6180-42a0-ab88-20f7382dd24c" -} - -resource "azurerm_role_assignment" "role-assignment-network" { - scope = azurerm_network_profile.network-profile.id - role_definition_name = data.azurerm_role_definition.networkRoleDefinition.name - principal_id = var.ACI-OID - depends_on = [ - azurerm_network_profile.network-profile - ] -} - -resource "azurerm_role_assignment" "role-assignment-contributor" { - scope = azurerm_relay_namespace.relay-namespace.id - role_definition_name = data.azurerm_role_definition.contributorRoleDefinition.name - principal_id = var.ACI-OID - depends_on = [ - azurerm_relay_namespace.relay-namespace - ] -} - -#================ Private Endpoints ================ -resource "azurerm_private_endpoint" "private-endpoint" { - name = var.private-endpoint-name - resource_group_name = data.azurerm_resource_group.existing-rg.name - location = data.azurerm_resource_group.existing-rg.location - subnet_id = azurerm_subnet.relay-subnet.id - private_service_connection { - name = "${data.azurerm_virtual_network.existing-vnet.location}-privsvc" # Max Length 80 characters - private_connection_resource_id = azurerm_relay_namespace.relay-namespace.id - is_manual_connection = false - subresource_names = ["namespace"] - } - tags = var.tags - lifecycle { ignore_changes = [tags] } - depends_on = [ - azurerm_relay_namespace.relay-namespace, - azurerm_subnet.relay-subnet - ] -} - -#================ Private DNS ================ -resource "azurerm_private_dns_zone" "global-private-dns-zone" { - name = "privatelink.servicebus.windows.net" - resource_group_name = data.azurerm_resource_group.existing-rg.name - tags = var.tags - lifecycle { ignore_changes = [tags] } -} - -resource "azurerm_private_dns_zone_virtual_network_link" "dns-zone-link" { - name = azurerm_relay_namespace.relay-namespace.name - resource_group_name = data.azurerm_resource_group.existing-rg.name - private_dns_zone_name = "privatelink.servicebus.windows.net" - virtual_network_id = data.azurerm_virtual_network.existing-vnet.id - depends_on = [azurerm_private_dns_zone.global-private-dns-zone] -} - -resource "azurerm_private_dns_a_record" "ussc-dns-a-record" { - name = azurerm_relay_namespace.relay-namespace.name - zone_name = azurerm_private_dns_zone.global-private-dns-zone.name - resource_group_name = data.azurerm_resource_group.existing-rg.name - ttl = 3600 - records = [cidrhost(var.relay-subnet-prefix[0], 4)] - depends_on = [azurerm_private_dns_zone.global-private-dns-zone] -} - -#================ Storage ================ -resource "azurerm_storage_account" "storageaccount" { - name = var.storageaccount-name - resource_group_name = data.azurerm_resource_group.existing-rg.name - location = data.azurerm_resource_group.existing-rg.location - account_tier = "Standard" - account_replication_type = "LRS" - min_tls_version = "TLS1_2" - allow_nested_items_to_be_public = false - network_rules { - default_action = "Deny" - virtual_network_subnet_ids = [azurerm_subnet.container-subnet.id] - } - - tags = merge(var.tags, { ms-resource-usage = "azure-cloud-shell" }) - lifecycle { ignore_changes = [tags] } -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/cloudshell/outputs.tf b/.github/actions/terraform-templates/azure/modules/cloudshell/outputs.tf deleted file mode 100644 index 9da5083260a..00000000000 --- a/.github/actions/terraform-templates/azure/modules/cloudshell/outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -output "container-subnet-id" { - value = azurerm_subnet.container-subnet.id -} - -output "relay-subnet-id" { - value = azurerm_subnet.relay-subnet.id -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/cloudshell/variables.tf b/.github/actions/terraform-templates/azure/modules/cloudshell/variables.tf deleted file mode 100644 index eefb31f075b..00000000000 --- a/.github/actions/terraform-templates/azure/modules/cloudshell/variables.tf +++ /dev/null @@ -1,65 +0,0 @@ -variable "existing-vnet-name" { - type = string - description = "the name of the existing virtual network" -} - -variable "existing-vnet-resource-group" { - type = string - description = "the name of the resource containing the existing virtual network" -} - -variable "relay-namespace-name" { - type = string - description = "the name to be assigned to the relay namespace" - default = "cshrelay" -} - -variable "ACI-OID" { - type = string - description = "Azure Container Instance OID" -} - -variable "container-subnet-name" { - type = string - description = "the name to be assigned to the cloudshell container subnet" - default = "cloudshellsubnet" -} - -variable "container-subnet-prefix" { - type = list(string) - description = "the list of address prefix(es) to be assigned to the cloudshell container subnet" -} - -variable "relay-subnet-name" { - type = string - description = "the name to be assigned to the relay subnet" - default = "relaysubnet" -} - -variable "relay-subnet-prefix" { - type = list(string) - description = "the list of address prefix(es) to be assigned to the relay subnet" -} - -variable "storage-subnet-name" { - type = string - description = "the name to be assigned to the storage subnet" - default = "storagesubnet" -} - -variable "storageaccount-name" { - type = string - description = "the name of the storage account to create" -} - -variable "private-endpoint-name" { - type = string - description = "the name to be assigned to the private endpoint" - default = "cloudshellRelayEndpoint" -} - -variable "tags" { - type = map(string) - description = "the list of tags to be assigned" - default = {} -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/container_app/main.tf b/.github/actions/terraform-templates/azure/modules/container_app/main.tf deleted file mode 100644 index 3bb6080b45b..00000000000 --- a/.github/actions/terraform-templates/azure/modules/container_app/main.tf +++ /dev/null @@ -1,59 +0,0 @@ -resource "azurerm_container_app_environment" "default" { - name = "capp-env-${var.key}" - location = var.common.location - resource_group_name = var.common.resource_group.name -} - -resource "azurerm_storage_share" "default" { - name = "capp-share-${var.key}" - quota = "200" - storage_account_name = var.storage_account.name -} - -resource "azurerm_container_app_environment_storage" "example" { - name = "capp-env-storage-${var.key}" - container_app_environment_id = azurerm_container_app_environment.default.id - account_name = var.storage_account.name - share_name = azurerm_storage_share.default.name - access_key = var.storage_account.primary_access_key - access_mode = "ReadWrite" -} - -resource "azurerm_container_app" "default" { - name = "capp-${var.key}" - container_app_environment_id = azurerm_container_app_environment.default.id - resource_group_name = var.common.resource_group.name - revision_mode = "Single" - - ingress { - allow_insecure_connections = true - target_port = 22 - transport = "auto" - traffic_weight { - latest_revision = true - percentage = 100 - } - } - template { - container { - name = "examplecontainerapp" - image = "mcr.microsoft.com/azure-dev-cli-apps:latest" - cpu = 1.0 - memory = "2Gi" - command = [ - "/bin/bash", - "-c", - "echo 'Hello, Azure Container App!' && sleep infinity" - ] - volume_mounts { - name = azurerm_storage_share.default.name - path = "/mnt/storage" - } - } - volume { - name = azurerm_storage_share.default.name - storage_name = azurerm_container_app_environment_storage.example.name - storage_type = "AzureFile" - } - } -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/container_app/~inputs.tf b/.github/actions/terraform-templates/azure/modules/container_app/~inputs.tf deleted file mode 100644 index 66fe143f1a6..00000000000 --- a/.github/actions/terraform-templates/azure/modules/container_app/~inputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -variable "common" {} -variable "key" {} -variable "storage_account" {} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/container_app/~outputs.tf b/.github/actions/terraform-templates/azure/modules/container_app/~outputs.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/.github/actions/terraform-templates/azure/modules/container_instance/main.tf b/.github/actions/terraform-templates/azure/modules/container_instance/main.tf deleted file mode 100644 index 4b4fa15a246..00000000000 --- a/.github/actions/terraform-templates/azure/modules/container_instance/main.tf +++ /dev/null @@ -1,58 +0,0 @@ -resource "azurerm_storage_share" "default" { - for_each = var.shares - - name = "cinst-share-${var.key}" - quota = each.value.gb - storage_account_name = var.storage_account.name - access_tier = each.value.tier -} - -resource "azurerm_container_group" "default" { - name = "cinst-${var.key}" - location = var.common.location - resource_group_name = var.common.resource_group.name - ip_address_type = "Public" - os_type = var.os_type - dns_name_label = "cinst-${var.key}" - - container { - name = "cinst-${var.key}" - image = var.image - cpu = var.cpu_cores - memory = var.mem_gb - commands = var.commands - - secure_environment_variables = { - USER_PASSWORD = var.user_password - } - - ports { - port = 2222 - protocol = "TCP" - } - - dynamic "volume" { - for_each = var.shares - content { - name = volume.key - storage_account_name = var.storage_account.name - storage_account_key = var.storage_account.primary_access_key - share_name = azurerm_storage_share.default[volume.key].name - read_only = false - mount_path = volume.value.mount_path - } - } - - dynamic "volume" { - for_each = var.repos - content { - name = volume.key - mount_path = volume.value.mount_path - git_repo { - url = volume.value.url - directory = "/" - } - } - } - } -} diff --git a/.github/actions/terraform-templates/azure/modules/container_instance/~inputs.tf b/.github/actions/terraform-templates/azure/modules/container_instance/~inputs.tf deleted file mode 100644 index 04345384e36..00000000000 --- a/.github/actions/terraform-templates/azure/modules/container_instance/~inputs.tf +++ /dev/null @@ -1,27 +0,0 @@ -variable "common" {} -variable "key" {} -variable "storage_account" { - default = null -} -variable "image" {} -variable "cpu_cores" {} -variable "mem_gb" {} -variable "commands" { - default = [] -} -variable "exec" { - default = "" -} -variable "repos" { - default = {} -} -variable "shares" { - default = {} -} -variable "os_type" { - default = "Linux" -} -variable "user_password" { - sensitive = true - default = null -} diff --git a/.github/actions/terraform-templates/azure/modules/container_instance/~outputs.tf b/.github/actions/terraform-templates/azure/modules/container_instance/~outputs.tf deleted file mode 100644 index fd6df497c9f..00000000000 --- a/.github/actions/terraform-templates/azure/modules/container_instance/~outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "exec" { - value = "az container exec --name ${azurerm_container_group.default.name} --resource-group ${var.common.resource_group.name} --container-name ${azurerm_container_group.default.name} ${var.exec != "" ? "--exec-command '${var.exec}'" : ""}" -} diff --git a/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/main.tf b/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/main.tf deleted file mode 100644 index f3c41207767..00000000000 --- a/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -resource "azurerm_cosmosdb_postgresql_cluster" "default" { - name = "cluster-${var.common.uid}-${var.common.env}-${var.key}" - location = var.common.location - resource_group_name = var.common.resource_group.name - administrator_login_password = var.admin_password - coordinator_storage_quota_in_mb = 262144 - coordinator_vcore_count = 4 - node_count = 0 -} diff --git a/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~inputs.tf b/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~inputs.tf deleted file mode 100644 index c86953cc18c..00000000000 --- a/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~inputs.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "common" {} -variable "key" {} -variable "admin_password" { - sensitive = true -} diff --git a/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~outputs.tf b/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~outputs.tf deleted file mode 100644 index f15b6a1bc12..00000000000 --- a/.github/actions/terraform-templates/azure/modules/cosmosdb_postgresql/v1/~outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "meta" { - value = azurerm_cosmosdb_postgresql_cluster.default -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/data_factory/v1/main.tf b/.github/actions/terraform-templates/azure/modules/data_factory/v1/main.tf deleted file mode 100644 index b4e4badc5f7..00000000000 --- a/.github/actions/terraform-templates/azure/modules/data_factory/v1/main.tf +++ /dev/null @@ -1,85 +0,0 @@ -resource "azurerm_data_factory" "default" { - name = "df-${var.common.uid}-${var.common.env}" - location = var.common.location - resource_group_name = var.common.resource_group.name - - identity { - type = "SystemAssigned" - } -} - -resource "azurerm_role_assignment" "df_blob" { - scope = var.common.resource_group.id - role_definition_name = "Storage Blob Data Contributor" - principal_id = azurerm_data_factory.default.identity[0].principal_id -} - -resource "azurerm_role_assignment" "df_kv" { - scope = var.common.resource_group.id - role_definition_name = "Key Vault Reader" - principal_id = azurerm_data_factory.default.identity[0].principal_id -} - -resource "azurerm_role_assignment" "df_kv_secrets" { - scope = var.common.resource_group.id - role_definition_name = "Key Vault Secrets User" - principal_id = azurerm_data_factory.default.identity[0].principal_id -} - -resource "azurerm_data_factory_linked_service_azure_blob_storage" "default" { - name = var.storage_account.name - data_factory_id = azurerm_data_factory.default.id - service_endpoint = var.storage_account.primary_blob_endpoint - use_managed_identity = true -} - -resource "azurerm_data_factory_linked_custom_service" "rest_default" { - name = "${var.common.env}-rest" - data_factory_id = azurerm_data_factory.default.id - description = "Managed by Terraform" - - type = "RestService" - type_properties_json = <<JSON -{ - "url": "${var.secrets["${var.common.env}-API-ENDPOINT"].value}", - "enableServerCertificateValidation": true, - "authenticationType": "Basic", - "userName": "${var.secrets["${var.common.env}-KEY"].value}", - "password": { - "type": "AzureKeyVaultSecret", - "store": { - "referenceName": "${azurerm_data_factory_linked_service_key_vault.default.name}", - "type": "LinkedServiceReference" - }, - "secretName": "${var.common.env}-SECRET" - } -} -JSON - - depends_on = [azurerm_role_assignment.df_kv_secrets] -} - -resource "azurerm_data_factory_linked_service_key_vault" "default" { - name = "${var.common.env}-kv" - data_factory_id = azurerm_data_factory.default.id - key_vault_id = var.key_vault.id -} - - -resource "azurerm_data_factory_custom_dataset" "rest_default" { - name = "${var.common.env}-rest" - data_factory_id = azurerm_data_factory.default.id - type = "RestResource" - - linked_service { - name = azurerm_data_factory_linked_custom_service.rest_default.name - } - - type_properties_json = <<JSON -{ - "relativeUrl": "${var.secrets["${var.common.env}-API-URL"].value}" -} -JSON - - description = "test description" -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/data_factory/v1/~inputs.tf b/.github/actions/terraform-templates/azure/modules/data_factory/v1/~inputs.tf deleted file mode 100644 index 50e95c75e4b..00000000000 --- a/.github/actions/terraform-templates/azure/modules/data_factory/v1/~inputs.tf +++ /dev/null @@ -1,4 +0,0 @@ -variable "common" {} -variable "storage_account" {} -variable "key_vault" {} -variable "secrets" {} diff --git a/.github/actions/terraform-templates/azure/modules/data_factory/v1/~outputs.tf b/.github/actions/terraform-templates/azure/modules/data_factory/v1/~outputs.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/.github/actions/terraform-templates/azure/modules/data_factory/v2/main.tf b/.github/actions/terraform-templates/azure/modules/data_factory/v2/main.tf deleted file mode 100644 index 6bee82a323b..00000000000 --- a/.github/actions/terraform-templates/azure/modules/data_factory/v2/main.tf +++ /dev/null @@ -1,17 +0,0 @@ -resource "azurerm_data_factory" "default" { - name = "df-${var.common.uid}-${var.common.env}-${var.key}" - location = var.common.location - resource_group_name = var.common.resource_group.name - - identity { - type = "SystemAssigned" - } -} - -resource "azurerm_role_assignment" "default" { - for_each = var.roles - - scope = var.common.resource_group.id - role_definition_name = each.value - principal_id = azurerm_data_factory.default.identity[0].principal_id -} diff --git a/.github/actions/terraform-templates/azure/modules/data_factory/v2/~inputs.tf b/.github/actions/terraform-templates/azure/modules/data_factory/v2/~inputs.tf deleted file mode 100644 index 4fedd304f9c..00000000000 --- a/.github/actions/terraform-templates/azure/modules/data_factory/v2/~inputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -variable "common" {} -variable "roles" {} -variable "key" {} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/data_factory/v2/~outputs.tf b/.github/actions/terraform-templates/azure/modules/data_factory/v2/~outputs.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/.github/actions/terraform-templates/azure/modules/dns_zone/main.tf b/.github/actions/terraform-templates/azure/modules/dns_zone/main.tf deleted file mode 100644 index c254ac97465..00000000000 --- a/.github/actions/terraform-templates/azure/modules/dns_zone/main.tf +++ /dev/null @@ -1,14 +0,0 @@ -resource "azurerm_private_dns_zone" "default" { - name = var.dns_name - resource_group_name = var.common.resource_group.name -} - -resource "azurerm_private_dns_zone_virtual_network_link" "default" { - for_each = var.dns_links - - name = "vnet-link-${each.value.vnet_key}-${var.key}" - resource_group_name = var.common.resource_group.name - private_dns_zone_name = azurerm_private_dns_zone.default.name - virtual_network_id = lookup(var.vnets, "vnet-${each.value.vnet_key}", "") - registration_enabled = each.value.registration_enabled -} diff --git a/.github/actions/terraform-templates/azure/modules/dns_zone/~inputs.tf b/.github/actions/terraform-templates/azure/modules/dns_zone/~inputs.tf deleted file mode 100644 index 9740cc56f82..00000000000 --- a/.github/actions/terraform-templates/azure/modules/dns_zone/~inputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -variable "common" {} -variable "key" {} -variable "dns_name" {} -variable "dns_links" {} -variable "vnets" { - //default = "" -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/dns_zone/~outputs.tf b/.github/actions/terraform-templates/azure/modules/dns_zone/~outputs.tf deleted file mode 100644 index d4d8f388958..00000000000 --- a/.github/actions/terraform-templates/azure/modules/dns_zone/~outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "meta" { - value = azurerm_private_dns_zone.default -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/init/v1/main.tf b/.github/actions/terraform-templates/azure/modules/init/v1/main.tf deleted file mode 100644 index 5edb227566f..00000000000 --- a/.github/actions/terraform-templates/azure/modules/init/v1/main.tf +++ /dev/null @@ -1,18 +0,0 @@ -resource "azurerm_role_assignment" "dev_roles" { - for_each = toset(var.dev_roles) - - scope = var.common.resource_group.id - role_definition_name = each.value - principal_id = var.common.owner.object_id -} - -resource "azuread_group" "sqladmins" { - display_name = "sqladmins-${var.common.env}" - owners = [var.common.owner.object_id] - security_enabled = true - - members = [ - var.common.owner.object_id, - /* more users */ - ] -} diff --git a/.github/actions/terraform-templates/azure/modules/init/v1/~inputs.tf b/.github/actions/terraform-templates/azure/modules/init/v1/~inputs.tf deleted file mode 100644 index 49d79042088..00000000000 --- a/.github/actions/terraform-templates/azure/modules/init/v1/~inputs.tf +++ /dev/null @@ -1,2 +0,0 @@ -variable "common" {} -variable "dev_roles" {} diff --git a/.github/actions/terraform-templates/azure/modules/init/v1/~outputs.tf b/.github/actions/terraform-templates/azure/modules/init/v1/~outputs.tf deleted file mode 100644 index a59c8b34b56..00000000000 --- a/.github/actions/terraform-templates/azure/modules/init/v1/~outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "sqladmins" { - value = azuread_group.sqladmins -} diff --git a/.github/actions/terraform-templates/azure/modules/init/v2/main.tf b/.github/actions/terraform-templates/azure/modules/init/v2/main.tf deleted file mode 100644 index 6db44742e3c..00000000000 --- a/.github/actions/terraform-templates/azure/modules/init/v2/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "azurerm_role_assignment" "dev_roles" { - for_each = toset(var.dev_roles) - - scope = var.common.resource_group.id - role_definition_name = each.value - principal_id = var.common.owner.object_id -} diff --git a/.github/actions/terraform-templates/azure/modules/init/v2/~inputs.tf b/.github/actions/terraform-templates/azure/modules/init/v2/~inputs.tf deleted file mode 100644 index 49d79042088..00000000000 --- a/.github/actions/terraform-templates/azure/modules/init/v2/~inputs.tf +++ /dev/null @@ -1,2 +0,0 @@ -variable "common" {} -variable "dev_roles" {} diff --git a/.github/actions/terraform-templates/azure/modules/init/v2/~outputs.tf b/.github/actions/terraform-templates/azure/modules/init/v2/~outputs.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/.github/actions/terraform-templates/azure/modules/key_vault/main.tf b/.github/actions/terraform-templates/azure/modules/key_vault/main.tf deleted file mode 100644 index bdc7d9cd964..00000000000 --- a/.github/actions/terraform-templates/azure/modules/key_vault/main.tf +++ /dev/null @@ -1,23 +0,0 @@ -resource "azurerm_key_vault" "default" { - name = "kv-${var.common.uid}-${var.common.env}-${var.key}" - location = var.common.location - resource_group_name = var.common.resource_group.name - tenant_id = var.common.tenant_id - sku_name = "standard" - soft_delete_retention_days = 7 - purge_protection_enabled = false - enable_rbac_authorization = true -} - -resource "azurerm_key_vault_secret" "default" { - for_each = var.secrets - - name = each.key - value = each.value.value - key_vault_id = azurerm_key_vault.default.id - - lifecycle { - ignore_changes = [value] - } -} - diff --git a/.github/actions/terraform-templates/azure/modules/key_vault/~inputs.tf b/.github/actions/terraform-templates/azure/modules/key_vault/~inputs.tf deleted file mode 100644 index 451440d1c74..00000000000 --- a/.github/actions/terraform-templates/azure/modules/key_vault/~inputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -variable "common" {} -variable "secrets" {} -variable "key" {} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/key_vault/~outputs.tf b/.github/actions/terraform-templates/azure/modules/key_vault/~outputs.tf deleted file mode 100644 index 07ab666933a..00000000000 --- a/.github/actions/terraform-templates/azure/modules/key_vault/~outputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -output "meta" { - value = azurerm_key_vault.default -} - -output "secrets" { - value = azurerm_key_vault_secret.default -} diff --git a/.github/actions/terraform-templates/azure/modules/logic_app/main.tf b/.github/actions/terraform-templates/azure/modules/logic_app/main.tf deleted file mode 100644 index 06a73c9fb1a..00000000000 --- a/.github/actions/terraform-templates/azure/modules/logic_app/main.tf +++ /dev/null @@ -1,110 +0,0 @@ -resource "azurerm_logic_app_workflow" "default" { - name = "la-${var.common.env}" - location = var.common.location - resource_group_name = var.common.resource_group.name - - identity { - type = "SystemAssigned" - } - - lifecycle { - ignore_changes = [parameters, workflow_parameters] - } -} - -data "azapi_resource" "logicapp_resource" { - depends_on = [azurerm_logic_app_workflow.default, azurerm_resource_group_template_deployment.la_db_workflow] - name = azurerm_logic_app_workflow.default.name - parent_id = var.common.resource_group.id - type = "Microsoft.Logic/workflows@2019-05-01" - - response_export_values = ["*"] -} - -resource "azurerm_logic_app_trigger_http_request" "default" { - name = "${var.common.env}-trigger" - logic_app_id = azurerm_logic_app_workflow.default.id - method = "POST" - - schema = <<SCHEMA -{ - "properties": { - "name": { - "type": "string" - } - }, - "type": "object" -} -SCHEMA - - depends_on = [azurerm_resource_group_template_deployment.la_db_workflow] - lifecycle { - replace_triggered_by = [azurerm_resource_group_template_deployment.la_db_workflow] - } -} - -data "template_file" "la_db_workflow" { - template = file("${path.module}/workflows/${var.workflow_file}") -} - -resource "azurerm_resource_group_template_deployment" "la_db_workflow" { - depends_on = [azurerm_logic_app_workflow.default] - - name = "la-${var.common.env}-workflow" - resource_group_name = var.common.resource_group.name - deployment_mode = "Incremental" - parameters_content = jsonencode({ - "workflows_la_name" = { - value = azurerm_logic_app_workflow.default.name - } - "location" = { - value = var.common.location - } - "sql_api_id" = { - value = data.azurerm_managed_api.sql.id - } - "sql_conn_id" = { - value = azapi_resource.sql.id - } - "sql_conn_name" = { - value = azapi_resource.sql.name - } - "sql_server_fqdn" = { - value = var.sql_server_fqdn - } - "db_name" = { - value = var.db_name - } - "query" = { - value = "SELECT * FROM sys.objects" - } - }) - - template_content = data.template_file.la_db_workflow.template -} - -data "azurerm_managed_api" "sql" { - name = "sql" - location = var.common.location -} - -resource "azapi_resource" "sql" { - type = "Microsoft.Web/connections@2016-06-01" - name = "sql-la-db" - parent_id = var.common.resource_group.id - location = var.common.location - - body = jsonencode({ - properties = { - api = { - id = data.azurerm_managed_api.sql.id - } - displayName = "sql-la-db" - parameterValueSet = { - name = "oauthMI" - values = {} - } - } - }) - schema_validation_enabled = false -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/logic_app/workflows/workflow.json b/.github/actions/terraform-templates/azure/modules/logic_app/workflows/workflow.json deleted file mode 100644 index 1ad66cf6eef..00000000000 --- a/.github/actions/terraform-templates/azure/modules/logic_app/workflows/workflow.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "workflows_la_name": { - "type": "String" - }, - "location":{ - "type": "String" - }, - "sql_api_id":{ - "type": "String" - }, - "sql_conn_id":{ - "type": "String" - }, - "sql_conn_name":{ - "type": "String" - }, - "sql_server_fqdn":{ - "type": "String" - }, - "db_name":{ - "type": "String" - }, - "query":{ - "type": "String" - } - }, - "variables": {}, - "resources": [ - { - "type": "Microsoft.Logic/workflows", - "apiVersion": "2017-07-01", - "name": "[parameters('workflows_la_name')]", - "location": "[parameters('location')]", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "state": "Enabled", - "definition": { - "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", - "actions": { - "Execute_a_SQL_query_(V2)": { - "inputs": { - "body": { - "query": "@parameters('sql_server')['query']" - }, - "host": { - "connection": { - "name": "@parameters('$connections')['sql']['connectionId']" - } - }, - "method": "post", - "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('sql_server')['fqdn']))},@{encodeURIComponent(encodeURIComponent(parameters('sql_server')['db']))}/query/sql" - }, - "runAfter": {}, - "type": "ApiConnection" - }, - "Response": { - "inputs": { - "body": "@body('Execute_a_SQL_query_(V2)')?['resultsets']?['Table1']", - "statusCode": 200 - }, - "kind": "Http", - "runAfter": { - "Execute_a_SQL_query_(V2)": [ - "Succeeded" - ] - }, - "type": "Response" - } - }, - "contentVersion": "1.0.0.0", - "parameters": { - "$connections": { - "defaultValue": {}, - "type": "Object" - }, - "sql_server": { - "defaultValue": {}, - "type": "Object" - } - }, - "triggers": {} - }, - "parameters": { - "$connections": { - "value": { - "sql": { - "connectionId": "[parameters('sql_conn_id')]", - "connectionName": "[parameters('sql_conn_name')]", - "connectionProperties": { - "authentication": { - "type": "ManagedServiceIdentity" - } - }, - "id": "[parameters('sql_api_id')]" - } - } - }, - "sql_server" : { - "value": { - "fqdn": "[parameters('sql_server_fqdn')]", - "db": "[parameters('db_name')]", - "query": "[parameters('query')]" - } - } - } - } - } - ] -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/logic_app/~inputs.tf b/.github/actions/terraform-templates/azure/modules/logic_app/~inputs.tf deleted file mode 100644 index 1bcccfe1a15..00000000000 --- a/.github/actions/terraform-templates/azure/modules/logic_app/~inputs.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "common" {} -variable "storage_account" {} -variable "workflow_file" {} -variable "sql_server_fqdn" {} -variable "db_name" {} diff --git a/.github/actions/terraform-templates/azure/modules/logic_app/~outputs.tf b/.github/actions/terraform-templates/azure/modules/logic_app/~outputs.tf deleted file mode 100644 index 43a8a35f2e4..00000000000 --- a/.github/actions/terraform-templates/azure/modules/logic_app/~outputs.tf +++ /dev/null @@ -1,16 +0,0 @@ -output "ids" { - value = azurerm_logic_app_workflow.default.identity[0] -} - -output "name" { - value = azurerm_logic_app_workflow.default.name -} - -output "meta" { - value = azurerm_logic_app_workflow.default -} - -output "endpoint" { - value = azurerm_logic_app_trigger_http_request.default.callback_url - sensitive = true -} diff --git a/.github/actions/terraform-templates/azure/modules/logic_app/~providers.tf b/.github/actions/terraform-templates/azure/modules/logic_app/~providers.tf deleted file mode 100644 index bb8af2ed8a1..00000000000 --- a/.github/actions/terraform-templates/azure/modules/logic_app/~providers.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - azapi = { - source = "Azure/azapi" - version = "1.12.1" - } - } -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/mssql_vm/main.tf b/.github/actions/terraform-templates/azure/modules/mssql_vm/main.tf deleted file mode 100644 index 79d46143ebd..00000000000 --- a/.github/actions/terraform-templates/azure/modules/mssql_vm/main.tf +++ /dev/null @@ -1,254 +0,0 @@ -data "http" "icanhazip" { - url = "http://icanhazip.com" -} - -locals { - public_ip = chomp(data.http.icanhazip.response_body) -} - -resource "azurerm_virtual_network" "example" { - name = "${var.key}-VN" - address_space = ["10.1.0.0/16"] - location = var.common.location - resource_group_name = var.common.resource_group.name - - dns_servers = var.ad_dns_ips -} - -resource "azurerm_network_security_group" "example" { - name = "${var.key}-NSG" - location = var.common.location - resource_group_name = var.common.resource_group.name -} - -resource "azurerm_subnet" "example" { - name = "${var.key}-SN" - resource_group_name = var.common.resource_group.name - virtual_network_name = azurerm_virtual_network.example.name - address_prefixes = ["10.1.0.0/24"] -} - -/*resource "azurerm_subnet_network_security_group_association" "example" { - subnet_id = azurerm_subnet.example.id - network_security_group_id = azurerm_network_security_group.example.id -}*/ - -resource "azurerm_public_ip" "vm" { - for_each = toset([ "vm1", "vm2" ]) - - name = "${var.key}-${each.key}-PIP" - location = var.common.location - resource_group_name = var.common.resource_group.name - allocation_method = "Dynamic" -} - -resource "azurerm_public_ip" "lb" { - name = "${var.key}-lb-PIP" - location = var.common.location - resource_group_name = var.common.resource_group.name - allocation_method = "Dynamic" -} - -resource "azurerm_lb" "example" { - name = "TestLoadBalancer" - location = var.common.location - resource_group_name = var.common.resource_group.name - - frontend_ip_configuration { - name = "PublicIPAddress" - public_ip_address_id = azurerm_public_ip.lb.id - } -} - -resource "azurerm_network_security_rule" "RDPRule" { - name = "RDPRule" - resource_group_name = var.common.resource_group.name - priority = 1000 - direction = "Inbound" - access = "Allow" - protocol = "Tcp" - source_port_range = "*" - destination_port_range = 3389 - source_address_prefix = "${local.public_ip}" - destination_address_prefix = "*" - network_security_group_name = azurerm_network_security_group.example.name -} - -resource "azurerm_network_security_rule" "MSSQLRule" { - name = "MSSQLRule" - resource_group_name = var.common.resource_group.name - priority = 1001 - direction = "Inbound" - access = "Allow" - protocol = "Tcp" - source_port_range = "*" - destination_port_range = 1433 - source_address_prefix = "${local.public_ip}" - destination_address_prefix = "*" - network_security_group_name = azurerm_network_security_group.example.name -} - -resource "azurerm_network_interface" "example" { - for_each = toset([ "vm1", "vm2" ]) - - name = "${var.key}-${each.key}-NIC" - location = var.common.location - resource_group_name = var.common.resource_group.name - - ip_configuration { - name = "exampleconfiguration1" - subnet_id = azurerm_subnet.example.id - private_ip_address_allocation = "Dynamic" - public_ip_address_id = azurerm_public_ip.vm[each.key].id - } -} - -resource "azurerm_network_interface_security_group_association" "example" { - for_each = toset([ "vm1", "vm2" ]) - - network_interface_id = azurerm_network_interface.example[each.key].id - network_security_group_id = azurerm_network_security_group.example.id -} - -resource "azurerm_windows_virtual_machine" "example" { - for_each = toset([ "vm1", "vm2" ]) - - name = "${var.key}-${each.key}-VM" - location = var.common.location - resource_group_name = var.common.resource_group.name - network_interface_ids = [azurerm_network_interface.example[each.key].id] - size = "Standard_B2s" - provision_vm_agent = true - enable_automatic_updates = true - - admin_username = "exampleadmin" - admin_password = "Password1234!" - - source_image_reference { - publisher = "MicrosoftSQLServer" - offer = "SQL2022-WS2022" #"SQL2017-WS2016" - sku = "sqldev-gen2" #"SQLDEV" - version = "latest" - } - - os_disk { - name = "${var.key}-${each.key}-OSDisk" - caching = "ReadWrite" - storage_account_type = "Standard_LRS" - } - -} - -// Waits for up to 1 hour for the Domain to become available. Will return an error 1 if unsuccessful preventing the member attempting to join. -// todo - find out why this is so variable? (approx 40min during testing) - -resource "azurerm_virtual_machine_extension" "wait-for-domain-to-provision" { - for_each = toset([ "vm1", "vm2" ]) - - name = "TestConnectionDomain" - publisher = "Microsoft.Compute" - type = "CustomScriptExtension" - type_handler_version = "1.9" - virtual_machine_id = azurerm_windows_virtual_machine.example[each.key].id - settings = <<SETTINGS - { - "commandToExecute": "powershell.exe -Command \"while (!(Test-Connection -ComputerName ${var.domain_name} -Count 1 -Quiet) -and ($retryCount++ -le 360)) { Start-Sleep 10 } \"" - } -SETTINGS -} - -resource "azurerm_virtual_machine_extension" "join-domain" { - for_each = toset([ "vm1", "vm2" ]) - - name = azurerm_windows_virtual_machine.example[each.key].name - publisher = "Microsoft.Compute" - type = "JsonADDomainExtension" - type_handler_version = "1.3" - virtual_machine_id = azurerm_windows_virtual_machine.example[each.key].id - - settings = <<SETTINGS - { - "Name": "${var.domain_name}", - "OUPath": "", - "User": "${var.ad_username}@${var.domain_name}", - "Restart": "true", - "Options": "3" - } -SETTINGS - - protected_settings = <<SETTINGS - { - "Password": "${var.ad_password}" - } -SETTINGS - - depends_on = [azurerm_virtual_machine_extension.wait-for-domain-to-provision] -} - -resource "azurerm_mssql_virtual_machine_group" "example" { - name = "examplegroup" - resource_group_name = var.common.resource_group.name - location = var.common.location - - sql_image_offer = "SQL2022-WS2022" #"SQL2017-WS2016" - sql_image_sku = "Developer" - - wsfc_domain_profile { - fqdn = var.domain_name - cluster_bootstrap_account_name = "install@${var.domain_name}" - cluster_operator_account_name = "install@${var.domain_name}" - sql_service_account_name = "sqlsvc@${var.domain_name}" - cluster_subnet_type = "SingleSubnet" - } -} - -resource "azurerm_mssql_virtual_machine" "example" { - for_each = toset([ "vm1", "vm2" ]) - - virtual_machine_id = azurerm_windows_virtual_machine.example[each.key].id - sql_license_type = "PAYG" - sql_virtual_machine_group_id = azurerm_mssql_virtual_machine_group.example.id - - wsfc_domain_credential { - cluster_bootstrap_account_password = "P@ssw0rd1234!" - cluster_operator_account_password = "P@ssw0rd1234!" - sql_service_account_password = "P@ssw0rd1234!" - } - - depends_on = [ azurerm_virtual_machine_extension.join-domain ] -} - -resource "azurerm_mssql_virtual_machine_availability_group_listener" "example" { - name = "listener1" - availability_group_name = "availabilitygroup1" - port = 1433 - sql_virtual_machine_group_id = azurerm_mssql_virtual_machine_group.example.id - - load_balancer_configuration { - load_balancer_id = azurerm_lb.example.id - private_ip_address = "10.1.0.11" - probe_port = 51572 - subnet_id = azurerm_subnet.example.id - - sql_virtual_machine_ids = [ - azurerm_mssql_virtual_machine.example["vm1"].id, - azurerm_mssql_virtual_machine.example["vm2"].id - ] - } - - replica { - sql_virtual_machine_id = azurerm_mssql_virtual_machine.example["vm1"].id - role = "Primary" - commit = "Synchronous_Commit" - failover_mode = "Automatic" - readable_secondary = "All" - } - - replica { - sql_virtual_machine_id = azurerm_mssql_virtual_machine.example["vm2"].id - role = "Secondary" - commit = "Asynchronous_Commit" - failover_mode = "Manual" - readable_secondary = "No" - } -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/mssql_vm/~inputs.tf b/.github/actions/terraform-templates/azure/modules/mssql_vm/~inputs.tf deleted file mode 100644 index df9516d9ec1..00000000000 --- a/.github/actions/terraform-templates/azure/modules/mssql_vm/~inputs.tf +++ /dev/null @@ -1,12 +0,0 @@ -variable "common" {} -variable "key" {} -variable "ad_dns_ips" { - default = [] -} -variable "domain_name" {} -variable "ad_username" { - sensitive = true -} -variable "ad_password" { - sensitive = true -} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/private_endpoint/main.tf b/.github/actions/terraform-templates/azure/modules/private_endpoint/main.tf deleted file mode 100644 index ed8d57322e4..00000000000 --- a/.github/actions/terraform-templates/azure/modules/private_endpoint/main.tf +++ /dev/null @@ -1,18 +0,0 @@ -resource "azurerm_private_endpoint" "default" { - name = "pe-${var.key}" - location = var.common.location - resource_group_name = var.common.resource_group.name - subnet_id = var.subnet_id - - private_dns_zone_group { - name = "pe-${var.key}-dns-group" - private_dns_zone_ids = var.dns_zone_ids - } - - private_service_connection { - name = "pe-${var.key}-sc" - private_connection_resource_id = var.resource_id - subresource_names = var.subresource_names - is_manual_connection = var.is_manual_connection - } -} diff --git a/.github/actions/terraform-templates/azure/modules/private_endpoint/~inputs.tf b/.github/actions/terraform-templates/azure/modules/private_endpoint/~inputs.tf deleted file mode 100644 index a175d0a48e3..00000000000 --- a/.github/actions/terraform-templates/azure/modules/private_endpoint/~inputs.tf +++ /dev/null @@ -1,7 +0,0 @@ -variable "common" {} -variable "key" {} -variable "dns_zone_ids" {} -variable "resource_id" {} -variable "subresource_names" {} -variable "is_manual_connection" {} -variable "subnet_id" {} diff --git a/.github/actions/terraform-templates/azure/modules/private_endpoint/~outputs.tf b/.github/actions/terraform-templates/azure/modules/private_endpoint/~outputs.tf deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/.github/actions/terraform-templates/azure/modules/storage_account/v1/main.tf b/.github/actions/terraform-templates/azure/modules/storage_account/v1/main.tf deleted file mode 100644 index 638422a982f..00000000000 --- a/.github/actions/terraform-templates/azure/modules/storage_account/v1/main.tf +++ /dev/null @@ -1,26 +0,0 @@ -resource "azurerm_storage_account" "default" { - name = "sa${var.common.env}${var.common.uid}data" - location = var.common.location - resource_group_name = var.common.resource_group.name - account_tier = "Standard" - account_replication_type = "LRS" - public_network_access_enabled = true - is_hns_enabled = true - allow_nested_items_to_be_public = false - - identity { - type = "SystemAssigned" - } -} - -resource "azurerm_storage_container" "default" { - name = "container-${var.common.env}" - storage_account_name = azurerm_storage_account.default.name - container_access_type = "private" -} - -resource "azurerm_role_assignment" "la_azure_db" { - scope = azurerm_storage_account.default.id - role_definition_name = "Storage Blob Data Contributor" - principal_id = var.logic_app.principal_id -} diff --git a/.github/actions/terraform-templates/azure/modules/storage_account/v1/~inputs.tf b/.github/actions/terraform-templates/azure/modules/storage_account/v1/~inputs.tf deleted file mode 100644 index cc82b60e6f5..00000000000 --- a/.github/actions/terraform-templates/azure/modules/storage_account/v1/~inputs.tf +++ /dev/null @@ -1,2 +0,0 @@ -variable "common" {} -variable "logic_app" {} \ No newline at end of file diff --git a/.github/actions/terraform-templates/azure/modules/storage_account/v1/~outputs.tf b/.github/actions/terraform-templates/azure/modules/storage_account/v1/~outputs.tf deleted file mode 100644 index f2c3c043105..00000000000 --- a/.github/actions/terraform-templates/azure/modules/storage_account/v1/~outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "meta" { - value = azurerm_storage_account.default -} diff --git a/.github/actions/terraform-templates/azure/modules/storage_account/v2/main.tf b/.github/actions/terraform-templates/azure/modules/storage_account/v2/main.tf deleted file mode 100644 index 38b853678ef..00000000000 --- a/.github/actions/terraform-templates/azure/modules/storage_account/v2/main.tf +++ /dev/null @@ -1,15 +0,0 @@ -resource "azurerm_storage_account" "default" { - name = "sa${var.common.env}${var.common.uid}${var.key}" - location = var.common.location - resource_group_name = var.common.resource_group.name - account_tier = var.account_tier - account_replication_type = "LRS" - public_network_access_enabled = true - is_hns_enabled = false - allow_nested_items_to_be_public = false - account_kind = var.account_kind - - identity { - type = "SystemAssigned" - } -} diff --git a/.github/actions/terraform-templates/azure/modules/storage_account/v2/~inputs.tf b/.github/actions/terraform-templates/azure/modules/storage_account/v2/~inputs.tf deleted file mode 100644 index 2582fbde820..00000000000 --- a/.github/actions/terraform-templates/azure/modules/storage_account/v2/~inputs.tf +++ /dev/null @@ -1,8 +0,0 @@ -variable "common" {} -variable "key" {} -variable "account_tier" { - default = "Standard" -} -variable "account_kind" { - default = "StorageV2" -} diff --git a/.github/actions/terraform-templates/azure/modules/storage_account/v2/~outputs.tf b/.github/actions/terraform-templates/azure/modules/storage_account/v2/~outputs.tf deleted file mode 100644 index f2c3c043105..00000000000 --- a/.github/actions/terraform-templates/azure/modules/storage_account/v2/~outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "meta" { - value = azurerm_storage_account.default -} diff --git a/.github/actions/terraform-templates/azure/modules/vnet/main.tf b/.github/actions/terraform-templates/azure/modules/vnet/main.tf deleted file mode 100644 index c46b0fdc87c..00000000000 --- a/.github/actions/terraform-templates/azure/modules/vnet/main.tf +++ /dev/null @@ -1,22 +0,0 @@ -resource "azurerm_virtual_network" "default" { - name = "vnet-${var.key}" - location = var.common.location - resource_group_name = var.common.resource_group.name - address_space = var.address_space.value -} - -resource "azurerm_subnet" "default" { - for_each = var.subnets - - name = each.key - resource_group_name = var.common.resource_group.name - virtual_network_name = azurerm_virtual_network.default.name - address_prefixes = each.value.address_prefixes.value - - private_link_service_network_policies_enabled = each.value.link_service_policies - private_endpoint_network_policies_enabled = each.value.endpoint_policies - - lifecycle { - ignore_changes = [service_endpoints] - } -} diff --git a/.github/actions/terraform-templates/azure/modules/vnet/~inputs.tf b/.github/actions/terraform-templates/azure/modules/vnet/~inputs.tf deleted file mode 100644 index 5968f000891..00000000000 --- a/.github/actions/terraform-templates/azure/modules/vnet/~inputs.tf +++ /dev/null @@ -1,4 +0,0 @@ -variable "common" {} -variable "key" {} -variable "address_space" {} -variable "subnets" {} diff --git a/.github/actions/terraform-templates/azure/modules/vnet/~outputs.tf b/.github/actions/terraform-templates/azure/modules/vnet/~outputs.tf deleted file mode 100644 index eee7523ff33..00000000000 --- a/.github/actions/terraform-templates/azure/modules/vnet/~outputs.tf +++ /dev/null @@ -1,11 +0,0 @@ -output "name" { - value = azurerm_virtual_network.default.name -} - -output "meta" { - value = azurerm_virtual_network.default -} - -output "subnets" { - value = azurerm_subnet.default -} diff --git a/.github/actions/workflow-housekeeper/.github/dependabot.yml b/.github/actions/workflow-housekeeper/.github/dependabot.yml deleted file mode 100644 index 0d08e261a2a..00000000000 --- a/.github/actions/workflow-housekeeper/.github/dependabot.yml +++ /dev/null @@ -1,11 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file - -version: 2 -updates: - - package-ecosystem: "github-actions" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" diff --git a/.github/actions/workflow-housekeeper/.github/workflows/test_action.yml b/.github/actions/workflow-housekeeper/.github/workflows/test_action.yml deleted file mode 100644 index b63b8b8944c..00000000000 --- a/.github/actions/workflow-housekeeper/.github/workflows/test_action.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Test action - -on: - pull_request: - branches: - - main - push: - branches: - - main - - -jobs: - test-action: - runs-on: ubuntu-latest - steps: - - name: Check out repository - uses: actions/checkout@v4 - - name: Use local action - uses: ./ - id: local-action - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - ignore-branch-workflows: false - retention-time: '0 minutes' - retain-run-count: 2 - dry-run: false diff --git a/.github/actions/workflow-housekeeper/README.md b/.github/actions/workflow-housekeeper/README.md deleted file mode 100644 index 2bce1b74abe..00000000000 --- a/.github/actions/workflow-housekeeper/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Workflow Housekeeper - -Retain a time period or quantity of workflow runs. - -[](https://github.com/CDCgov/workflow-housekeeper/actions/workflows/test_action.yml) - -### Dependencies: - ->Change in repo: `Settings -> Actions -> General -> Workflow Permissions to allow read and write` - -## Inputs -```yml - ignore-branch-workflows: - description: 'Ignore runs from workflows currently in ./github/workflow' - required: false - retention-time: - description: 'Period of time to maintain history. E.g. "2 weeks", "3 days", etc.' - required: false - retain-run-count: - description: 'Number of latest runs to keep' - required: false - dry-run: - description: 'Only list runs pending deletion' - required: false -``` - -## Usage -```yml - - name: Checkout - uses: actions/checkout@v3 - - name: Run workflow housekeeper - uses: .github/actions/workflow-housekeeper - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -``` -or -```yml - - name: Checkout - uses: actions/checkout@v3 - - name: Run workflow housekeeper - uses: .github/actions/workflow-housekeeper - id: scan - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - ignore-branch-workflows: true - retention-time: '1 days' - retain-run-count: 1 - dry-run: false -``` - -## Generated summary -### ✨ Workflow Housekeeper ✨ - * .github/workflows/test_action.yml 4618840926 - * .github/workflows/test_action.yml 4618827035 diff --git a/.github/actions/workflow-housekeeper/action.yml b/.github/actions/workflow-housekeeper/action.yml deleted file mode 100644 index 796910b771c..00000000000 --- a/.github/actions/workflow-housekeeper/action.yml +++ /dev/null @@ -1,41 +0,0 @@ -# action.yml -name: 'Workflow Housekeeper' -description: 'Retain a time period or quantity of workflow runs.' -branding: - icon: 'trash-2' - color: 'red' -inputs: - ignore-branch-workflows: - description: 'Ignore runs from workflows currently in ./github/workflow' - required: false - retention-time: - description: 'Period of time to maintain history. E.g. "2 weeks", "3 days", etc.' - required: false - retain-run-count: - description: 'Number of latest runs to keep' - required: false - dry-run: - description: 'Only list runs pending deletion' - required: false -outputs: - housekeeping_output: - description: 'Output of housekeeping steps' - value: ${{ steps.local-action.outputs.housekeeping_output }} -runs: - using: "composite" - steps: - - name: Run local action - id: local-action - run: | - ${{ github.action_path }}/lib/housekeeper.sh \ - "${{ github.repository }}" \ - "${{ inputs.ignore-branch-workflows }}" \ - "${{ inputs.retention-time }}" \ - "${{ inputs.retain-run-count }}" \ - "${{ inputs.dry-run }}" - shell: bash - - name: Create summary - run: | - echo "### :sparkles: Workflow Housekeeper :sparkles:" >> $GITHUB_STEP_SUMMARY - echo -e "${{ steps.local-action.outputs.housekeeping_output }}" >> $GITHUB_STEP_SUMMARY - shell: bash diff --git a/.github/actions/workflow-housekeeper/lib/housekeeper.sh b/.github/actions/workflow-housekeeper/lib/housekeeper.sh deleted file mode 100644 index ff4bd6e84cc..00000000000 --- a/.github/actions/workflow-housekeeper/lib/housekeeper.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -# Init -repo="$1" -ignore_branch_workflows=$2 -retention_time=$3 -retain_run_count=$4 -dry_run=$5 - -# ignore_branch_workflows -if [[ -z $ignore_branch_workflows ]]; then - ignore_branch_workflows=false -fi -echo "ignore_branch_workflows: $ignore_branch_workflows" - -if [[ $ignore_branch_workflows = true ]]; then - files=$(ls -1 .github/workflows/ | sed 's/^/and .path != \".github\/workflows\//;s/$/\"/') -else - files="" -fi - -# retention_time -if [[ -z $retention_time ]]; then - retention_time="" -else - keep_date=$(date -d "$date -$retention_time" +%s) - keep_stmt="| select (.run_started_at | . == null or fromdateiso8601 < $keep_date)" -fi -echo "time_threshold: $retention_time" - -# retain_run_count -if [[ -z $retain_run_count ]]; then - retain_run_count=0 -fi -let retain_run_count2=retain_run_count*2 -echo "retain_run_count: $retain_run_count2" - -# dry_run -if [[ -z $dry_run ]]; then - dry_run=false -fi -echo "dry_run: $dry_run" - -# Build jq query -runs="repos/$repo/actions/runs" -query=".workflow_runs[] \ -| select( \ -.path != \".github/workflows/placeholder.yaml\" \ -"$files" -) -$keep_stmt -| (.path,.id)" - -# Get run ids -output=$(gh api --paginate $runs --jq "$query") -output=($(echo $output | tr " " "\n")) -output=${output[@]:$retain_run_count2} - -# Delete or echo run ids -for id in $output; do - if [[ $dry_run = false ]]; then - [[ $id != ".git"* ]] && gh api --silent $runs/$id -X DELETE - else - [[ $id != ".git"* ]] && echo "gh api --silent $runs/$id -X DELETE" || echo "$id" - fi - [[ $id = ".git"* ]] && summary+=" * $id" || summary+=" $id\n" - - # Prevent rate limit - sleep 1; -done - -echo "housekeeping_output=$(echo "${summary}")" >>$GITHUB_OUTPUT From 80e3ee762145757741c040e8c41191d467cd86da Mon Sep 17 00:00:00 2001 From: Eduardo Valdes <eduardo.valdes@aquia.io> Date: Mon, 21 Oct 2024 21:11:13 -0700 Subject: [PATCH 25/32] Customizing this GitHub Action as an internal feature --- .../.github/dependabot.yml | 6 --- .../action-connect-ovpn/.github/vpn/test.ovpn | 42 ------------------- .../.github => }/vpn/config.ovpn | 0 .../.github => }/workflows/check_vpn.yml | 4 +- 4 files changed, 2 insertions(+), 50 deletions(-) delete mode 100644 .github/actions/action-connect-ovpn/.github/dependabot.yml delete mode 100644 .github/actions/action-connect-ovpn/.github/vpn/test.ovpn rename .github/{actions/action-connect-ovpn/.github => }/vpn/config.ovpn (100%) rename .github/{actions/action-connect-ovpn/.github => }/workflows/check_vpn.yml (91%) diff --git a/.github/actions/action-connect-ovpn/.github/dependabot.yml b/.github/actions/action-connect-ovpn/.github/dependabot.yml deleted file mode 100644 index 123014908be..00000000000 --- a/.github/actions/action-connect-ovpn/.github/dependabot.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "daily" diff --git a/.github/actions/action-connect-ovpn/.github/vpn/test.ovpn b/.github/actions/action-connect-ovpn/.github/vpn/test.ovpn deleted file mode 100644 index b236608121f..00000000000 --- a/.github/actions/action-connect-ovpn/.github/vpn/test.ovpn +++ /dev/null @@ -1,42 +0,0 @@ -client -remote azuregateway-d1ad0077-4117-45f0-b6fb-3989f1dd1945-a2e7e46fde0f.vpn.azure.com 443 -verify-x509-name 'd1ad0077-4117-45f0-b6fb-3989f1dd1945.vpn.azure.com' name -remote-cert-tls server - -dev tun -proto tcp -resolv-retry infinite -nobind - -auth SHA256 -cipher AES-256-GCM -persist-key -persist-tun - -tls-timeout 30 -tls-version-min 1.2 -key-direction 1 - -dhcp-option DNS 10.0.2.4 -dhcp-option DOMAIN azure.net -dhcp-option DOMAIN azure.com -dhcp-option DOMAIN azurewebsites.net -dhcp-option DOMAIN windows.net - -verb 3 - -# P2S CA root certificate -ca ca.crt - -# Pre Shared Key -tls-auth tls.key - -# P2S client certificate -# Please fill this field with a PEM formatted client certificate -# Alternatively, configure 'cert PATH_TO_CLIENT_CERT' to use input from a PEM certificate file. -cert user.crt - -# P2S client certificate private key -# Please fill this field with a PEM formatted private key of the client certificate. -# Alternatively, configure 'key PATH_TO_CLIENT_KEY' to use input from a PEM key file. -key user.key diff --git a/.github/actions/action-connect-ovpn/.github/vpn/config.ovpn b/.github/vpn/config.ovpn similarity index 100% rename from .github/actions/action-connect-ovpn/.github/vpn/config.ovpn rename to .github/vpn/config.ovpn diff --git a/.github/actions/action-connect-ovpn/.github/workflows/check_vpn.yml b/.github/workflows/check_vpn.yml similarity index 91% rename from .github/actions/action-connect-ovpn/.github/workflows/check_vpn.yml rename to .github/workflows/check_vpn.yml index 3b63e1895bb..ecca0120506 100644 --- a/.github/actions/action-connect-ovpn/.github/workflows/check_vpn.yml +++ b/.github/workflows/check_vpn.yml @@ -1,6 +1,6 @@ name: Check connect vpn -on: pull_request +on: pull_request jobs: check-vpn: @@ -10,7 +10,7 @@ jobs: - name: Install Open VPN run: sudo apt-get install openvpn - name: Connect VPN - uses: ./ + uses: ./.github/actions/action-connect-ovpn id: connect_vpn with: PING_URL: '${{ secrets.PING_URL }}' From 7a6ee08e8322941bccf332477b0c98c0252b2e40 Mon Sep 17 00:00:00 2001 From: Eduardo Valdes <eduardo.valdes@aquia.io> Date: Mon, 21 Oct 2024 21:37:54 -0700 Subject: [PATCH 26/32] Masking action-result to avoid breaking build --- .github/workflows/check_vpn.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_vpn.yml b/.github/workflows/check_vpn.yml index ecca0120506..f5585ac2e15 100644 --- a/.github/workflows/check_vpn.yml +++ b/.github/workflows/check_vpn.yml @@ -26,4 +26,4 @@ jobs: run: echo "$STATUS" - name: kill vpn if: always() - run: sudo killall openvpn + run: sudo killall openvpn 2>&>/dev/null From f6900d684b97c72ac4d4d33eb94923b86efe493c Mon Sep 17 00:00:00 2001 From: Eduardo Valdes <eduardo.valdes@aquia.io> Date: Mon, 21 Oct 2024 21:42:23 -0700 Subject: [PATCH 27/32] Masking action-result to avoid breaking build --- .github/workflows/check_vpn.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_vpn.yml b/.github/workflows/check_vpn.yml index f5585ac2e15..acad9f8e8e3 100644 --- a/.github/workflows/check_vpn.yml +++ b/.github/workflows/check_vpn.yml @@ -26,4 +26,4 @@ jobs: run: echo "$STATUS" - name: kill vpn if: always() - run: sudo killall openvpn 2>&>/dev/null + run: sudo killall openvpn 2>&1>/dev/null From 3419c9e0d622c5085e1a6467686bfc9a19f5f7db Mon Sep 17 00:00:00 2001 From: Eduardo Valdes <eduardo.valdes@aquia.io> Date: Mon, 21 Oct 2024 21:44:54 -0700 Subject: [PATCH 28/32] Masking action-result to avoid breaking build --- .github/workflows/check_vpn.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check_vpn.yml b/.github/workflows/check_vpn.yml index acad9f8e8e3..c78bce62889 100644 --- a/.github/workflows/check_vpn.yml +++ b/.github/workflows/check_vpn.yml @@ -24,6 +24,6 @@ jobs: env: STATUS: ${{ steps.connect_vpn.outputs.STATUS }} run: echo "$STATUS" - - name: kill vpn - if: always() - run: sudo killall openvpn 2>&1>/dev/null + # - name: kill vpn + # if: always() + # run: sudo killall openvpn From 09cabfcbc63e20d9c31db97e1f30fb7282acdf86 Mon Sep 17 00:00:00 2001 From: Eduardo Valdes <eduardo.valdes@aquia.io> Date: Mon, 21 Oct 2024 21:47:55 -0700 Subject: [PATCH 29/32] Configuring daily schedule package-ecosystem for action-connect-ovpn --- .github/dependabot.yml | 54 +----------------------------------------- 1 file changed, 1 insertion(+), 53 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ea193a27f3a..895288adfc6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -121,62 +121,10 @@ updates: interval: "daily" ## Importing Josiah Siegel's external/remote GitHub Actions - - package-ecosystem: "github-actions" - directory: "/.github/actions/azviz-action" - schedule: - interval: "daily" - package-ecosystem: "github-actions" directory: "/.github/actions/action-connect-ovpn" schedule: interval: "daily" - - package-ecosystem: "github-actions" - directory: "/.github/actions/checksum-validate-action" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/.github/actions/git-secrets" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/.github/actions/randomrepo" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/.github/actions/rapid-wsl" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/.github/actions/reliable-pull-request-action" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/.github/actions/remote-branch-action" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/.github/actions/runleaks" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/.github/actions/slack-boltjs-app" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/.github/actions/stackoverflow_in_pg" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/.github/actions/terraform-stats" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/.github/actions/terraform-templates" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/.github/actions/workflow-housekeeper" - schedule: - interval: "daily" # Frontend - package-ecosystem: "npm" @@ -392,7 +340,7 @@ updates: time: "04:17" timezone: "US/Eastern" rebase-strategy: "disabled" - + # Backend - package-ecosystem: "gradle" directory: "/prime-router" From 09fb31a9d239f509f38712b76104e35de4b10ab5 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 22 Oct 2024 13:17:37 -0700 Subject: [PATCH 30/32] Updating the markers with a Corporate email account for clarity --- .github/actions/deploy-backend/action.yml | 2 +- .github/actions/vpn-azure/action.yml | 2 +- .github/workflows/alert_terraform_changes.yml | 2 +- .github/workflows/deploy_terraform.yml | 2 +- .github/workflows/log_management.yml | 4 ++-- .github/workflows/prepare_deployment_branch.yaml | 4 ++-- .github/workflows/release_to_azure.yml | 2 +- .github/workflows/scan_action_logs.yml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/actions/deploy-backend/action.yml b/.github/actions/deploy-backend/action.yml index de808a918d8..cd614f83d0b 100644 --- a/.github/actions/deploy-backend/action.yml +++ b/.github/actions/deploy-backend/action.yml @@ -334,7 +334,7 @@ runs: if: inputs.checksum-validation == 'true' uses: JosiahSiegel/checksum-validate-action@ebdf8c12c00912d18de93c483b935d51582f9236 - ## emvaldes@hotmail.com (Replace) uses: ./.github/actions/checksum-validate-action + ## eduardo.valdes@aquia.io (Replace) uses: ./.github/actions/checksum-validate-action with: key: backend diff --git a/.github/actions/vpn-azure/action.yml b/.github/actions/vpn-azure/action.yml index 5c9aaf5f784..f96f288e382 100644 --- a/.github/actions/vpn-azure/action.yml +++ b/.github/actions/vpn-azure/action.yml @@ -41,7 +41,7 @@ runs: shell: bash - uses: josiahsiegel/action-connect-ovpn@794339aff94452216c97f609476c367a43a31295 - ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/action-connect-ovpn + ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/action-connect-ovpn if: inputs.env-name && inputs.ca-cert != 'false' id: connect_vpn diff --git a/.github/workflows/alert_terraform_changes.yml b/.github/workflows/alert_terraform_changes.yml index c551ec265fc..8cc6804d5a1 100644 --- a/.github/workflows/alert_terraform_changes.yml +++ b/.github/workflows/alert_terraform_changes.yml @@ -30,7 +30,7 @@ jobs: - name: Collect Terraform stats uses: josiahsiegel/terraform-stats@68b8cbe42c494333fbf6f8d90ac86da1fb69dcc2 - ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/terraform-stats + ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/terraform-stats id: stats1 with: diff --git a/.github/workflows/deploy_terraform.yml b/.github/workflows/deploy_terraform.yml index 1d288da62db..12cbbb7ca55 100644 --- a/.github/workflows/deploy_terraform.yml +++ b/.github/workflows/deploy_terraform.yml @@ -53,7 +53,7 @@ jobs: - name: Collect Terraform stats uses: josiahsiegel/terraform-stats@68b8cbe42c494333fbf6f8d90ac86da1fb69dcc2 - ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/terraform-stats + ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/terraform-stats id: stats1 with: diff --git a/.github/workflows/log_management.yml b/.github/workflows/log_management.yml index 88685e96f40..359e2d40d4a 100644 --- a/.github/workflows/log_management.yml +++ b/.github/workflows/log_management.yml @@ -14,7 +14,7 @@ jobs: - name: Workflow Housekeeper - workflows NOT in default branch uses: JosiahSiegel/workflow-housekeeper@731cc20bb613208b34efb6ac74aab4ba147abb50 - ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/workflow-housekeeper + ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/workflow-housekeeper env: GITHUB_TOKEN: ${{ secrets.LOG_MANAGEMENT_TOKEN }} @@ -26,7 +26,7 @@ jobs: - name: Workflow Housekeeper - workflows in default branch uses: JosiahSiegel/workflow-housekeeper@731cc20bb613208b34efb6ac74aab4ba147abb50 - ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/workflow-housekeeper + ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/workflow-housekeeper env: GITHUB_TOKEN: ${{ secrets.LOG_MANAGEMENT_TOKEN }} diff --git a/.github/workflows/prepare_deployment_branch.yaml b/.github/workflows/prepare_deployment_branch.yaml index df0f8ed61db..3f4d110415f 100644 --- a/.github/workflows/prepare_deployment_branch.yaml +++ b/.github/workflows/prepare_deployment_branch.yaml @@ -29,7 +29,7 @@ jobs: - name: "Create branch '${{ env.BRANCH_NAME }}' to contain the changes for the deployment on ${{ env.DEPLOYMENT_DATE }}" uses: JosiahSiegel/remote-branch-action@dbe7a2138eb064fbfdb980abee918091a7501fbe - ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/remote-branch-action + ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/remote-branch-action with: branch: "${{ env.BRANCH_NAME }}" @@ -38,7 +38,7 @@ jobs: id: pr uses: JosiahSiegel/reliable-pull-request-action@ae8d0c88126329ee363a35392793d0bc94cb82e7 - ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/reliable-pull-request-action + ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/reliable-pull-request-action env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release_to_azure.yml b/.github/workflows/release_to_azure.yml index f8438d4ee0a..6c640f7141d 100644 --- a/.github/workflows/release_to_azure.yml +++ b/.github/workflows/release_to_azure.yml @@ -146,7 +146,7 @@ jobs: if: needs.pre_job.outputs.has_router_change == 'true' && env.checksum_validation == 'true' uses: JosiahSiegel/checksum-validate-action@ebdf8c12c00912d18de93c483b935d51582f9236 - ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/checksum-validate-action + ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/checksum-validate-action with: key: backend diff --git a/.github/workflows/scan_action_logs.yml b/.github/workflows/scan_action_logs.yml index 687f4ea94dd..294ffc53a02 100644 --- a/.github/workflows/scan_action_logs.yml +++ b/.github/workflows/scan_action_logs.yml @@ -14,7 +14,7 @@ jobs: - name: Scan run logs uses: josiahsiegel/runleaks@4dd30d107c03b6ade87978e10c94a77015e488f9 - ## emvaldes@hotmail.com (Replace) - uses: ./.github/actions/runleaks + ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/runleaks id: scan with: From 0604e2412117d7e0da09e07dc6612a020db1e557 Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 22 Oct 2024 13:28:39 -0700 Subject: [PATCH 31/32] Finalizing some changes based on Matt's recommendations --- .github/dependabot.yml | 1 - .github/vpn/config.ovpn | 2 +- .github/workflows/check_vpn.yml | 29 ----------------------------- 3 files changed, 1 insertion(+), 31 deletions(-) delete mode 100644 .github/workflows/check_vpn.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 895288adfc6..9222d7f6686 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -120,7 +120,6 @@ updates: schedule: interval: "daily" - ## Importing Josiah Siegel's external/remote GitHub Actions - package-ecosystem: "github-actions" directory: "/.github/actions/action-connect-ovpn" schedule: diff --git a/.github/vpn/config.ovpn b/.github/vpn/config.ovpn index 01f03eceeea..7a71e54d8c6 100644 --- a/.github/vpn/config.ovpn +++ b/.github/vpn/config.ovpn @@ -18,4 +18,4 @@ verb 3 reneg-sec 0 ca ca.crt cert user.crt -key user.key \ No newline at end of file +key user.key diff --git a/.github/workflows/check_vpn.yml b/.github/workflows/check_vpn.yml deleted file mode 100644 index c78bce62889..00000000000 --- a/.github/workflows/check_vpn.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Check connect vpn - -on: pull_request - -jobs: - check-vpn: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install Open VPN - run: sudo apt-get install openvpn - - name: Connect VPN - uses: ./.github/actions/action-connect-ovpn - id: connect_vpn - with: - PING_URL: '${{ secrets.PING_URL }}' - FILE_OVPN: '.github/vpn/test.ovpn' - TLS_KEY: ${{ secrets.TLS_KEY }} - env: - CA_CRT: ${{ secrets.CA_CRT}} - USER_CRT: ${{ secrets.USER_CRT }} - USER_KEY: ${{ secrets.USER_KEY }} - - name: Check Connect VPN - env: - STATUS: ${{ steps.connect_vpn.outputs.STATUS }} - run: echo "$STATUS" - # - name: kill vpn - # if: always() - # run: sudo killall openvpn From 5af3c9c0405591f4cf4e9e1598a6a6f9b473d07b Mon Sep 17 00:00:00 2001 From: "Eduardo Valdes Jr." <emvaldes@hotmail.com> Date: Tue, 22 Oct 2024 14:41:24 -0700 Subject: [PATCH 32/32] Adjusted the markers to reflect a changed by the DevSecOps - Aquia team --- .github/actions/deploy-backend/action.yml | 2 +- .github/actions/vpn-azure/action.yml | 2 +- .github/workflows/alert_terraform_changes.yml | 2 +- .github/workflows/deploy_terraform.yml | 2 +- .github/workflows/log_management.yml | 4 ++-- .github/workflows/prepare_deployment_branch.yaml | 4 ++-- .github/workflows/release_to_azure.yml | 2 +- .github/workflows/scan_action_logs.yml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/actions/deploy-backend/action.yml b/.github/actions/deploy-backend/action.yml index cd614f83d0b..60c558eae6d 100644 --- a/.github/actions/deploy-backend/action.yml +++ b/.github/actions/deploy-backend/action.yml @@ -334,7 +334,7 @@ runs: if: inputs.checksum-validation == 'true' uses: JosiahSiegel/checksum-validate-action@ebdf8c12c00912d18de93c483b935d51582f9236 - ## eduardo.valdes@aquia.io (Replace) uses: ./.github/actions/checksum-validate-action + ## DevSecOps - Aquia (Replace) uses: ./.github/actions/checksum-validate-action with: key: backend diff --git a/.github/actions/vpn-azure/action.yml b/.github/actions/vpn-azure/action.yml index f96f288e382..7dd45a24d3e 100644 --- a/.github/actions/vpn-azure/action.yml +++ b/.github/actions/vpn-azure/action.yml @@ -41,7 +41,7 @@ runs: shell: bash - uses: josiahsiegel/action-connect-ovpn@794339aff94452216c97f609476c367a43a31295 - ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/action-connect-ovpn + ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/action-connect-ovpn if: inputs.env-name && inputs.ca-cert != 'false' id: connect_vpn diff --git a/.github/workflows/alert_terraform_changes.yml b/.github/workflows/alert_terraform_changes.yml index 8cc6804d5a1..c7730e3a01a 100644 --- a/.github/workflows/alert_terraform_changes.yml +++ b/.github/workflows/alert_terraform_changes.yml @@ -30,7 +30,7 @@ jobs: - name: Collect Terraform stats uses: josiahsiegel/terraform-stats@68b8cbe42c494333fbf6f8d90ac86da1fb69dcc2 - ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/terraform-stats + ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/terraform-stats id: stats1 with: diff --git a/.github/workflows/deploy_terraform.yml b/.github/workflows/deploy_terraform.yml index 12cbbb7ca55..a5ce59ce698 100644 --- a/.github/workflows/deploy_terraform.yml +++ b/.github/workflows/deploy_terraform.yml @@ -53,7 +53,7 @@ jobs: - name: Collect Terraform stats uses: josiahsiegel/terraform-stats@68b8cbe42c494333fbf6f8d90ac86da1fb69dcc2 - ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/terraform-stats + ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/terraform-stats id: stats1 with: diff --git a/.github/workflows/log_management.yml b/.github/workflows/log_management.yml index 359e2d40d4a..8774b3dadda 100644 --- a/.github/workflows/log_management.yml +++ b/.github/workflows/log_management.yml @@ -14,7 +14,7 @@ jobs: - name: Workflow Housekeeper - workflows NOT in default branch uses: JosiahSiegel/workflow-housekeeper@731cc20bb613208b34efb6ac74aab4ba147abb50 - ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/workflow-housekeeper + ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/workflow-housekeeper env: GITHUB_TOKEN: ${{ secrets.LOG_MANAGEMENT_TOKEN }} @@ -26,7 +26,7 @@ jobs: - name: Workflow Housekeeper - workflows in default branch uses: JosiahSiegel/workflow-housekeeper@731cc20bb613208b34efb6ac74aab4ba147abb50 - ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/workflow-housekeeper + ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/workflow-housekeeper env: GITHUB_TOKEN: ${{ secrets.LOG_MANAGEMENT_TOKEN }} diff --git a/.github/workflows/prepare_deployment_branch.yaml b/.github/workflows/prepare_deployment_branch.yaml index 3f4d110415f..01c35749047 100644 --- a/.github/workflows/prepare_deployment_branch.yaml +++ b/.github/workflows/prepare_deployment_branch.yaml @@ -29,7 +29,7 @@ jobs: - name: "Create branch '${{ env.BRANCH_NAME }}' to contain the changes for the deployment on ${{ env.DEPLOYMENT_DATE }}" uses: JosiahSiegel/remote-branch-action@dbe7a2138eb064fbfdb980abee918091a7501fbe - ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/remote-branch-action + ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/remote-branch-action with: branch: "${{ env.BRANCH_NAME }}" @@ -38,7 +38,7 @@ jobs: id: pr uses: JosiahSiegel/reliable-pull-request-action@ae8d0c88126329ee363a35392793d0bc94cb82e7 - ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/reliable-pull-request-action + ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/reliable-pull-request-action env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release_to_azure.yml b/.github/workflows/release_to_azure.yml index 6c640f7141d..0c22420eb31 100644 --- a/.github/workflows/release_to_azure.yml +++ b/.github/workflows/release_to_azure.yml @@ -146,7 +146,7 @@ jobs: if: needs.pre_job.outputs.has_router_change == 'true' && env.checksum_validation == 'true' uses: JosiahSiegel/checksum-validate-action@ebdf8c12c00912d18de93c483b935d51582f9236 - ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/checksum-validate-action + ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/checksum-validate-action with: key: backend diff --git a/.github/workflows/scan_action_logs.yml b/.github/workflows/scan_action_logs.yml index 294ffc53a02..a618bc92078 100644 --- a/.github/workflows/scan_action_logs.yml +++ b/.github/workflows/scan_action_logs.yml @@ -14,7 +14,7 @@ jobs: - name: Scan run logs uses: josiahsiegel/runleaks@4dd30d107c03b6ade87978e10c94a77015e488f9 - ## eduardo.valdes@aquia.io (Replace) - uses: ./.github/actions/runleaks + ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/runleaks id: scan with: