Repository: jftuga/less-Windows Branch: master Commit: e25a4c83c8b9 Files: 9 Total size: 13.1 KB Directory structure: gitextract_23kj5y12/ ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ ├── comment.yml │ └── nightly.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.py ├── shared.py └── version_compare.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" ================================================ FILE: .github/workflows/comment.yml ================================================ on: workflow_dispatch: inputs: issue_number: required: true owner: required: true repo: required: true body: required: true jobs: comment: runs-on: ubuntu-latest steps: - uses: actions/github-script@v9 with: github-token: ${{ secrets.WINGET_TOKEN }} script: | github.rest.issues.createComment({ issue_number: ${{ github.event.inputs.issue_number }}, owner: '${{ github.event.inputs.owner }}', repo: '${{ github.event.inputs.repo }}', body: '${{ github.event.inputs.body }}' }) ================================================ FILE: .github/workflows/nightly.yml ================================================ name: nightly on: push: branches: [ master ] pull_request: branches: [ master ] schedule: - cron: '0 0 * * *' workflow_dispatch: jobs: checkver: runs-on: ubuntu-latest outputs: new_version: ${{ steps.check_new_version.outputs.new_version }} steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 - name: Compare versions run: python version_compare.py - name: Check if new version exists id: check_new_version run: | # If new.txt exists and is not empty if [ -s "new.txt" ]; then NEW_VERSION=$(cat new.txt) echo "New version is $NEW_VERSION" echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT fi build: runs-on: windows-${{ matrix.arch == 'arm' && '2022' || 'latest' }} needs: checkver if: ${{ needs.checkver.outputs.new_version }} strategy: matrix: arch: [x64, x86, arm64, arm] steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 - uses: ilammy/msvc-dev-cmd@v1 with: arch: ${{ matrix.arch != 'x64' && 'amd64_' || '' }}${{ matrix.arch }} sdk: ${{ matrix.arch == 'arm' && '10.0.22621.0' || '' }} - name: Build run: python .\build.py - name: Upload less to artifact uses: actions/upload-artifact@v7 with: name: less-${{ matrix.arch }} path: | less.exe lesskey.exe release: needs: [checkver, build] if: ${{ github.event_name != 'pull_request' }} runs-on: ubuntu-latest permissions: contents: write discussions: write steps: - name: Get all artifacts uses: actions/download-artifact@v8 - name: Zip each artifact run: find . -type d ! -path . -execdir zip -9 -rj "{}.zip" "{}" \; - uses: octokit/request-action@v2.x id: get_workflow_runtime with: route: GET /repos/{owner}/{repo}/actions/runs/{run_id} owner: ${{ github.repository_owner }} repo: less-Windows run_id: ${{ github.run_id }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - id: extract_major_version run: | MAJOR_VERSION=$(echo ${{ needs.checkver.outputs.new_version }} | cut -d. -f1) echo "major_version=$MAJOR_VERSION" >> $GITHUB_OUTPUT - uses: softprops/action-gh-release@v3 with: files: '*.zip' body: | Built with GitHub Actions at ${{ fromJson(steps.get_workflow_runtime.outputs.data).updated_at }} Release notes can be found [here](http://greenwoodsoftware.com/less/news.${{ steps.extract_major_version.outputs.major_version }}.html). tag_name: less-v${{ needs.checkver.outputs.new_version }} discussion_category_name: Announcements env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} winget: name: Publish to WinGet needs: [checkver, release] runs-on: ubuntu-latest steps: - uses: vedantmgoyal9/winget-releaser@main with: identifier: jftuga.less version: ${{ needs.checkver.outputs.new_version }} release-tag: less-v${{ needs.checkver.outputs.new_version }} installers-regex: '\.zip$' token: ${{ secrets.WINGET_TOKEN }} ================================================ FILE: .gitignore ================================================ less-*/ less*.zip compile.bat download.html __pycache__/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 J Taylor 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 ================================================ # less-Windows [![nightly](https://github.com/jftuga/less-Windows/actions/workflows/nightly.yml/badge.svg)](https://github.com/jftuga/less-Windows/actions/workflows/nightly.yml) GNU [less](https://en.wikipedia.org/wiki/Less_\(Unix\)) compiled for Windows from the [less source](http://greenwoodsoftware.com/less/) via GitHub Actions. New versions are being checked daily, and builds are compiled with the latest version of Visual Studio. ## Installation Binaries for `less.exe` (and `lesskey.exe`) are provided on the [Releases Page](https://github.com/jftuga/less-Windows/releases). Download the appropriate one for your system. If you prefer to install less via a package manager, you can choose one of the following options: ### Winget A new version is pushed to the upstream [winget-pkgs](https://github.com/microsoft/winget-pkgs) for every release: ```powershell winget install jftuga.less ``` ### Chocolatey [less](https://community.chocolatey.org/packages/less) is available in the Community Repository: ```powershell choco install less ``` ### Scoop [less](https://scoop.sh/#/apps?q=main%2Fless&s=0&d=1&o=true) is available in the Main bucket: ```powershell scoop install less ``` ================================================ FILE: build.py ================================================ #!/usr/bin/env python3 r""" builder.py -John Taylor May-13-2020 Download and compile GNU less with Visual Studio less.exe and lesskey.exe are created """ import os import os.path import shutil import subprocess import sys import time import urllib.request import zipfile from shared import download_less_web_page, get_latest_version_url, LESSURL def download_and_save(url: str) -> bool: """Download the less .zip file and save it to the current directory """ # something like less-561.zip archive = url.split("/")[-1] if os.path.exists(archive): sz = os.stat(archive).st_size print("File already exists: %s with size: %d" % (archive, sz)) return archive try: urllib.request.urlretrieve(url, archive) except: return False return archive def extract_archive(archive: str) -> str: """Unzip the archive file, remove preexisting directory """ # given "less-561.zip", return "less-561 zip_dest = os.path.splitext(archive)[0] if os.path.exists(zip_dest): print("Removing preexisting directory: %s" % (zip_dest)) try: shutil.rmtree(zip_dest) time.sleep(1) except: return False try: with zipfile.ZipFile(archive, "r") as z: z.extractall(".") except: return False return zip_dest def create_compile_batchfile(archive_dest: str): """Create a .bat file containing environment setup and nmake compile commands """ bat = "compile.bat" try: with open(bat, "w") as fp: fp.write("@echo off\n") fp.write("cd %s\n" % (archive_dest)) fp.write("nmake /f Makefile.wnm\n") fp.write("copy /y less.exe ..\n") fp.write("copy /y lesskey.exe ..\n") except: return False return bat def main(): if not (page := download_less_web_page()): print("Unable to download URL: %s" % (LESSURL)) sys.exit(10) return version, url = get_latest_version_url(page) if version is None: print("Unable to extract version from: %s" % (LESSURL), file=sys.stderr) sys.exit(20) if not (archive := download_and_save(url)): print("Unable to download file: %s" % (url), file=sys.stderr) sys.exit(30) if not (archive_dest := extract_archive(archive)): print("Unable to unzip archive: %s" % (archive), file=sys.stderr) sys.exit(40) if not (cmd := create_compile_batchfile(archive_dest)): print("Unable to create batch file", file=sys.stderr) sys.exit(50) result = subprocess.run((cmd,), shell=True, capture_output=True) if result.returncode > 0: err = result.stderr.decode("utf-8") out = result.stdout.decode("utf-8") print("Compile failed:\n%s\n\n%s\n" % (out, err)) sys.exit(60) if "__main__" == __name__: main() # end of script ================================================ FILE: shared.py ================================================ #!/usr/bin/env python3 r""" shared.py -John Taylor May-14-2020 Functions shared between build.py and version_compare.py """ import re import time import urllib.request LESSURL = "http://greenwoodsoftware.com/less/download.html" version_url_re = re.compile(r"""Download RECOMMENDED version (.*?) """, re.M | re.S | re.I) NEWFILE = "new.txt" def download_less_web_page() -> str: """Download LESSURL and save the contents to fname Returns: An in-memory version of the downloaded web page """ fname = "download.html" try: urllib.request.urlretrieve(LESSURL, fname) time.sleep(1) except: return False try: with open(fname) as fp: page = fp.read() except: return False return page def get_latest_version_url(page: str) -> tuple: """Return the URL for the "RECOMMENDED version" Args: page: an HTML web page, provided in LESSURL Returns: A tuple containing: (version number, zip archive URL) Ex: 551, http://greenwoodsoftware.com/less/less-551.zip """ match = version_url_re.findall(page) if not len(match): return (None, None) version = match[0] archive = "less-%s.zip" % version url = LESSURL.replace("download.html", archive) return version, url ================================================ FILE: version_compare.py ================================================ #!/usr/bin/env python3 r""" version_compare.py -John Taylor May-14-2020 Compare local github version with less web site """ import json import urllib.request import re import sys from shared import download_less_web_page, get_latest_version_url, LESSURL, NEWFILE LOCALURL = "https://api.github.com/repos/jftuga/less-Windows/releases" def download_local_web_page() -> str: """Download and return JSON from LOCALURL """ try: with urllib.request.urlopen(LOCALURL) as f: page = f.read() except: return False page = page.decode("utf-8") return page def get_latest_local_version(page: str) -> str: """Extract and return the lastest release version from a JSON page Ex: 561 """ try: j = json.loads(page) except: return False if not len(j): return "500" newest = j[0] print(f'{newest["tag_name"]=}') # The initial version is different than future versions if "v560" == newest["tag_name"]: return "560" # given less-v561.17, return 561 release_version = newest["tag_name"][6:11] if release_version.endswith(".0"): release_version = re.sub(r"\.0$", "", release_version) print(f'{release_version=}') return release_version def main(): """Write to new.txt when a new version needs to be downloaded """ if not (page := download_local_web_page()): print("Unable to download URL: %s" % (LOCALURL)) sys.exit(10) if not (local_version := get_latest_local_version(page)): print("Unable to extract version from URL: %s" % (LOCALURL)) sys.exit(20) if not (page := download_less_web_page()): print("Unable to download URL: %s" % (LESSURL)) sys.exit(30) return remote_version, _ = get_latest_version_url(page) if remote_version is None: print("Unable to extract version from: %s" % (LESSURL), file=sys.stderr) sys.exit(40) if remote_version == local_version: print(f"Versions are the same: remote_version: {remote_version} local_version: {local_version}") return if float(local_version) >= float(remote_version): print(f"Local version is newer: local_version: {local_version} remote_version: {remote_version}") sys.exit(120) print(f"Remote version is newer: remote_version: {remote_version} local_version: {local_version}") print(f"Saving new version to file: {NEWFILE}") try: with open(NEWFILE, mode="w") as fp: fp.write("%s\n" % remote_version) except: print(f"Unable able to open file for writing: {NEWFILE}") sys.exit(50) if "__main__" == __name__: main()