├── .github ├── ISSUE_TEMPLATE.md ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── pr.yml │ └── master.yml ├── pkgs └── example.txt ├── environment.yml ├── README.md └── mark_broken.py /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @conda-forge/core 2 | -------------------------------------------------------------------------------- /pkgs/example.txt: -------------------------------------------------------------------------------- 1 | win-64/cf-autotick-bot-test-package-0.4-py38_0.tar.bz2 2 | win-64/cf-autotick-bot-test-package-0.4-py27_0.tar.bz2 3 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: cf 2 | channels: 3 | - conda-forge 4 | - nodefaults 5 | dependencies: 6 | - anaconda-client 7 | - python=3.7 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Mark packages as broken on conda-forge** 2 | 3 | If you want to mark a package as broken on conda-forge, send a Pull Request 4 | adding a new `.txt` file in `pkgs/` with a list of full names of the packages 5 | to be moved from `main` to `broken`. See `pkgs/example.txt` for an example. 6 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Checklist: 2 | 3 | * [ ] Added a link to the relevant issue in the PR description. 4 | * [ ] Pinged the team for the package. 5 | 10 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: Check Packages Exist on Main 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | jobs: 7 | check: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: goanpeca/setup-miniconda@v1 12 | with: 13 | activate-environment: cf 14 | environment-file: environment.yml 15 | auto-activate-base: true 16 | - name: Check packages exist on main 17 | shell: bash -l {0} 18 | run: | 19 | conda activate cf 20 | python mark_broken.py check 21 | -------------------------------------------------------------------------------- /.github/workflows/master.yml: -------------------------------------------------------------------------------- 1 | name: Mark Packages as Broken 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | check: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: goanpeca/setup-miniconda@v1 12 | with: 13 | activate-environment: cf 14 | environment-file: environment.yml 15 | auto-activate-base: true 16 | - name: Mark packages as broken 17 | shell: bash -l {0} 18 | run: | 19 | conda activate cf 20 | git config --global user.name "conda-forge-admin" 21 | git config --global user.email "pelson.pub+conda-forge@gmail.com" 22 | python mark_broken.py mark 23 | env: 24 | BINSTAR_TOKEN: ${{ secrets.BINSTAR_TOKEN }} 25 | GITHUB_TOKEN: ${{ secrets.CF_GITHUB_TOKEN }} 26 | - name: Push changes 27 | uses: ad-m/github-push-action@master 28 | with: 29 | github_token: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /mark_broken.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from glob import glob 3 | import subprocess 4 | import os 5 | import tempfile 6 | import requests 7 | 8 | 9 | def split_pkg(pkg): 10 | if not pkg.endswith(".tar.bz2"): 11 | raise RuntimeError("Can only process packages that end in .tar.bz2") 12 | pkg = pkg[:-8] 13 | plat, pkg_name = pkg.split("/") 14 | name_ver, build = pkg_name.rsplit('-', 1) 15 | name, ver = name_ver.rsplit('-', 1) 16 | return plat, name, ver, build 17 | 18 | 19 | def get_files(): 20 | return [f for f in glob("pkgs/*") if f != "pkgs/example.txt"] 21 | 22 | 23 | def check_packages(): 24 | for file_name in get_files(): 25 | with open(file_name, "r") as f: 26 | pkgs = f.readlines() 27 | pkgs = [pkg.strip() for pkg in pkgs] 28 | for pkg in pkgs: 29 | # ignore blank lines or Python-style comments 30 | if pkg.startswith('#') or len(pkg) == 0: 31 | continue 32 | plat, name, ver, build = split_pkg(pkg) 33 | subprocess.check_call( 34 | f"CONDA_SUBDIR={plat} conda search {name}={ver}={build} " 35 | "-c conda-forge --override-channels", 36 | shell=True, 37 | ) 38 | 39 | 40 | token_path = os.path.expanduser( 41 | "~/.config/binstar/https%3A%2F%2Fapi.anaconda.org.token") 42 | 43 | 44 | def mark_broken_file(file_name): 45 | with open(file_name, "r") as f: 46 | pkgs = f.readlines() 47 | pkgs = [pkg.strip() for pkg in pkgs] 48 | for pkg in pkgs: 49 | # ignore blank lines or Python-style comments 50 | if pkg.startswith('#') or len(pkg) == 0: 51 | continue 52 | print(" package: %s" % pkg, flush=True) 53 | plat, name, ver, build = split_pkg(pkg) 54 | r = requests.post( 55 | "https://api.anaconda.org/channels/conda-forge/broken", 56 | headers={'Authorization': 'token {}'.format(os.environ["BINSTAR_TOKEN"])}, 57 | json={ 58 | "basename": pkg, 59 | "package": name, 60 | "version": ver, 61 | } 62 | ) 63 | if r.status_code != 201: 64 | print(" could not mark broken", flush=True) 65 | return 66 | else: 67 | print(" marked broken", flush=True) 68 | subprocess.check_call(f"git rm {file_name}", shell=True) 69 | subprocess.check_call( 70 | f"git commit -m 'Remove {file_name} after marking broken'", shell=True) 71 | subprocess.check_call("git show", shell=True) 72 | 73 | 74 | def mark_broken(): 75 | if "BINSTAR_TOKEN" not in os.environ: 76 | return 77 | 78 | files = get_files() 79 | print("found files: %s" % files, flush=True) 80 | for file_name in files: 81 | print("working on file %s" % file_name, flush=True) 82 | mark_broken_file(file_name) 83 | 84 | with tempfile.TemporaryDirectory() as tmpdir: 85 | subprocess.check_call( 86 | "git clone https://github.com/conda-forge/" 87 | "conda-forge-repodata-patches-feedstock.git", 88 | cwd=tmpdir, 89 | shell=True, 90 | ) 91 | 92 | subprocess.check_call( 93 | "git remote set-url --push origin " 94 | "https://${GITHUB_TOKEN}@github.com/conda-forge/" 95 | "conda-forge-repodata-patches-feedstock.git", 96 | cwd=os.path.join(tmpdir, "conda-forge-repodata-patches-feedstock"), 97 | shell=True, 98 | ) 99 | 100 | fstr = " ".join(f for f in files) 101 | subprocess.check_call( 102 | "git commit --allow-empty -am 'resync repo data " 103 | "for broken packages in files %s'" % fstr, 104 | cwd=os.path.join(tmpdir, "conda-forge-repodata-patches-feedstock"), 105 | shell=True, 106 | ) 107 | 108 | subprocess.check_call( 109 | "git push", 110 | cwd=os.path.join(tmpdir, "conda-forge-repodata-patches-feedstock"), 111 | shell=True, 112 | ) 113 | 114 | 115 | if __name__ == "__main__": 116 | if len(sys.argv) != 2: 117 | raise RuntimeError("Need 1 and only 1 argument") 118 | if sys.argv[1] == 'check': 119 | check_packages() 120 | elif sys.argv[1] == 'mark': 121 | mark_broken() 122 | else: 123 | raise RuntimeError(f"Unrecognized argument {sys.argv[1]}") 124 | --------------------------------------------------------------------------------