master f8078eb41c28 cached
5 files
17.8 KB
5.5k tokens
1 requests
Download .txt
Repository: tamcore/autoupdate-oh-my-zsh-plugins
Branch: master
Commit: f8078eb41c28
Files: 5
Total size: 17.8 KB

Directory structure:
gitextract_yps7i5tb/

├── .github/
│   └── workflows/
│       └── ci.yaml
├── .pre-commit-config.yaml
├── LICENSE
├── README.md
└── autoupdate.plugin.zsh

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/ci.yaml
================================================
name: CI

on:
  workflow_dispatch:
  pull_request:
    paths-ignore:
      - '**.md'
      - LICENSE
  push:
    branches: [master]
    paths-ignore:
      - '**.md'
      - LICENSE

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  pre-commit:
    runs-on: ubuntu-latest
    timeout-minutes: 5

    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Setup Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: 3.x

      - name: Run pre-commit hooks
        uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1

  ci:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-15]
    runs-on: ${{ matrix.os }}
    timeout-minutes: 15

    steps:
      - name: Install zsh
        if: runner.os == 'Linux'
        shell: bash
        run: sudo apt-get update && sudo apt-get -y install zsh

      - name: Create zsh runner
        shell: bash
        run: |
          cat > /usr/local/bin/zsh-runner << 'EOF'
          #!/bin/bash
          # Runs a script in an interactive zsh with a pseudo-TTY.
          # Linux script(1) uses -c; macOS script(1) takes a positional command.
          if [ "$(uname)" = "Darwin" ]; then
            exec script -q -e -a /dev/null /bin/zsh "$1"
          else
            exec script -q -e -a /dev/null -c "zsh -i $1"
          fi
          EOF
          chmod +x /usr/local/bin/zsh-runner

      - name: Install oh-my-zsh
        run: sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended

      - name: Checkout plugin
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          path: autoupdate

      - name: Checkout zsh-autosuggestions
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          repository: zsh-users/zsh-autosuggestions
          path: zsh-autosuggestions
          fetch-depth: 0

      - name: Checkout zsh-syntax-highlighting
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          repository: zsh-users/zsh-syntax-highlighting
          path: zsh-syntax-highlighting
          fetch-depth: 0

      - name: Downgrade plugins to old commits
        shell: bash
        run: |
          git -C zsh-autosuggestions reset --hard 89c600c873a7e64b075c916850e80bfd62f81de9
          ZSH_SYNTAX_OLD=$(git -C zsh-syntax-highlighting rev-list HEAD~50 -n1)
          git -C zsh-syntax-highlighting reset --hard "$ZSH_SYNTAX_OLD"
          echo "ZSH_SYNTAX_OLD_SHA=$ZSH_SYNTAX_OLD" >> "$GITHUB_ENV"

      - name: Move plugins to oh-my-zsh custom directory
        shell: bash
        run: |
          mkdir -p "$HOME/.oh-my-zsh/custom/plugins"
          mv -v autoupdate zsh-autosuggestions zsh-syntax-highlighting "$HOME/.oh-my-zsh/custom/plugins"

      - name: Create .zshrc
        shell: bash
        run: |
          cat > "$HOME/.zshrc" << EOC
          export ZSH="$HOME/.oh-my-zsh"
          ZSH_THEME="robbyrussell"
          plugins=(autoupdate)

          # Always ignore the autoupdate plugin itself (detached HEAD in CI).
          # Append to any ignore list already set by the test step env var.
          export ZSH_CUSTOM_AUTOUPDATE_IGNORE="\${ZSH_CUSTOM_AUTOUPDATE_IGNORE:+\${ZSH_CUSTOM_AUTOUPDATE_IGNORE} }autoupdate"

          source $HOME/.oh-my-zsh/oh-my-zsh.sh
          EOC

      - name: Test basic plugin update
        shell: zsh-runner {0}
        env:
          TERM: xterm-256color
        run: |
          source "$HOME/.zshrc"
          upgrade_oh_my_zsh_custom

      - name: Verify both plugins were updated
        shell: bash
        run: |
          set -x
          AUTOSUGGESTIONS_HEAD=$(git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" rev-parse HEAD)
          SYNTAX_HEAD=$(git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting" rev-parse HEAD)
          if [ "$AUTOSUGGESTIONS_HEAD" = "89c600c873a7e64b075c916850e80bfd62f81de9" ]; then
            echo "ERROR: zsh-autosuggestions was not updated"
            exit 1
          fi
          if [ "$SYNTAX_HEAD" = "$ZSH_SYNTAX_OLD_SHA" ]; then
            echo "ERROR: zsh-syntax-highlighting was not updated"
            exit 1
          fi
          echo "Both plugins updated successfully"

      - name: Re-downgrade plugins for quiet mode test
        shell: bash
        run: |
          git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" reset --hard 89c600c873a7e64b075c916850e80bfd62f81de9
          git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting" reset --hard "$ZSH_SYNTAX_OLD_SHA"

      - name: Test quiet mode
        shell: zsh-runner {0}
        env:
          TERM: xterm-256color
          ZSH_CUSTOM_AUTOUPDATE_QUIET: "true"
        run: |
          source "$HOME/.zshrc"
          upgrade_oh_my_zsh_custom > /tmp/quiet_output.txt 2>&1

      - name: Verify quiet mode suppresses output
        shell: bash
        run: |
          if grep -q "Upgrading Custom Plugins" /tmp/quiet_output.txt; then
            echo "ERROR: Quiet mode should suppress the upgrade banner"
            cat /tmp/quiet_output.txt
            exit 1
          fi
          echo "Quiet mode test passed"

      - name: Re-downgrade plugins for parallel workers test
        shell: bash
        run: |
          git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" reset --hard 89c600c873a7e64b075c916850e80bfd62f81de9
          git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting" reset --hard "$ZSH_SYNTAX_OLD_SHA"

      - name: Test parallel workers
        shell: zsh-runner {0}
        env:
          TERM: xterm-256color
          ZSH_CUSTOM_AUTOUPDATE_NUM_WORKERS: "2"
        run: |
          source "$HOME/.zshrc"
          upgrade_oh_my_zsh_custom

      - name: Verify parallel update
        shell: bash
        run: |
          set -x
          AUTOSUGGESTIONS_HEAD=$(git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" rev-parse HEAD)
          SYNTAX_HEAD=$(git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting" rev-parse HEAD)
          if [ "$AUTOSUGGESTIONS_HEAD" = "89c600c873a7e64b075c916850e80bfd62f81de9" ]; then
            echo "ERROR: zsh-autosuggestions was not updated in parallel mode"
            exit 1
          fi
          if [ "$SYNTAX_HEAD" = "$ZSH_SYNTAX_OLD_SHA" ]; then
            echo "ERROR: zsh-syntax-highlighting was not updated in parallel mode"
            exit 1
          fi
          echo "Parallel workers test passed"

      - name: Re-downgrade plugins for ignore list test
        shell: bash
        run: |
          git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" reset --hard 89c600c873a7e64b075c916850e80bfd62f81de9
          git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting" reset --hard "$ZSH_SYNTAX_OLD_SHA"

      - name: Test ignore list
        shell: zsh-runner {0}
        env:
          TERM: xterm-256color
          ZSH_CUSTOM_AUTOUPDATE_IGNORE: "zsh-autosuggestions"
        run: |
          source "$HOME/.zshrc"
          upgrade_oh_my_zsh_custom

      - name: Verify ignored plugin was skipped
        shell: bash
        run: |
          set -x
          AUTOSUGGESTIONS_HEAD=$(git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" rev-parse HEAD)
          SYNTAX_HEAD=$(git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting" rev-parse HEAD)
          if [ "$AUTOSUGGESTIONS_HEAD" != "89c600c873a7e64b075c916850e80bfd62f81de9" ]; then
            echo "ERROR: zsh-autosuggestions should have been skipped but was updated"
            exit 1
          fi
          if [ "$SYNTAX_HEAD" = "$ZSH_SYNTAX_OLD_SHA" ]; then
            echo "ERROR: zsh-syntax-highlighting should have been updated"
            exit 1
          fi
          echo "Ignore list test passed"

      - name: Re-downgrade plugins and create nested repo for nested git test
        shell: bash
        run: |
          git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" reset --hard 89c600c873a7e64b075c916850e80bfd62f81de9
          git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting" reset --hard "$ZSH_SYNTAX_OLD_SHA"
          NESTED="$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions/test-nested-dep"
          git init "$NESTED"
          git -C "$NESTED" -c user.email="ci@test" -c user.name="CI" commit --allow-empty -m "initial"
          NESTED_SHA=$(git -C "$NESTED" rev-parse HEAD)
          git -C "$NESTED" checkout --detach HEAD
          echo "NESTED_SHA=$NESTED_SHA" >> "$GITHUB_ENV"

      - name: Test nested git directories are ignored
        shell: zsh-runner {0}
        env:
          TERM: xterm-256color
        run: |
          source "$HOME/.zshrc"
          upgrade_oh_my_zsh_custom

      - name: Verify nested git test
        shell: bash
        run: |
          set -x
          AUTOSUGGESTIONS_HEAD=$(git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" rev-parse HEAD)
          SYNTAX_HEAD=$(git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting" rev-parse HEAD)
          NESTED_HEAD=$(git -C "$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions/test-nested-dep" rev-parse HEAD)
          if [ "$AUTOSUGGESTIONS_HEAD" = "89c600c873a7e64b075c916850e80bfd62f81de9" ]; then
            echo "ERROR: zsh-autosuggestions was not updated"
            exit 1
          fi
          if [ "$SYNTAX_HEAD" = "$ZSH_SYNTAX_OLD_SHA" ]; then
            echo "ERROR: zsh-syntax-highlighting was not updated"
            exit 1
          fi
          if [ "$NESTED_HEAD" != "$NESTED_SHA" ]; then
            echo "ERROR: nested repo should not have been touched"
            exit 1
          fi
          echo "Nested git test passed"


================================================
FILE: .pre-commit-config.yaml
================================================
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
  rev: v5.0.0
  hooks:
  - id: check-yaml
  - id: end-of-file-fixer
  - id: trailing-whitespace
  - id: mixed-line-ending

- repo: https://github.com/Lucas-C/pre-commit-hooks
  rev: v1.5.5
  hooks:
    - id: forbid-crlf
    - id: remove-crlf
    - id: forbid-tabs
    - id: remove-tabs
      args: [--whitespaces-count, '2']

- repo: https://github.com/shellcheck-py/shellcheck-py
  rev: v0.10.0.1
  hooks:
    - id: shellcheck
      # .zsh files have no shebang; treat as bash for analysis. SC2059 is
      # intentionally disabled: color escape codes in printf format strings
      # are valid and deliberate throughout this plugin.
      args: ['--shell=bash', '--severity=warning', '--exclude=SC2059']


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018-2022 Philipp Born

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.


================================================
FILE: README.md
================================================
autoupdate-zsh-plugin
====================

[oh-my-zsh plugin](https://github.com/robbyrussell/oh-my-zsh) for auto updating of git-repositories in $ZSH_CUSTOM folder

## Install

Create a new directory in `$ZSH_CUSTOM/plugins` called `autoupdate` and clone this repo into that directory. Note: it must be named `autoupdate` or oh-my-zsh won't recognize that it is a valid plugin directory.
```
git clone https://github.com/TamCore/autoupdate-oh-my-zsh-plugins $ZSH_CUSTOM/plugins/autoupdate
```

## Usage

Add `autoupdate` to the `plugins=()` list in your `~/.zshrc` file and you're done.

```bash
plugins=(autoupdate)

# Multiple plugins should be separated by space character
# plugins=(somePlugin autoupdate)
```

By default this will auto update both plugins and themes, found in the $ZSH_CUSTOM folder, every 13 days (which is the OhMyZsh default).

If you want to check for updates more or less often, you can export the `UPDATE_ZSH_DAYS` variable in your `~/.zshrc` file:
```bash
# to check for updates once a month
export UPDATE_ZSH_DAYS=30
# or to check for updates daily
export UPDATE_ZSH_DAYS=1
```

Another possibility is to use the provided upgrade function, which one may call
at any time using `upgrade_oh_my_zsh_custom`. There shouldn't be any difference
with the automatic operation. Also, a convenient alias that calls the OhMyZsh
update function `omz update` and then `upgrade_oh_my_zsh_custom`, called
`upgrade_oh_my_zsh_all`, is available as well. However, running `omz update` directly **will not** trigger the this plugin.

### Quiet mode

To turn off the "Upgrading custom plugins" message (for example, if you're using [Powerlevel10k's instant prompt](https://github.com/romkatv/powerlevel10k#instant-prompt)), add this to your `~/.zshrc` file:
```shell
# Uncomment the following line to change how often to auto-update (in days).
# export UPDATE_ZSH_DAYS=13
ZSH_CUSTOM_AUTOUPDATE_QUIET=true
```

### Parallel downloads

To speed up updates by setting maximum number of parallel downloads, add this to your `~/.zshrc` file:
```shell
# Values accepted (min: 1, max: 16)
# Parallel downloads will not be enabled if value is out-of-range
ZSH_CUSTOM_AUTOUPDATE_NUM_WORKERS=8
```

### Ignore Plugin List

To prevent some plugins from update, add this to your `~/.zshrc` file:
```shell
# support ',' ';' ':' as the split char
ZSH_CUSTOM_AUTOUPDATE_IGNORE="zsh-autosuggestions, autoupdate; zsh-autosuggestions"
```


================================================
FILE: autoupdate.plugin.zsh
================================================
# - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# self-update

if which tput >/dev/null 2>&1; then
  ncolors=$(tput colors)
fi

if [[ $- == *i* ]] && [ -n "$ncolors" ] && [ "$ncolors" -ge 8 ]; then
  RED="$(tput setaf 1)"
  BLUE="$(tput setaf 4)"
  GREEN="$(tput setaf 2)"
  NORMAL="$(tput sgr0)"
else
  BLUE=""
  GREEN=""
  NORMAL=""
fi

zmodload zsh/datetime

function _current_epoch() {
  echo $(( $EPOCHSECONDS / 60 / 60 / 24 ))
}

function _update_zsh_custom_update() {
  echo "LAST_EPOCH=$(_current_epoch)" >| "${ZSH_CACHE_DIR}/.zsh-custom-update"
}

function _get_epoch_target() {
  local epoch_target

  zstyle -g epoch_target ':omz:update' frequency \
    || epoch_target="$UPDATE_ZSH_DAYS"
  if [[ -z "$epoch_target" ]]; then
    # Default to old behavior
    epoch_target=13
  fi

  echo "$epoch_target"
}

epoch_target="$(_get_epoch_target)"

function _upgrade_custom_plugin() {
  # path of plugin/theme
  p=$(dirname "$d")
  # it's name
  pn=$(basename "$p")
  # it's type (plugin/theme)
  pt=$(dirname "$p")
  pt=$(basename ${pt:0:((${#pt} - 1))})

  # Check if plugin should be ignored
  if [[ -n "$ZSH_CUSTOM_AUTOUPDATE_IGNORE" ]]; then
    echo "$ZSH_CUSTOM_AUTOUPDATE_IGNORE" | tr ',;: ' '\n' | grep -v '^$' | while read ignored; do
      if [[ "$pn" == "$ignored" ]]; then
        printf "${BLUE}%s${NORMAL}\n" "Skipping $pn $pt (in ignore list)"
        return 0
      fi
    done
  fi

  last_head=$( git -C "${p}" rev-parse HEAD )
  if git -C "${p}" pull --quiet --rebase --stat --autostash
  then
    curr_head=$( git -C "${p}" rev-parse HEAD )
    if [ "${last_head}" != "${curr_head}" ]
    then
      printf "${GREEN}%s${NORMAL}\n" "Hooray! the $pn $pt has been updated."
    else
      printf "${BLUE}%s${NORMAL}\n" "The $pn $pt was already at the latest version."
    fi
  else
    printf "${RED}%s${NORMAL}\n" "There was an error updating the $pn $pt. Try again later?"
  fi
}

function upgrade_oh_my_zsh_custom() {
  if [[ -z "$ZSH_CUSTOM_AUTOUPDATE_QUIET" ]]; then
    printf "${BLUE}%s${NORMAL}\n" "Upgrading Custom Plugins"
  fi

  num_workers=$( printf "%.0f" "$ZSH_CUSTOM_AUTOUPDATE_NUM_WORKERS" )
  set +m
  find -L "${ZSH_CUSTOM}" -maxdepth 3 -name .git | while IFS= read -r d
  do
    if ! test "$num_workers" -gt 1 2> /dev/null || \
    test "$num_workers" -gt 16 2> /dev/null; then
      _upgrade_custom_plugin "${d}"
    else
      ((i=(i+1)%$num_workers)) || wait
      (_upgrade_custom_plugin "${d}") &
    fi
  done
  wait
  set -m
}

alias upgrade_oh_my_zsh_all='zsh "$ZSH/tools/upgrade.sh"; upgrade_oh_my_zsh_custom'


if [ -f ~/.zsh-custom-update ]
then
  mv ~/.zsh-custom-update "${ZSH_CACHE_DIR}/.zsh-custom-update"
fi

function _dispatch_update_mode() {
  local mode

  zstyle -g mode ':omz:update' mode
  if [[ -z "$mode" ]]; then
    if [[ "$DISABLE_AUTO_UPDATE" == "true" ]]; then
      mode="disabled"
    elif [[ "$DISABLE_UPDATE_PROMPT" == "true" ]]; then
      mode="auto"
    else
      mode="prompt"
    fi
  fi

  echo "$mode"
}

update_mode="$(_dispatch_update_mode)"

if [[ "$update_mode" == "disabled" ]]
then
  : # No updates
elif [ -f "${ZSH_CACHE_DIR}/.zsh-custom-update" ]
then
  . "${ZSH_CACHE_DIR}/.zsh-custom-update"

  if [[ -z "$LAST_EPOCH" ]]
  then
    LAST_EPOCH=0
  fi

  epoch_diff=$(($(_current_epoch) - $LAST_EPOCH))
  if [ "$epoch_diff" -gt "$epoch_target" ]
  then
    if [[ "$update_mode" == "auto" ]]
    then
      (upgrade_oh_my_zsh_custom)
    elif [[ "$update_mode" == "reminder" ]]
    then
      echo "[oh-my-zsh] It's time to update! You can do that by running \`upgrade_oh_my_zsh_custom\`"
    else
      echo "[oh-my-zsh] Would you like to check for custom plugin updates? [Y/n]: \c"
      read line
      if [[ "$line" == Y* ]] || [[ "$line" == y* ]] || [ -z "$line" ]
      then
        (upgrade_oh_my_zsh_custom)
      fi
    fi
    _update_zsh_custom_update
  fi
else
  _update_zsh_custom_update
fi

unset -f _update_zsh_custom_update _current_epoch \
  _get_epoch_target _dispatch_update_mode
Download .txt
gitextract_yps7i5tb/

├── .github/
│   └── workflows/
│       └── ci.yaml
├── .pre-commit-config.yaml
├── LICENSE
├── README.md
└── autoupdate.plugin.zsh
Condensed preview — 5 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (19K chars).
[
  {
    "path": ".github/workflows/ci.yaml",
    "chars": 9964,
    "preview": "name: CI\n\non:\n  workflow_dispatch:\n  pull_request:\n    paths-ignore:\n      - '**.md'\n      - LICENSE\n  push:\n    branche"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 768,
    "preview": "repos:\n- repo: https://github.com/pre-commit/pre-commit-hooks\n  rev: v5.0.0\n  hooks:\n  - id: check-yaml\n  - id: end-of-f"
  },
  {
    "path": "LICENSE",
    "chars": 1074,
    "preview": "MIT License\n\nCopyright (c) 2018-2022 Philipp Born\n\nPermission is hereby granted, free of charge, to any person obtaining"
  },
  {
    "path": "README.md",
    "chars": 2433,
    "preview": "autoupdate-zsh-plugin\n====================\n\n[oh-my-zsh plugin](https://github.com/robbyrussell/oh-my-zsh) for auto updat"
  },
  {
    "path": "autoupdate.plugin.zsh",
    "chars": 4000,
    "preview": "# - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n# self-update\n\nif which tput >/dev/null 2>&1; then\n  ncolors=$"
  }
]

About this extraction

This page contains the full source code of the tamcore/autoupdate-oh-my-zsh-plugins GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 5 files (17.8 KB), approximately 5.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!