├── .dprintrc.json ├── .github └── workflows │ └── deno.yml ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── assets └── logo.svg ├── deps.ts ├── get-release-url.ts ├── mod.ts ├── renovate.json └── test.ts /.dprintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://dprint.dev/schemas/v0.json", 3 | "projectType": "openSource", 4 | "incremental": true, 5 | "typescript": { 6 | "semiColons": "asi" 7 | }, 8 | "json": {}, 9 | "markdown": {}, 10 | "useTabs": false, 11 | "indentWidth": 4, 12 | "includes": [ 13 | "**/*.{ts,tsx,js,jsx,json,md,rs}" 14 | ], 15 | "excludes": [ 16 | "**/node_modules", 17 | "**/*-lock.json", 18 | "**/target", 19 | "**/examples" 20 | ], 21 | "plugins": [ 22 | "https://plugins.dprint.dev/typescript-0.40.1.wasm", 23 | "https://plugins.dprint.dev/json-0.8.0.wasm", 24 | "https://plugins.dprint.dev/markdown-0.5.1.wasm" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/deno.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Deno and run `deno lint` on a stable Ubuntu build. 2 | 3 | name: Deno 4 | 5 | on: 6 | push: 7 | branches: [main] 8 | pull_request: 9 | branches: [main] 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Setup repo 17 | uses: actions/checkout@v2 18 | 19 | - uses: denolib/setup-deno@v2 20 | with: 21 | deno-version: v1.x 22 | 23 | - name: Cache Dependencies 24 | run: deno cache --unstable deps.ts 25 | 26 | - name: Lint 27 | run: deno lint --unstable 28 | 29 | - name: Test 30 | run: deno test --allow-net -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.import_intellisense_origins": { 4 | "https://deno.land": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Khushraj Rathod (me@khushrajrathod.com) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | cloud download to man holding cup illustration 3 |

Get Release URL

4 |

5 | Get the latest release URL from any hosting provider. Supports pattern matching 6 |

7 |

8 | build status 9 | language 10 | code size 11 | issues 12 | license 13 | version 14 |

15 |

16 | View on deno.land 17 |

