Migration to GNU Stow
This commit is contained in:
19
zsh/.oh-my-zsh/.github/CODEOWNERS
vendored
Normal file
19
zsh/.oh-my-zsh/.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# Plugin owners
|
||||
plugins/archlinux/ @ratijas
|
||||
plugins/dbt/ @msempere
|
||||
plugins/eza/ @pepoluan
|
||||
plugins/genpass/ @atoponce
|
||||
plugins/git-lfs/ @hellovietduc
|
||||
plugins/gitfast/ @felipec
|
||||
plugins/kube-ps1/ @mcornella
|
||||
plugins/kubectl/ @mcornella
|
||||
plugins/kubectx/ @mcornella
|
||||
plugins/opentofu/ @mcornella
|
||||
plugins/react-native @esthor
|
||||
plugins/sdk/ @rgoldberg
|
||||
plugins/shell-proxy/ @septs
|
||||
plugins/starship/ @axieax
|
||||
plugins/terraform/ @mcornella
|
||||
plugins/universalarchive/ @Konfekt
|
||||
plugins/wp-cli/ @joshmedeski
|
||||
plugins/zoxide/ @ajeetdsouza
|
||||
6
zsh/.oh-my-zsh/.github/FUNDING.yml
vendored
Normal file
6
zsh/.oh-my-zsh/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
github:
|
||||
- ohmyzsh
|
||||
- robbyrussell
|
||||
- mcornella
|
||||
- carlosala
|
||||
open_collective: ohmyzsh
|
||||
87
zsh/.oh-my-zsh/.github/INCIDENT_RESPONSE_PLAN.md
vendored
Normal file
87
zsh/.oh-my-zsh/.github/INCIDENT_RESPONSE_PLAN.md
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
# Incident Response Plan
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please see [the latest guidelines](https://github.com/ohmyzsh/ohmyzsh/blob/master/SECURITY.md) for instructions.
|
||||
|
||||
## Phases
|
||||
|
||||
### Triage
|
||||
|
||||
1. Is this a valid security vulnerability?
|
||||
|
||||
- [ ] It affects our CI/CD or any of our repositories.
|
||||
- [ ] For ohmyzsh/ohmyzsh, it affects the latest commit.
|
||||
- [ ] For others, it affects the latest commit on the default branch.
|
||||
- [ ] It affects a third-party dependency:
|
||||
- [ ] Zsh or git
|
||||
- [ ] For a plugin, the vulnerability is a result of our usage of the dependency.
|
||||
|
||||
2. What's the scope of the vulnerability?
|
||||
|
||||
- [ ] Our codebase.
|
||||
- [ ] A direct third-party dependency (Zsh, git, other plugins).
|
||||
- [ ] An indirect third-party dependency.
|
||||
- [ ] Out of scope, a third-party dependency that is the responsibility of the user.
|
||||
- [ ] Out of scope, any other case (edit this plan and add the details).
|
||||
|
||||
3. Is the vulnerability actionable?
|
||||
|
||||
- [ ] Yes, we can submit a fix.
|
||||
- [ ] Yes, we can disable a feature.
|
||||
- [ ] Yes, we can mitigate the risk.
|
||||
- [ ] Yes, we can remove a vulnerable dependency.
|
||||
- [ ] Yes, we can apply a workaround.
|
||||
- [ ] Yes, we can apply a patch to a vulnerable dependency ([example for CVE-2021-45444](https://github.com/ohmyzsh/ohmyzsh/blob/cb72d7dcbf08b435c7f8a6470802b207b2aa02c3/lib/vcs_info.zsh)).
|
||||
- [ ] No, the vulnerability is not actionable.
|
||||
|
||||
4. What's the impact of the vulnerability?
|
||||
|
||||
Assess using the *CIA* triad:
|
||||
|
||||
- **Confidentiality**: example: report or sharing of secrets.
|
||||
- **Integrity**: affects the integrity of the system (deletion, corruption or encryption of data, OS file corruption, etc.).
|
||||
- **Availability**: denial of login, deletion of required files to boot / login, etc.
|
||||
|
||||
5. What's the exploitability of the vulnerability?
|
||||
|
||||
Consider how easy it is to exploit, and if it affects all users or requires specific configurations.
|
||||
|
||||
6. What's the severity of the vulnerability?
|
||||
|
||||
You can use the [CVSS v3.1](https://www.first.org/cvss/specification-document) to assess the severity of the vulnerability.
|
||||
|
||||
7. When was the vulnerability introduced?
|
||||
|
||||
- Find the responsible code path.
|
||||
- Find the commit or Pull Request that introduced the vulnerability.
|
||||
|
||||
8. Who are our security contacts?
|
||||
|
||||
Assess upstream or downstream contacts, and their desired channels of security.
|
||||
|
||||
> TODO: add a list of contacts.
|
||||
|
||||
### Mitigation
|
||||
|
||||
- **Primary focus:** removing possibility of exploitation fast.
|
||||
- **Secondary focus:** addressing the root cause.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Make sure to test that the mitigation works as expected, and does not introduce new vulnerabilities.
|
||||
> When deploying a patch, make sure not to disclose the vulnerability in the commit message or PR description.
|
||||
|
||||
> TODO: introduce a fast-track update process for security patches.
|
||||
|
||||
### Disclosure
|
||||
|
||||
Primary goal: inform our users about the vulnerability, and whether they are affected or not affected based on information they should be able to check themselves in a straightforward way.
|
||||
|
||||
> TODO: add a vulnerability disclosure template.
|
||||
|
||||
### Learn
|
||||
|
||||
- Document the vulnerability, steps performed, and lessons learned.
|
||||
- Document the timeline of events.
|
||||
- Document and address improvements on the Security Incident Response Plan.
|
||||
- Depending on the severity of the vulnerability, consider disclosing the root cause or not based on likely impact on users and estimated potential victims still affected.
|
||||
68
zsh/.oh-my-zsh/.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
68
zsh/.oh-my-zsh/.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
name: Report a bug
|
||||
description: Report a bug that isn't caused by Oh My Zsh. If unsure, use this form
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Self Check
|
||||
- Look for similar errors in existing [GitHub Issues](https://github.com/ohmyzsh/ohmyzsh/issues?q=is%3Aissue) (open or closed).
|
||||
- Try reaching out on the [Discord server](https://discord.gg/ohmyzsh) for help.
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear description of what the bug is.
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: |
|
||||
Steps to reproduce the problem.
|
||||
placeholder: |
|
||||
For example:
|
||||
1. Enable plugin '...'
|
||||
2. Run command '...' or try to complete command '...'
|
||||
3. See error
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: A brief description of what should happen.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots and recordings
|
||||
description: |
|
||||
If applicable, add screenshots to help explain your problem. You can also record an asciinema session: https://asciinema.org/
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: OS / Linux distribution
|
||||
placeholder: Windows 10, Ubuntu 20.04, Arch Linux, macOS 10.15...
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Zsh version
|
||||
description: Run `echo $ZSH_VERSION` to check.
|
||||
placeholder: "5.6"
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Terminal emulator
|
||||
placeholder: iTerm2, GNOME Terminal, Terminal.app...
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: If using WSL on Windows, which version of WSL
|
||||
description: Run `wsl -l -v` to check.
|
||||
options:
|
||||
- WSL1
|
||||
- WSL2
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context about the problem here. This can be themes, plugins, custom settings...
|
||||
77
zsh/.oh-my-zsh/.github/ISSUE_TEMPLATE/bug_report_omz.yml
vendored
Normal file
77
zsh/.oh-my-zsh/.github/ISSUE_TEMPLATE/bug_report_omz.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
name: Report a bug in Oh My Zsh
|
||||
description: Create a report to help us improve Oh My Zsh
|
||||
labels: ['Bug']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Self Check
|
||||
- **Make sure this bug only happens with Oh My Zsh enabled**.
|
||||
- Look for similar errors in existing [GitHub Issues](https://github.com/ohmyzsh/ohmyzsh/issues?q=is%3Aissue) (open or closed).
|
||||
- Try reaching out on the [Discord server](https://discord.gg/ohmyzsh) for help.
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear description of what the bug is.
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: |
|
||||
Steps to reproduce the problem.
|
||||
placeholder: |
|
||||
For example:
|
||||
1. Enable plugin '...'
|
||||
2. Run command '...' or try to complete command '...'
|
||||
3. See error
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: A brief description of what should happen.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots and recordings
|
||||
description: |
|
||||
If applicable, add screenshots to help explain your problem. You can also record an asciinema session: https://asciinema.org/
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: OS / Linux distribution
|
||||
placeholder: Windows 10, Ubuntu 20.04, Arch Linux, macOS 10.15...
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Zsh version
|
||||
description: Run `echo $ZSH_VERSION` to check.
|
||||
placeholder: "5.6"
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Oh My Zsh version
|
||||
description: Run `omz version` to check.
|
||||
placeholder: master (bf303965)
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Terminal emulator
|
||||
placeholder: iTerm2, GNOME Terminal, Terminal.app...
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: If using WSL on Windows, which version of WSL
|
||||
description: Run `wsl -l -v` to check.
|
||||
options:
|
||||
- WSL1
|
||||
- WSL2
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context about the problem here. This can be themes, plugins, custom settings...
|
||||
8
zsh/.oh-my-zsh/.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
zsh/.oh-my-zsh/.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Support
|
||||
url: https://github.com/ohmyzsh/ohmyzsh/discussions
|
||||
about: Ask the community for support
|
||||
- name: Get help on Discord
|
||||
url: https://discord.gg/ohmyzsh
|
||||
about: Have a quick question? Join the Discord server and ask on the appropriate channel.
|
||||
37
zsh/.oh-my-zsh/.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
37
zsh/.oh-my-zsh/.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Feature request
|
||||
description: Suggest a feature for Oh My Zsh
|
||||
labels: ["Feature"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Self Check
|
||||
- Look for similar features in existing [GitHub Issues](https://github.com/ohmyzsh/ohmyzsh/issues?q=is%3Aissue) (open or closed).
|
||||
- type: input
|
||||
attributes:
|
||||
label: If the feature request is for a plugin or theme, specify it here.
|
||||
description: The name of the plugin or theme that you would like us to improve.
|
||||
placeholder: e.g. Git plugin, Agnoster theme
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If the feature solves a problem you have, specify it here.
|
||||
description: A description of what the problem is.
|
||||
placeholder: Ex. I'm always frustrated when...
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the proposed feature.
|
||||
description: A description of what you want to happen. Be as specific as possible.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
description: A description of any alternative solutions or features you've considered. This can also include other plugins or themes.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context, screenshots or Discord conversations about the feature request here. Also if you have any PRs related to this issue that are already open that you would like us to look at.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Related Issues
|
||||
description: Is there any open or closed issues that is related to this feature request? If so please link them below!
|
||||
20
zsh/.oh-my-zsh/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
20
zsh/.oh-my-zsh/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
## Standards checklist:
|
||||
|
||||
<!-- Fill with an x the ones that apply. Example: [x] -->
|
||||
|
||||
- [ ] The PR title is descriptive.
|
||||
- [ ] The PR doesn't replicate another PR which is already open.
|
||||
- [ ] I have read the contribution guide and followed all the instructions.
|
||||
- [ ] The code follows the code style guide detailed in the wiki.
|
||||
- [ ] The code is mine or it's from somewhere with an MIT-compatible license.
|
||||
- [ ] The code is efficient, to the best of my ability, and does not waste computer resources.
|
||||
- [ ] The code is stable and I have tested it myself, to the best of my abilities.
|
||||
- [ ] If the code introduces new aliases, I provide a valid use case for all plugin users down below.
|
||||
|
||||
## Changes:
|
||||
|
||||
- [...]
|
||||
|
||||
## Other comments:
|
||||
|
||||
...
|
||||
14
zsh/.oh-my-zsh/.github/dependabot.yml
vendored
Normal file
14
zsh/.oh-my-zsh/.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "sunday"
|
||||
labels: []
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/.github/workflows/dependencies"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "sunday"
|
||||
labels: []
|
||||
48
zsh/.oh-my-zsh/.github/dependencies.yml
vendored
Normal file
48
zsh/.oh-my-zsh/.github/dependencies.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
dependencies:
|
||||
plugins/gitfast:
|
||||
repo: felipec/git-completion
|
||||
branch: master
|
||||
version: tag:v2.2
|
||||
postcopy: |
|
||||
set -e
|
||||
rm -rf git-completion.plugin.zsh Makefile t tools
|
||||
mv README.adoc MANUAL.adoc
|
||||
mv -f src/* .
|
||||
rmdir src
|
||||
plugins/gradle:
|
||||
repo: gradle/gradle-completion
|
||||
branch: master
|
||||
version: a9d7c822e42cc6a5b028b59e46cffcc8e7bc1134
|
||||
precopy: |
|
||||
set -e
|
||||
find . ! -name _gradle ! -name LICENSE -delete
|
||||
plugins/history-substring-search:
|
||||
repo: zsh-users/zsh-history-substring-search
|
||||
branch: master
|
||||
version: 87ce96b1862928d84b1afe7c173316614b30e301
|
||||
precopy: |
|
||||
set -e
|
||||
rm -f zsh-history-substring-search.plugin.zsh
|
||||
test -e zsh-history-substring-search.zsh && mv zsh-history-substring-search.zsh history-substring-search.zsh
|
||||
postcopy: |
|
||||
set -e
|
||||
test -e dependencies/OMZ-README.md && cat dependencies/OMZ-README.md >> README.md
|
||||
plugins/wd:
|
||||
repo: mfaerevaag/wd
|
||||
branch: master
|
||||
version: tag:v0.10.1
|
||||
precopy: |
|
||||
set -e
|
||||
rm -r test
|
||||
rm install.sh tty.gif wd.1
|
||||
plugins/z:
|
||||
branch: master
|
||||
repo: agkozak/zsh-z
|
||||
version: cf9225feebfae55e557e103e95ce20eca5eff270
|
||||
precopy: |
|
||||
set -e
|
||||
test -e README.md && mv -f README.md MANUAL.md
|
||||
postcopy: |
|
||||
set -e
|
||||
test -e _zshz && mv -f _zshz _z
|
||||
test -e zsh-z.plugin.zsh && mv -f zsh-z.plugin.zsh z.plugin.zsh
|
||||
43
zsh/.oh-my-zsh/.github/workflows/dependencies.yml
vendored
Normal file
43
zsh/.oh-my-zsh/.github/workflows/dependencies.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Update dependencies
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
schedule:
|
||||
- cron: "0 6 * * 0"
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check for updates
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'ohmyzsh/ohmyzsh'
|
||||
permissions:
|
||||
contents: write # this is needed to push commits and branches
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Authenticate as @ohmyzsh
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||
with:
|
||||
app-id: ${{ secrets.OHMYZSH_APP_ID }}
|
||||
private-key: ${{ secrets.OHMYZSH_APP_PRIVATE_KEY }}
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: "pip"
|
||||
- name: Process dependencies
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
GIT_APP_NAME: ohmyzsh[bot]
|
||||
GIT_APP_EMAIL: 54982679+ohmyzsh[bot]@users.noreply.github.com
|
||||
TMP_DIR: ${{ runner.temp }}
|
||||
run: |
|
||||
pip install -r .github/workflows/dependencies/requirements.txt
|
||||
python3 .github/workflows/dependencies/updater.py
|
||||
1
zsh/.oh-my-zsh/.github/workflows/dependencies/.gitignore
vendored
Normal file
1
zsh/.oh-my-zsh/.github/workflows/dependencies/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.venv
|
||||
7
zsh/.oh-my-zsh/.github/workflows/dependencies/requirements.txt
vendored
Normal file
7
zsh/.oh-my-zsh/.github/workflows/dependencies/requirements.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
certifi==2025.11.12
|
||||
charset-normalizer==3.4.4
|
||||
idna==3.11
|
||||
PyYAML==6.0.3
|
||||
requests==2.32.5
|
||||
semver==3.0.4
|
||||
urllib3==2.6.2
|
||||
606
zsh/.oh-my-zsh/.github/workflows/dependencies/updater.py
vendored
Normal file
606
zsh/.oh-my-zsh/.github/workflows/dependencies/updater.py
vendored
Normal file
@@ -0,0 +1,606 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import timeit
|
||||
from copy import deepcopy
|
||||
from typing import Literal, NotRequired, Optional, TypedDict
|
||||
|
||||
import requests
|
||||
import yaml
|
||||
from semver import Version
|
||||
|
||||
# Get TMP_DIR variable from environment
|
||||
TMP_DIR = os.path.join(os.environ.get("TMP_DIR", "/tmp"), "ohmyzsh")
|
||||
# Relative path to dependencies.yml file
|
||||
DEPS_YAML_FILE = ".github/dependencies.yml"
|
||||
# Dry run flag
|
||||
DRY_RUN = os.environ.get("DRY_RUN", "0") == "1"
|
||||
|
||||
# utils for tag comparison
|
||||
BASEVERSION = re.compile(
|
||||
r"""[vV]?
|
||||
(?P<major>(0|[1-9])\d*)
|
||||
(\.
|
||||
(?P<minor>(0|[1-9])\d*)
|
||||
(\.
|
||||
(?P<patch>(0|[1-9])\d*)
|
||||
)?
|
||||
)?
|
||||
""",
|
||||
re.VERBOSE,
|
||||
)
|
||||
|
||||
|
||||
def coerce(version: str) -> Optional[Version]:
|
||||
match = BASEVERSION.search(version)
|
||||
if not match:
|
||||
return None
|
||||
|
||||
# BASEVERSION looks for `MAJOR.minor.patch` in the string given
|
||||
# it fills with None if any of them is missing (for example `2.1`)
|
||||
ver = {
|
||||
key: 0 if value is None else value for key, value in match.groupdict().items()
|
||||
}
|
||||
# Version takes `major`, `minor`, `patch` arguments
|
||||
ver = Version(**ver) # pyright: ignore[reportArgumentType]
|
||||
return ver
|
||||
|
||||
|
||||
class CodeTimer:
|
||||
def __init__(self, name=None):
|
||||
self.name = " '" + name + "'" if name else ""
|
||||
|
||||
def __enter__(self):
|
||||
self.start = timeit.default_timer()
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.took = (timeit.default_timer() - self.start) * 1000.0
|
||||
print("Code block" + self.name + " took: " + str(self.took) + " ms")
|
||||
|
||||
|
||||
### YAML representation
|
||||
def str_presenter(dumper, data):
|
||||
"""
|
||||
Configures yaml for dumping multiline strings
|
||||
Ref: https://stackoverflow.com/a/33300001
|
||||
"""
|
||||
if len(data.splitlines()) > 1: # check for multiline string
|
||||
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
|
||||
return dumper.represent_scalar("tag:yaml.org,2002:str", data)
|
||||
|
||||
|
||||
yaml.add_representer(str, str_presenter)
|
||||
yaml.representer.SafeRepresenter.add_representer(str, str_presenter)
|
||||
|
||||
|
||||
# Types
|
||||
class DependencyDict(TypedDict):
|
||||
repo: str
|
||||
branch: str
|
||||
version: str
|
||||
precopy: NotRequired[str]
|
||||
postcopy: NotRequired[str]
|
||||
|
||||
|
||||
class DependencyYAML(TypedDict):
|
||||
dependencies: dict[str, DependencyDict]
|
||||
|
||||
|
||||
class UpdateStatusFalse(TypedDict):
|
||||
has_updates: Literal[False]
|
||||
|
||||
|
||||
class UpdateStatusTrue(TypedDict):
|
||||
has_updates: Literal[True]
|
||||
version: str
|
||||
compare_url: str
|
||||
head_ref: str
|
||||
head_url: str
|
||||
|
||||
|
||||
class CommandRunner:
|
||||
class Exception(Exception):
|
||||
def __init__(self, message, returncode, stage, stdout, stderr):
|
||||
super().__init__(message)
|
||||
self.returncode = returncode
|
||||
self.stage = stage
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
|
||||
@staticmethod
|
||||
def run_or_fail(command: list[str], stage: str, *args, **kwargs):
|
||||
if DRY_RUN and command[0] == "gh":
|
||||
command.insert(0, "echo")
|
||||
|
||||
result = subprocess.run(command, *args, capture_output=True, **kwargs)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise CommandRunner.Exception(
|
||||
f"{stage} command failed with exit code {result.returncode}",
|
||||
returncode=result.returncode,
|
||||
stage=stage,
|
||||
stdout=result.stdout.decode("utf-8"),
|
||||
stderr=result.stderr.decode("utf-8"),
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class DependencyStore:
|
||||
store: DependencyYAML = {"dependencies": {}}
|
||||
|
||||
@staticmethod
|
||||
def set(data: DependencyYAML):
|
||||
DependencyStore.store = data
|
||||
|
||||
@staticmethod
|
||||
def update_dependency_version(path: str, version: str) -> DependencyYAML:
|
||||
with CodeTimer(f"store deepcopy: {path}"):
|
||||
store_copy = deepcopy(DependencyStore.store)
|
||||
|
||||
dependency = store_copy["dependencies"].get(path)
|
||||
if dependency is None:
|
||||
raise ValueError(f"Dependency {path} {version} not found")
|
||||
dependency["version"] = version
|
||||
store_copy["dependencies"][path] = dependency
|
||||
|
||||
return store_copy
|
||||
|
||||
@staticmethod
|
||||
def write_store(file: str, data: DependencyYAML):
|
||||
with open(file, "w") as yaml_file:
|
||||
yaml.safe_dump(data, yaml_file, sort_keys=False)
|
||||
|
||||
|
||||
class Dependency:
|
||||
def __init__(self, path: str, values: DependencyDict):
|
||||
self.path = path
|
||||
self.values = values
|
||||
|
||||
self.name: str = ""
|
||||
self.desc: str = ""
|
||||
self.kind: str = ""
|
||||
|
||||
match path.split("/"):
|
||||
case ["plugins", name]:
|
||||
self.name = name
|
||||
self.kind = "plugin"
|
||||
self.desc = f"{name} plugin"
|
||||
case ["themes", name]:
|
||||
self.name = name.replace(".zsh-theme", "")
|
||||
self.kind = "theme"
|
||||
self.desc = f"{self.name} theme"
|
||||
case _:
|
||||
self.name = self.desc = path
|
||||
|
||||
def __str__(self):
|
||||
output: str = ""
|
||||
for key in DependencyDict.__dict__["__annotations__"].keys():
|
||||
if key not in self.values:
|
||||
output += f"{key}: None\n"
|
||||
continue
|
||||
|
||||
value = self.values[key]
|
||||
if "\n" not in value:
|
||||
output += f"{key}: {value}\n"
|
||||
else:
|
||||
output += f"{key}:\n "
|
||||
output += value.replace("\n", "\n ", value.count("\n") - 1)
|
||||
return output
|
||||
|
||||
def update_or_notify(self):
|
||||
# Print dependency settings
|
||||
print(f"Processing {self.desc}...", file=sys.stderr)
|
||||
print(self, file=sys.stderr)
|
||||
|
||||
# Check for updates
|
||||
repo = self.values["repo"]
|
||||
remote_branch = self.values["branch"]
|
||||
version = self.values["version"]
|
||||
is_tag = version.startswith("tag:")
|
||||
|
||||
try:
|
||||
with CodeTimer(f"update check: {repo}"):
|
||||
if is_tag:
|
||||
status = GitHub.check_newer_tag(repo, version.replace("tag:", ""))
|
||||
else:
|
||||
status = GitHub.check_updates(repo, remote_branch, version)
|
||||
|
||||
if status["has_updates"] is True:
|
||||
short_sha = status["head_ref"][:8]
|
||||
new_version = status["version"] if is_tag else short_sha
|
||||
|
||||
try:
|
||||
branch_name = f"update/{self.path}/{new_version}"
|
||||
|
||||
# Create new branch
|
||||
branch = Git.checkout_or_create_branch(branch_name)
|
||||
|
||||
# Update dependency files
|
||||
self.__apply_upstream_changes()
|
||||
|
||||
if not Git.repo_is_clean():
|
||||
# Update dependencies.yml file
|
||||
self.__update_yaml(
|
||||
f"tag:{new_version}" if is_tag else status["version"]
|
||||
)
|
||||
|
||||
# Add all changes and commit
|
||||
has_new_commit = Git.add_and_commit(self.name, new_version)
|
||||
|
||||
if has_new_commit:
|
||||
# Push changes to remote
|
||||
Git.push(branch)
|
||||
|
||||
# Create GitHub PR
|
||||
GitHub.create_pr(
|
||||
branch,
|
||||
f"chore({self.name}): update to version {new_version}",
|
||||
f"""## Description
|
||||
|
||||
Update for **{self.desc}**: update to version [{new_version}]({status["head_url"]}).
|
||||
Check out the [list of changes]({status["compare_url"]}).
|
||||
""",
|
||||
)
|
||||
|
||||
# Clean up repository
|
||||
Git.clean_repo()
|
||||
except (CommandRunner.Exception, shutil.Error) as e:
|
||||
# Handle exception on automatic update
|
||||
match type(e):
|
||||
case CommandRunner.Exception:
|
||||
# Print error message
|
||||
print(
|
||||
f"Error running {e.stage} command: {e.returncode}", # pyright: ignore[reportAttributeAccessIssue]
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(e.stderr, file=sys.stderr) # pyright: ignore[reportAttributeAccessIssue]
|
||||
case shutil.Error:
|
||||
print(f"Error copying files: {e}", file=sys.stderr)
|
||||
|
||||
try:
|
||||
Git.clean_repo()
|
||||
except CommandRunner.Exception as e:
|
||||
print(
|
||||
f"Error reverting repository to clean state: {e}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Create a GitHub issue to notify maintainer
|
||||
title = f"{self.path}: update to {new_version}"
|
||||
body = f"""## Description
|
||||
|
||||
There is a new version of `{self.name}` {self.kind} available.
|
||||
|
||||
New version: [{new_version}]({status["head_url"]})
|
||||
Check out the [list of changes]({status["compare_url"]}).
|
||||
"""
|
||||
|
||||
print("Creating GitHub issue", file=sys.stderr)
|
||||
print(f"{title}\n\n{body}", file=sys.stderr)
|
||||
GitHub.create_issue(title, body)
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
|
||||
def __update_yaml(self, new_version: str) -> None:
|
||||
dep_yaml = DependencyStore.update_dependency_version(self.path, new_version)
|
||||
DependencyStore.write_store(DEPS_YAML_FILE, dep_yaml)
|
||||
|
||||
def __apply_upstream_changes(self) -> None:
|
||||
# Patterns to ignore in copying files from upstream repo
|
||||
GLOBAL_IGNORE = [".git", ".github", ".gitignore"]
|
||||
|
||||
path = os.path.abspath(self.path)
|
||||
precopy = self.values.get("precopy")
|
||||
postcopy = self.values.get("postcopy")
|
||||
|
||||
repo = self.values["repo"]
|
||||
branch = self.values["branch"]
|
||||
remote_url = f"https://github.com/{repo}.git"
|
||||
repo_dir = os.path.join(TMP_DIR, repo)
|
||||
|
||||
# Clone repository
|
||||
Git.clone(remote_url, branch, repo_dir, reclone=True)
|
||||
|
||||
# Run precopy on tmp repo
|
||||
if precopy is not None:
|
||||
print("Running precopy script:", end="\n ", file=sys.stderr)
|
||||
print(
|
||||
precopy.replace("\n", "\n ", precopy.count("\n") - 1), file=sys.stderr
|
||||
)
|
||||
CommandRunner.run_or_fail(
|
||||
["bash", "-c", precopy], cwd=repo_dir, stage="Precopy"
|
||||
)
|
||||
|
||||
# Copy files from upstream repo
|
||||
print(f"Copying files from {repo_dir} to {path}", file=sys.stderr)
|
||||
shutil.copytree(
|
||||
repo_dir,
|
||||
path,
|
||||
dirs_exist_ok=True,
|
||||
ignore=shutil.ignore_patterns(*GLOBAL_IGNORE),
|
||||
)
|
||||
|
||||
# Run postcopy on our repository
|
||||
if postcopy is not None:
|
||||
print("Running postcopy script:", end="\n ", file=sys.stderr)
|
||||
print(
|
||||
postcopy.replace("\n", "\n ", postcopy.count("\n") - 1),
|
||||
file=sys.stderr,
|
||||
)
|
||||
CommandRunner.run_or_fail(
|
||||
["bash", "-c", postcopy], cwd=path, stage="Postcopy"
|
||||
)
|
||||
|
||||
|
||||
class Git:
|
||||
default_branch = "master"
|
||||
|
||||
@staticmethod
|
||||
def clone(remote_url: str, branch: str, repo_dir: str, reclone=False):
|
||||
# If repo needs to be fresh
|
||||
if reclone and os.path.exists(repo_dir):
|
||||
shutil.rmtree(repo_dir)
|
||||
|
||||
# Clone repo in tmp directory and checkout branch
|
||||
if not os.path.exists(repo_dir):
|
||||
print(
|
||||
f"Cloning {remote_url} to {repo_dir} and checking out {branch}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
CommandRunner.run_or_fail(
|
||||
["git", "clone", "--depth=1", "-b", branch, remote_url, repo_dir],
|
||||
stage="Clone",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def checkout_or_create_branch(branch_name: str):
|
||||
# Get current branch name
|
||||
result = CommandRunner.run_or_fail(
|
||||
["git", "rev-parse", "--abbrev-ref", "HEAD"], stage="GetDefaultBranch"
|
||||
)
|
||||
Git.default_branch = result.stdout.decode("utf-8").strip()
|
||||
|
||||
# Create new branch and return created branch name
|
||||
try:
|
||||
# try to checkout already existing branch
|
||||
CommandRunner.run_or_fail(
|
||||
["git", "checkout", branch_name], stage="CreateBranch"
|
||||
)
|
||||
except CommandRunner.Exception:
|
||||
# otherwise create new branch
|
||||
CommandRunner.run_or_fail(
|
||||
["git", "checkout", "-b", branch_name], stage="CreateBranch"
|
||||
)
|
||||
return branch_name
|
||||
|
||||
@staticmethod
|
||||
def repo_is_clean() -> bool:
|
||||
"""
|
||||
Returns `True` if the repo is clean.
|
||||
Returns `False` if the repo is dirty.
|
||||
"""
|
||||
try:
|
||||
CommandRunner.run_or_fail(
|
||||
["git", "diff", "--exit-code"], stage="CheckRepoClean"
|
||||
)
|
||||
return True
|
||||
except CommandRunner.Exception:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def add_and_commit(scope: str, version: str) -> bool:
|
||||
"""
|
||||
Returns `True` if there were changes and were indeed commited.
|
||||
Returns `False` if the repo was clean and no changes were commited.
|
||||
"""
|
||||
if Git.repo_is_clean():
|
||||
return False
|
||||
|
||||
user_name = os.environ.get("GIT_APP_NAME")
|
||||
user_email = os.environ.get("GIT_APP_EMAIL")
|
||||
|
||||
# Add all files to git staging
|
||||
CommandRunner.run_or_fail(["git", "add", "-A", "-v"], stage="AddFiles")
|
||||
|
||||
# Reset environment and git config
|
||||
clean_env = os.environ.copy()
|
||||
clean_env["LANG"] = "C.UTF-8"
|
||||
clean_env["GIT_CONFIG_GLOBAL"] = "/dev/null"
|
||||
clean_env["GIT_CONFIG_NOSYSTEM"] = "1"
|
||||
|
||||
# Commit with settings above
|
||||
CommandRunner.run_or_fail(
|
||||
[
|
||||
"git",
|
||||
"-c",
|
||||
f"user.name={user_name}",
|
||||
"-c",
|
||||
f"user.email={user_email}",
|
||||
"commit",
|
||||
"-m",
|
||||
f"chore({scope}): update to {version}",
|
||||
],
|
||||
stage="CreateCommit",
|
||||
env=clean_env,
|
||||
)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def push(branch: str):
|
||||
CommandRunner.run_or_fail(
|
||||
["git", "push", "-u", "origin", branch], stage="PushBranch"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def clean_repo():
|
||||
CommandRunner.run_or_fail(
|
||||
["git", "reset", "--hard", "HEAD"], stage="ResetRepository"
|
||||
)
|
||||
CommandRunner.run_or_fail(
|
||||
["git", "checkout", Git.default_branch], stage="CheckoutDefaultBranch"
|
||||
)
|
||||
|
||||
|
||||
class GitHub:
|
||||
@staticmethod
|
||||
def check_newer_tag(repo, current_tag) -> UpdateStatusFalse | UpdateStatusTrue:
|
||||
# GET /repos/:owner/:repo/git/refs/tags
|
||||
url = f"https://api.github.com/repos/{repo}/git/refs/tags"
|
||||
|
||||
# Send a GET request to the GitHub API
|
||||
response = requests.get(url)
|
||||
current_version = coerce(current_tag)
|
||||
if current_version is None:
|
||||
raise ValueError(
|
||||
f"Stored {current_version} from {repo} does not follow semver"
|
||||
)
|
||||
|
||||
# If the request was successful
|
||||
if response.status_code == 200:
|
||||
# Parse the JSON response
|
||||
data = response.json()
|
||||
|
||||
if len(data) == 0:
|
||||
return {
|
||||
"has_updates": False,
|
||||
}
|
||||
|
||||
latest_ref = None
|
||||
latest_version: Optional[Version] = None
|
||||
for ref in data:
|
||||
# we find the tag since GitHub returns it as plain git ref
|
||||
tag_version = coerce(ref["ref"].replace("refs/tags/", ""))
|
||||
if tag_version is None:
|
||||
# we skip every tag that is not semver-complaint
|
||||
continue
|
||||
if latest_version is None or tag_version.compare(latest_version) > 0:
|
||||
# if we have a "greater" semver version, set it as latest
|
||||
latest_version = tag_version
|
||||
latest_ref = ref
|
||||
|
||||
# raise if no valid semver tag is found
|
||||
if latest_ref is None or latest_version is None:
|
||||
raise ValueError(f"No tags following semver found in {repo}")
|
||||
|
||||
# we get the tag since GitHub returns it as plain git ref
|
||||
latest_tag = latest_ref["ref"].replace("refs/tags/", "")
|
||||
|
||||
if latest_version.compare(current_version) <= 0:
|
||||
return {
|
||||
"has_updates": False,
|
||||
}
|
||||
|
||||
return {
|
||||
"has_updates": True,
|
||||
"version": latest_tag,
|
||||
"compare_url": f"https://github.com/{repo}/compare/{current_tag}...{latest_tag}",
|
||||
"head_ref": latest_ref["object"]["sha"],
|
||||
"head_url": f"https://github.com/{repo}/releases/tag/{latest_tag}",
|
||||
}
|
||||
else:
|
||||
# If the request was not successful, raise an exception
|
||||
raise Exception(
|
||||
f"GitHub API request failed with status code {response.status_code}: {response.json()}"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def check_updates(repo, branch, version) -> UpdateStatusFalse | UpdateStatusTrue:
|
||||
url = f"https://api.github.com/repos/{repo}/compare/{version}...{branch}"
|
||||
|
||||
# Send a GET request to the GitHub API
|
||||
response = requests.get(url)
|
||||
|
||||
# If the request was successful
|
||||
if response.status_code == 200:
|
||||
# Parse the JSON response
|
||||
data = response.json()
|
||||
|
||||
# If the base is behind the head, there is a newer version
|
||||
has_updates = data["status"] != "identical"
|
||||
|
||||
if not has_updates:
|
||||
return {
|
||||
"has_updates": False,
|
||||
}
|
||||
|
||||
return {
|
||||
"has_updates": data["status"] != "identical",
|
||||
"version": data["commits"][-1]["sha"],
|
||||
"compare_url": data["permalink_url"],
|
||||
"head_ref": data["commits"][-1]["sha"],
|
||||
"head_url": data["commits"][-1]["html_url"],
|
||||
}
|
||||
else:
|
||||
# If the request was not successful, raise an exception
|
||||
raise Exception(
|
||||
f"GitHub API request failed with status code {response.status_code}: {response.json()}"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def create_issue(title: str, body: str) -> None:
|
||||
cmd = ["gh", "issue", "create", "-t", title, "-b", body]
|
||||
CommandRunner.run_or_fail(cmd, stage="CreateIssue")
|
||||
|
||||
@staticmethod
|
||||
def create_pr(branch: str, title: str, body: str) -> None:
|
||||
# first of all let's check if PR is already open
|
||||
check_cmd = [
|
||||
"gh",
|
||||
"pr",
|
||||
"list",
|
||||
"--state",
|
||||
"open",
|
||||
"--head",
|
||||
branch,
|
||||
"--json",
|
||||
"title",
|
||||
]
|
||||
# returncode is 0 also if no PRs are found
|
||||
output = json.loads(
|
||||
CommandRunner.run_or_fail(check_cmd, stage="CheckPullRequestOpen")
|
||||
.stdout.decode("utf-8")
|
||||
.strip()
|
||||
)
|
||||
# we have PR in this case!
|
||||
if len(output) > 0:
|
||||
return
|
||||
cmd = [
|
||||
"gh",
|
||||
"pr",
|
||||
"create",
|
||||
"-B",
|
||||
Git.default_branch,
|
||||
"-H",
|
||||
branch,
|
||||
"-t",
|
||||
title,
|
||||
"-b",
|
||||
body,
|
||||
]
|
||||
CommandRunner.run_or_fail(cmd, stage="CreatePullRequest")
|
||||
|
||||
|
||||
def main():
|
||||
# Load the YAML file
|
||||
with open(DEPS_YAML_FILE, "r") as yaml_file:
|
||||
data: DependencyYAML = yaml.safe_load(yaml_file)
|
||||
|
||||
if "dependencies" not in data:
|
||||
raise Exception("dependencies.yml not properly formatted")
|
||||
|
||||
# Cache YAML version
|
||||
DependencyStore.set(data)
|
||||
|
||||
dependencies = data["dependencies"]
|
||||
for path in dependencies:
|
||||
dependency = Dependency(path, dependencies[path])
|
||||
dependency.update_or_notify()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
66
zsh/.oh-my-zsh/.github/workflows/installer.yml
vendored
Normal file
66
zsh/.oh-my-zsh/.github/workflows/installer.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
name: Test and Deploy installer
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
push:
|
||||
paths:
|
||||
- 'tools/install.sh'
|
||||
- '.github/workflows/installer/**'
|
||||
- '.github/workflows/installer.yml'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: read # to checkout
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test installer
|
||||
if: github.repository == 'ohmyzsh/ohmyzsh'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Set up git repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- name: Install zsh
|
||||
if: runner.os == 'Linux'
|
||||
run: sudo apt-get update; sudo apt-get install zsh
|
||||
- name: Test installer
|
||||
run: sh ./tools/install.sh
|
||||
|
||||
deploy:
|
||||
name: Deploy installer in install.ohmyz.sh
|
||||
if: github.ref == 'refs/heads/master'
|
||||
runs-on: ubuntu-latest
|
||||
environment: vercel
|
||||
needs:
|
||||
- test
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- name: Install Vercel CLI
|
||||
run: npm install -g vercel
|
||||
- name: Setup project and deploy
|
||||
env:
|
||||
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
|
||||
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
|
||||
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
|
||||
run: |
|
||||
cp tools/install.sh .github/workflows/installer/install.sh
|
||||
cd .github/workflows/installer
|
||||
vc deploy --prod -t "$VERCEL_TOKEN"
|
||||
1
zsh/.oh-my-zsh/.github/workflows/installer/.gitignore
vendored
Normal file
1
zsh/.oh-my-zsh/.github/workflows/installer/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
install.sh
|
||||
2
zsh/.oh-my-zsh/.github/workflows/installer/.vercelignore
vendored
Normal file
2
zsh/.oh-my-zsh/.github/workflows/installer/.vercelignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/*
|
||||
!/install.sh
|
||||
23
zsh/.oh-my-zsh/.github/workflows/installer/vercel.json
vendored
Normal file
23
zsh/.oh-my-zsh/.github/workflows/installer/vercel.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"headers": [
|
||||
{
|
||||
"source": "/(|install.sh)",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "text/plain"
|
||||
},
|
||||
{
|
||||
"key": "Content-Disposition",
|
||||
"value": "inline; filename=\"install.sh\""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "/",
|
||||
"destination": "/install.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
43
zsh/.oh-my-zsh/.github/workflows/main.yml
vendored
Normal file
43
zsh/.oh-my-zsh/.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: CI
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
branches:
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Run tests
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'ohmyzsh/ohmyzsh'
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Set up git repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- name: Install zsh
|
||||
run: sudo apt-get update; sudo apt-get install zsh
|
||||
- name: Check syntax
|
||||
run: |
|
||||
for file in ./oh-my-zsh.sh \
|
||||
./lib/*.zsh \
|
||||
./plugins/*/*.plugin.zsh \
|
||||
./plugins/*/_* \
|
||||
./themes/*.zsh-theme; do
|
||||
zsh -n "$file" || return 1
|
||||
done
|
||||
155
zsh/.oh-my-zsh/.github/workflows/project.yml
vendored
Normal file
155
zsh/.oh-my-zsh/.github/workflows/project.yml
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
name: Project tracking
|
||||
on:
|
||||
issues:
|
||||
types: [opened, reopened]
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add to project
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'ohmyzsh/ohmyzsh'
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
- name: Authenticate as @ohmyzsh
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||
with:
|
||||
app-id: ${{ secrets.OHMYZSH_APP_ID }}
|
||||
private-key: ${{ secrets.OHMYZSH_APP_PRIVATE_KEY }}
|
||||
- name: Read project data
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
ORGANIZATION: ohmyzsh
|
||||
PROJECT_NUMBER: "1"
|
||||
run: |
|
||||
# Get Project data
|
||||
gh api graphql -f query='
|
||||
query($org: String!, $number: Int!) {
|
||||
organization(login: $org){
|
||||
projectV2(number: $number) {
|
||||
id
|
||||
fields(first:20) {
|
||||
nodes {
|
||||
... on ProjectV2Field {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
|
||||
|
||||
# Parse project data
|
||||
cat >> "$GITHUB_ENV" <<EOF
|
||||
PROJECT_ID=$(jq '.data.organization.projectV2.id' project_data.json)
|
||||
PLUGIN_FIELD_ID=$(jq '.data.organization.projectV2.fields.nodes[] | select(.name == "Plugin") | .id' project_data.json)
|
||||
THEME_FIELD_ID=$(jq '.data.organization.projectV2.fields.nodes[] | select(.name == "Theme") | .id' project_data.json)
|
||||
EOF
|
||||
- name: Add to project
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
ISSUE_OR_PR_ID: ${{ github.event.issue.node_id || github.event.pull_request.node_id }}
|
||||
run: |
|
||||
item_id="$(gh api graphql -f query='
|
||||
mutation($project: ID!, $content: ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $project, contentId: $content}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
' -f project="$PROJECT_ID" -f content="$ISSUE_OR_PR_ID" --jq '.data.addProjectV2ItemById.item.id')"
|
||||
|
||||
echo "ITEM_ID=$item_id" >> $GITHUB_ENV
|
||||
- name: Classify Pull Request
|
||||
if: github.event_name == 'pull_request_target'
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
# Get the list of modified files in the PR, and extract plugins and themes
|
||||
gh pr view "$PR_NUMBER" \
|
||||
--repo "$GITHUB_REPOSITORY" \
|
||||
--json files --jq '.files.[].path' | awk -F/ '
|
||||
BEGIN {
|
||||
plugins = 0
|
||||
themes = 0
|
||||
}
|
||||
/^plugins\// {
|
||||
if (plugin == $2) next
|
||||
plugin = $2
|
||||
plugins++
|
||||
}
|
||||
/^themes\// {
|
||||
gsub(/\.zsh-theme$/, "", $2)
|
||||
if (theme == $2) next
|
||||
theme = $2
|
||||
themes++
|
||||
}
|
||||
END {
|
||||
# plugin and theme are values controlled by the PR author
|
||||
# so we should sanitize them before using anywhere else
|
||||
if (plugins == 1) {
|
||||
gsub(/[^a-zA-Z0-9._-]/, "", plugin)
|
||||
print "PLUGIN=" plugin
|
||||
}
|
||||
if (themes == 1) {
|
||||
gsub(/[^a-zA-Z0-9._-]/, "", theme)
|
||||
print "THEME=" theme
|
||||
}
|
||||
}
|
||||
' >> "$GITHUB_ENV"
|
||||
- name: Fill Pull Request fields in project
|
||||
if: github.event_name == 'pull_request_target'
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
run: |
|
||||
gh api graphql -f query='
|
||||
mutation (
|
||||
$project: ID!
|
||||
$item: ID!
|
||||
$plugin_field: ID!
|
||||
$plugin_value: String!
|
||||
$theme_field: ID!
|
||||
$theme_value: String!
|
||||
) {
|
||||
set_plugin: updateProjectV2ItemFieldValue(input: {
|
||||
projectId: $project
|
||||
itemId: $item
|
||||
fieldId: $plugin_field
|
||||
value: {
|
||||
text: $plugin_value
|
||||
}
|
||||
}) {
|
||||
projectV2Item {
|
||||
id
|
||||
}
|
||||
}
|
||||
set_theme: updateProjectV2ItemFieldValue(input: {
|
||||
projectId: $project
|
||||
itemId: $item
|
||||
fieldId: $theme_field
|
||||
value: {
|
||||
text: $theme_value
|
||||
}
|
||||
}) {
|
||||
projectV2Item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
' -f project="$PROJECT_ID" -f item="$ITEM_ID" \
|
||||
-f plugin_field="$PLUGIN_FIELD_ID" -f plugin_value="$PLUGIN" \
|
||||
-f theme_field="$THEME_FIELD_ID" -f theme_value="$THEME" \
|
||||
--silent
|
||||
65
zsh/.oh-my-zsh/.github/workflows/scorecard.yml
vendored
Normal file
65
zsh/.oh-my-zsh/.github/workflows/scorecard.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
# 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.
|
||||
|
||||
name: Scorecard supply-chain security
|
||||
on:
|
||||
# For Branch-Protection check. Only the default branch is supported. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
|
||||
branch_protection_rule:
|
||||
# To guarantee Maintained check is occasionally updated. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
||||
schedule:
|
||||
- cron: '20 7 * * 2'
|
||||
push:
|
||||
branches: ["master"]
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
name: Scorecard analysis
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload the results to code-scanning dashboard.
|
||||
security-events: write
|
||||
# Needed to publish results and get a badge (see publish_results below).
|
||||
id-token: write
|
||||
contents: read
|
||||
actions: read
|
||||
# To allow GraphQL ListCommits to work
|
||||
issues: read
|
||||
pull-requests: read
|
||||
# To detect SAST tools
|
||||
checks: read
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
publish_results: true
|
||||
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
Reference in New Issue
Block a user