18 |
19 |
20 |
21 |
22 | 23 | ## Table of Contents 24 | 25 | - [Usage](#usage) 26 | - [Custom Provider Methods](#custom-provider-methods) 27 | - [API](#api) 28 | - [CLI](#cli) 29 | - [Quickstart](#quickstart) 30 | - [Installation](#installation) 31 | - [Running](#running) 32 | 33 | ## Usage 34 | 35 | ```ts 36 | import getReleaseURL from "https://deno.land/x/get_release_url@1.0.0/mod.ts" 37 | 38 | const urls = await getReleaseURL({ 39 | provider: "github", 40 | user: "phhusson", 41 | repo: "treble_experimentations", 42 | part: "arm64-ab-gapps", 43 | }) 44 | 45 | for (const url of urls) { 46 | console.log(url) 47 | } 48 | ``` 49 | 50 | ### Custom provider methods 51 | 52 | Default supported providers are [GitHub](https://github.com) and [BitBucket](https://bitbucket.org). You can add custom provider functions using `addProviderMethod` (PRs for more defaults are welcome!) 53 | 54 | ```ts 55 | import getReleaseURL, { 56 | addProviderMethod, 57 | } from "https://deno.land/x/get_release_url@1.0.0/mod.ts" 58 | 59 | const customGithubProvider = async ( 60 | { user, repo, part = "" }: { user: string; repo: string; part?: string }, 61 | ) => { 62 | const response = await fetch( 63 | `https://api.github.com/repos/${user}/${repo}/releases/latest`, 64 | ) 65 | const json = await response.json() 66 | 67 | if (json.message === "Not Found") throw new Error("Invalid repository") 68 | if (!("assets" in json)) throw new Error("Rate limit exceeded") 69 | 70 | let browser_download_urls: string[] = json.assets.map(( 71 | asset: { browser_download_url: string }, 72 | ) => asset.browser_download_url) 73 | return browser_download_urls.filter((url) => url.includes(part)) 74 | } 75 | 76 | addProviderMethod("github", customGithubProvider) 77 | 78 | await getReleaseURL({ 79 | provider: "github", 80 | user: "phhusson", 81 | repo: "treble_experimentations", 82 | part: "arm64-ab-gapps", 83 | }) // Uses custom method 84 | ``` 85 | 86 | ### API 87 | 88 | See [generated documentation](https://doc.deno.land/https/deno.land/x/get_release_url@1.0.0/mod.ts) 89 | 90 | ## CLI 91 | 92 | ### Quickstart 93 | 94 | ```bash 95 | deno run --allow-net https://deno.land/x/get_release_url@1.0.0/get-release-url.ts github phhusson treble_experimentations arm64-aonly 96 | ``` 97 | 98 | ### Installation 99 | 100 | ```bash 101 | deno install --allow-net https://deno.land/x/get_release_url@1.0.0/get-release-url.ts 102 | ``` 103 | 104 | ### Running 105 | 106 | ```bash 107 | get-release github phhusson treble_experimentations arm64-aonly 108 | ``` 109 | 110 | ### Usage 111 | 112 | ``` 113 | Usage: get-release (github|bitbucket) user repo [partofreleasefile] 114 | Ex: get-release github phhusson treble_experimentations 115 | get-release github phhusson treble_experimentations arm64-ab-gapps 116 | get-release bitbucket JesusFreke smali 117 | get-release bitbucket JesusFreke smali baksmali 118 | ``` 119 | 120 | ## Supporters 121 | 122 | [![Stargazers repo roster for @khrj/get-release-url](https://reporoster.com/stars/khrj/get-release-url)](https://github.com/khrj/get-release-url/stargazers) 123 | 124 | [![Forkers repo roster for @khrj/get-release-url](https://reporoster.com/forks/khrj/get-release-url)](https://github.com/khrj/get-release-url/network/members) 125 | 126 | ## Related 127 | 128 | - [Deno modules](https://github.com/khrj/deno-modules) 129 | -------------------------------------------------------------------------------- /assets/logo.svg: -------------------------------------------------------------------------------- 1 | downloading -------------------------------------------------------------------------------- /deps.ts: -------------------------------------------------------------------------------- 1 | export { assertEquals } from "https://deno.land/std@0.87.0/testing/asserts.ts" 2 | -------------------------------------------------------------------------------- /get-release-url.ts: -------------------------------------------------------------------------------- 1 | import getReleaseURL from "./mod.ts" 2 | const usage = () => { 3 | console.log( 4 | `Usage: get-release-url (github|bitbucket) user repo [partofreleasefile] 5 | Ex: get-release-url github phhusson treble_experimentations 6 | get-release-url github phhusson treble_experimentations arm64-ab-gapps 7 | get-release-url bitbucket JesusFreke smali 8 | get-release-url bitbucket JesusFreke smali baksmali`, 9 | ) 10 | Deno.exit(1) 11 | } 12 | 13 | if (Deno.args.length !== 3 && Deno.args.length !== 4) { 14 | usage() 15 | } 16 | 17 | try { 18 | const urls = await getReleaseURL({ 19 | provider: Deno.args[0], 20 | user: Deno.args[1], 21 | repo: Deno.args[2], 22 | part: Deno.args[3], 23 | }) 24 | 25 | for (const url of urls) { 26 | console.log(url) 27 | } 28 | } catch (e) { 29 | console.log(e.message) 30 | usage() 31 | } 32 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | const normalize = (string: string) => { 2 | try { 3 | return string.toUpperCase().trim() 4 | } catch (e) { 5 | return string 6 | } 7 | } 8 | 9 | const providerMethods: Record< 10 | string, 11 | ( 12 | { user, repo }: { user: string; repo: string; part?: string }, 13 | ) => Promise 14 | > = { 15 | GITHUB: async ({ user, repo, part = "" }: { user: string; repo: string; part?: string }) => { 16 | const response = await fetch(`https://api.github.com/repos/${user}/${repo}/releases/latest`) 17 | const json = await response.json() 18 | 19 | if (json.message === "Not Found") throw new Error("Invalid repository") 20 | if (!("assets" in json)) throw new Error("Rate limit exceeded") 21 | 22 | const browserDownloadUrls: string[] = json.assets.map((asset: { browser_download_url: string }) => 23 | asset.browser_download_url 24 | ) 25 | return browserDownloadUrls.filter((url) => url.includes(part)) 26 | }, 27 | BITBUCKET: async ({ user, repo, part = "" }: { user: string; repo: string; part?: string }) => { 28 | const response = await fetch(`https://api.bitbucket.org/2.0/repositories/${user}/${repo}/downloads/`) 29 | const json = await response.json() 30 | 31 | if (json.type === "error") throw "Invalid repository" 32 | const links: string[] = json.values.map((value: { links: { self: { href: string } } }) => value.links.self.href) 33 | return links.filter(url => url.includes(part)) 34 | }, 35 | } 36 | 37 | /** 38 | * Fetches one or many matching release URLs from the selected provider 39 | * @param options.provider The provider to fetch from. Available options are 40 | * 'github', 'bitbucket' and any others added via `addProviderMethod` 41 | * @param options.user Username to fetch from. This is the user name / org name 42 | * of the repository owner 43 | * @param options.repo Repository to fetch releases from 44 | * @param options.part Part of the name of release assets to filter. Eg. if a 45 | * release contains 3 assets, 'hi-1', 'hi-2', and '3', passing 'hi' as the part 46 | * will only return 'hi-1' and 'hi-2' 47 | */ 48 | export default async function getReleaseURL(options: { provider: string; user: string; repo: string; part?: string }) { 49 | const providerNormalized = normalize(options.provider) 50 | if (!(providerNormalized in providerMethods)) { 51 | throw new Error("Invalid provider") 52 | } 53 | 54 | return await providerMethods[providerNormalized]({ user: options.user, repo: options.repo, part: options.part || "" }) 55 | } 56 | 57 | /** 58 | * @param provider Provider for the method. You can override existing providers 59 | * if needed 60 | * @param method Custom method 61 | */ 62 | export function addProviderMethod( 63 | provider: string, 64 | method: ( 65 | { user, repo, part }: { user: string; repo: string; part?: string }, 66 | ) => Promise, 67 | ) { 68 | providerMethods[normalize(provider)] = method 69 | } 70 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "github>khrj/khrj" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /test.ts: -------------------------------------------------------------------------------- 1 | // See https://deno.land/manual/testing 2 | import { assertEquals } from "./deps.ts" 3 | import getRelease, { addProviderMethod } from "./mod.ts" 4 | 5 | Deno.test("Fetch all assets", async () => { 6 | const correct = [ 7 | "https://github.com/khrj/khrj/releases/download/1.0.0/3.txt", 8 | "https://github.com/khrj/khrj/releases/download/1.0.0/hi-1.txt", 9 | "https://github.com/khrj/khrj/releases/download/1.0.0/hi-2.txt", 10 | ].sort() 11 | 12 | const urls = await getRelease({ 13 | provider: "github", 14 | user: "khrj", 15 | repo: "khrj", 16 | }) 17 | 18 | assertEquals(correct, urls.sort()) 19 | }) 20 | 21 | Deno.test("Fetch matched assets", async () => { 22 | const correct = [ 23 | "https://github.com/khrj/khrj/releases/download/1.0.0/hi-1.txt", 24 | "https://github.com/khrj/khrj/releases/download/1.0.0/hi-2.txt", 25 | ].sort() 26 | 27 | const urls = await getRelease({ 28 | provider: "github", 29 | user: "khrj", 30 | repo: "khrj", 31 | part: "hi", 32 | }) 33 | 34 | assertEquals(correct, urls.sort()) 35 | }) 36 | 37 | Deno.test("Custom fetch", async () => { 38 | addProviderMethod("myProvider", ({ user, repo, part }) => { 39 | assertEquals(user, "user") 40 | assertEquals(repo, "repo") 41 | assertEquals(part, "part") 42 | 43 | return Promise.resolve(["hi"]) 44 | }) 45 | 46 | const correct = ["hi"] 47 | 48 | const urls = await getRelease({ 49 | provider: "myProvider", 50 | user: "user", 51 | repo: "repo", 52 | part: "part", 53 | }) 54 | 55 | assertEquals(urls, correct) 56 | }) 57 | --------------------------------------------------------------------------------