├── .github ├── ISSUE_TEMPLATE │ └── bug_report.yml ├── dependabot.yml └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .release-please-manifest.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __test__ ├── DownloadURL.test.ts ├── DownloadURLFactory.test.ts └── InstallerFactory.test.ts ├── action.yml ├── biome.json ├── package.json ├── pnpm-lock.yaml ├── release-please-config.json ├── src ├── DownloadURL.ts ├── DownloadURLFactory.ts ├── Installer.ts ├── InstallerFactory.ts ├── errors.ts ├── index.ts ├── platform.ts └── versions.ts └── tsconfig.json /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug Report 2 | description: File a bug report 3 | title: "🐞 " 4 | labels: ["bug"] 5 | projects: [] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for taking the time to fill out this bug report! 11 | - type: textarea 12 | attributes: 13 | label: What is happening? 14 | description: Please provide a clear and concise description of what the bug is. 15 | placeholder: A clear and concise description of what the bug is. 16 | validations: 17 | required: true 18 | - type: input 19 | attributes: 20 | label: Version where you found the bug 21 | description: What version of the action are you using? 22 | placeholder: vX.Y.Z (e.g. v1.0.0) or find it in the logs if you're using a latest version 23 | validations: 24 | required: true 25 | - type: input 26 | attributes: 27 | label: Runner specifics 28 | description: What runner are you using? Self-hosted, GitHub-hosted, or a specific cloud provider? 29 | placeholder: e.g. ubuntu-latest, macos-latest, windows-latest, Self-hosted, etc. 30 | validations: 31 | required: true 32 | - type: textarea 33 | attributes: 34 | label: Action workflow configuration 35 | description: Full workflow file content or a snippet that reproduces the issue. 36 | placeholder: | 37 | steps: 38 | - uses: browser-actions/setup-firefox@latest 39 | with: 40 | firefox-version: 'latest' 41 | render: yaml 42 | validations: 43 | required: true 44 | - type: input 45 | attributes: 46 | label: Link to the workflow run 47 | description: Please provide a link to the workflow run where you encountered the bug. 48 | placeholder: 'https://github.com/...' 49 | - type: textarea 50 | attributes: 51 | label: Relevant log output 52 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 53 | render: shell 54 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'npm' 4 | directory: '/' 5 | schedule: 6 | interval: 'daily' 7 | commit-message: 8 | prefix: "fix(deps)" 9 | prefix-development: 'chore(deps):' 10 | groups: 11 | actions: 12 | patterns: 13 | - "@actions/*" 14 | typescript: 15 | patterns: 16 | - "typescript" 17 | - "@typescript-*" 18 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: 'build-test' 2 | on: 3 | pull_request: 4 | push: 5 | branches-ignore: 6 | - master 7 | workflow_call: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: pnpm/action-setup@v3 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version-file: 'package.json' 18 | cache: 'pnpm' 19 | - run: pnpm install --frozen-lockfile 20 | - run: pnpm lint 21 | - run: pnpm test 22 | - run: pnpm build 23 | - run: pnpm package 24 | - uses: actions/upload-artifact@v4 25 | with: 26 | name: dist 27 | path: ./dist/ 28 | 29 | test: 30 | needs: [build] 31 | strategy: 32 | matrix: 33 | os: [ubuntu, macos, windows] 34 | runs-on: ${{ matrix.os }}-latest 35 | steps: 36 | - uses: actions/download-artifact@v4 37 | with: 38 | name: dist 39 | 40 | - uses: ./ 41 | - run: | 42 | firefox --version 43 | - uses: ./ 44 | with: 45 | firefox-version: latest-esr 46 | - run: | 47 | firefox --version 48 | - uses: ./ 49 | with: 50 | firefox-version: "132.0" 51 | - run: | 52 | firefox --version 53 | - uses: ./ 54 | with: 55 | firefox-version: "devedition-132.0b1" 56 | - run: | 57 | firefox --version 58 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: 'release' 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | build: 9 | uses: ./.github/workflows/build.yml 10 | 11 | release: 12 | runs-on: ubuntu-latest 13 | needs: [build] 14 | steps: 15 | - uses: googleapis/release-please-action@v4 16 | id: release 17 | 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | ref: 'latest' 22 | if: ${{ steps.release.outputs.release_created }} 23 | - uses: actions/download-artifact@v4 24 | with: 25 | name: dist 26 | if: ${{ steps.release.outputs.release_created }} 27 | 28 | - name: Publish to GitHub 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | major_version: ${{ steps.release.outputs.major }} 32 | full_version: ${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}.${{ steps.release.outputs.patch }} 33 | tag_name: ${{ steps.release.outputs.tag_name }} 34 | run: | 35 | gpg --allow-secret-key-import --import <<< "${{ secrets.PRIVATE_SIGNING_KEY }}" 36 | git remote set-url origin https://github-actions:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY} 37 | git config commit.gpgsign true 38 | git config tag.gpgsign true 39 | git config --global user.name ueokande 40 | git config --global user.email ueokande@i-beam.org 41 | git config --global user.signingkey 7F94715D031FDE95 42 | 43 | git add . 44 | git commit --allow-empty --message "Release v${full_version} at ${GITHUB_SHA}" 45 | 46 | git tag -d v$major_version || true 47 | git tag -d v$full_version || true 48 | git tag -a v$major_version -m "Release v$full_version" 49 | git tag -a v$full_version -m "Release v$full_version" 50 | 51 | git push origin latest 52 | git push origin v$major_version --force 53 | git push origin v$full_version 54 | 55 | if: ${{ steps.release.outputs.release_created }} 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | 3 | /dist/ 4 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": "1.5.4" 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.5.4](https://github.com/browser-actions/setup-firefox/compare/setup-firefox-v1.5.3...setup-firefox-v1.5.4) (2025-01-25) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * Fix an off-by-one error with bz2/xz download URLs ([#626](https://github.com/browser-actions/setup-firefox/issues/626)) ([f7574dd](https://github.com/browser-actions/setup-firefox/commit/f7574dd7c71ff2cf094cdaa89735bdcb096d72e5)), closes [#625](https://github.com/browser-actions/setup-firefox/issues/625) 9 | 10 | ## [1.5.3](https://github.com/browser-actions/setup-firefox/compare/setup-firefox-v1.5.2...setup-firefox-v1.5.3) (2025-01-19) 11 | 12 | 13 | ### Bug Fixes 14 | 15 | * Coping with compression change for linux download ([#623](https://github.com/browser-actions/setup-firefox/issues/623)) ([063eed6](https://github.com/browser-actions/setup-firefox/commit/063eed68fefb6e72be98fb0c0d2d106784c79634)) 16 | 17 | ## [1.5.2](https://github.com/browser-actions/setup-firefox/compare/setup-firefox-v1.5.1...setup-firefox-v1.5.2) (2024-07-13) 18 | 19 | 20 | ### Bug Fixes 21 | 22 | * sign commits and tags ([#583](https://github.com/browser-actions/setup-firefox/issues/583)) ([6bcb267](https://github.com/browser-actions/setup-firefox/commit/6bcb26740ce1d8eabe7396b856c49439c24ed139)) 23 | 24 | ## [1.5.1](https://github.com/browser-actions/setup-firefox/compare/setup-firefox-v1.5.0...setup-firefox-v1.5.1) (2024-05-04) 25 | 26 | 27 | ### Bug Fixes 28 | 29 | * Adding arm64 to osx for macos-14 ([#546](https://github.com/browser-actions/setup-firefox/issues/546)) ([d880b17](https://github.com/browser-actions/setup-firefox/commit/d880b175f181fe0d1fbad6629de56872bfaf0146)) 30 | 31 | ## [1.5.0](https://github.com/browser-actions/setup-firefox/compare/setup-firefox-v1.4.1...setup-firefox-v1.5.0) (2024-02-29) 32 | 33 | 34 | ### Features 35 | 36 | * firefox devedition archive versions ([#517](https://github.com/browser-actions/setup-firefox/issues/517)) ([eb151a7](https://github.com/browser-actions/setup-firefox/commit/eb151a78a71e39af170d8510d66b4e7204853a94)) 37 | 38 | ## [1.4.1](https://github.com/browser-actions/setup-firefox/compare/setup-firefox-v1.4.0...setup-firefox-v1.4.1) (2024-02-17) 39 | 40 | 41 | ### Bug Fixes 42 | 43 | * using node20 ([#520](https://github.com/browser-actions/setup-firefox/issues/520)) ([b7d1295](https://github.com/browser-actions/setup-firefox/commit/b7d1295db99649993e97d0a01870e7c1f0a5100b)) 44 | 45 | ## [1.4.0](https://github.com/browser-actions/setup-firefox/compare/setup-firefox-v1.3.0...setup-firefox-v1.4.0) (2023-11-11) 46 | 47 | 48 | ### Features 49 | 50 | * output installed binary path ([#493](https://github.com/browser-actions/setup-firefox/issues/493)) ([e16c07e](https://github.com/browser-actions/setup-firefox/commit/e16c07ebedc21f9a09aabcb5b081200bb61fb309)) 51 | 52 | ## [1.3.0](https://github.com/browser-actions/setup-firefox/compare/setup-firefox-v1.2.0...setup-firefox-v1.3.0) (2023-09-18) 53 | 54 | 55 | ### Features 56 | 57 | * firefox nightly ([#455](https://github.com/browser-actions/setup-firefox/issues/455)) ([b638071](https://github.com/browser-actions/setup-firefox/commit/b638071cda49366abcca9eb674072123f5c5b34e)) 58 | 59 | ## [1.2.0](https://github.com/browser-actions/setup-firefox/compare/setup-firefox-v1.1.1...setup-firefox-v1.2.0) (2023-09-17) 60 | 61 | 62 | ### Features 63 | 64 | * restore Windows support ([#422](https://github.com/browser-actions/setup-firefox/issues/422)) ([6013839](https://github.com/browser-actions/setup-firefox/commit/6013839547aee35d36b4e705c9c044d7c789bb5b)) 65 | 66 | 67 | ### Bug Fixes 68 | 69 | * **deps:** bump the actions group with 1 update ([#448](https://github.com/browser-actions/setup-firefox/issues/448)) ([b7727a2](https://github.com/browser-actions/setup-firefox/commit/b7727a275849f856e9b379f6cd3b1c3009f0fa72)) 70 | 71 | ## [1.1.1](https://github.com/browser-actions/setup-firefox/compare/setup-firefox-v1.1.0...setup-firefox-v1.1.1) (2023-03-16) 72 | 73 | 74 | ### Bug Fixes 75 | 76 | * **deps:** bump @actions/io from 1.1.2 to 1.1.3 ([#412](https://github.com/browser-actions/setup-firefox/issues/412)) ([9f159e2](https://github.com/browser-actions/setup-firefox/commit/9f159e22432daecfcb5a1e8a2ee7d71bd3149437)) 77 | 78 | ## [1.1.0](https://github.com/browser-actions/setup-firefox/compare/setup-firefox-v1.0.1...setup-firefox-v1.1.0) (2023-02-12) 79 | 80 | 81 | ### Features 82 | 83 | * Output version ([#394](https://github.com/browser-actions/setup-firefox/issues/394)) ([a2adddc](https://github.com/browser-actions/setup-firefox/commit/a2adddcf8cd190325f47bb3b0fd0144374745885)) 84 | 85 | 86 | ### Bug Fixes 87 | 88 | * bump typescript from 4.3.2 to 4.9.5 ([#388](https://github.com/browser-actions/setup-firefox/issues/388)) ([a8f85c9](https://github.com/browser-actions/setup-firefox/commit/a8f85c9dfefee93d8db5ee3fc625bfbbd667e9e6)) 89 | * **deps:** bump @actions/core from 1.5.0 to 1.10.0 ([#387](https://github.com/browser-actions/setup-firefox/issues/387)) ([76d61e2](https://github.com/browser-actions/setup-firefox/commit/76d61e2aa8beb9f67bb18a379162b92f05031085)) 90 | * **deps:** bump @actions/io from 1.1.1 to 1.1.2 ([#384](https://github.com/browser-actions/setup-firefox/issues/384)) ([3eea4c1](https://github.com/browser-actions/setup-firefox/commit/3eea4c17e161f3542f7d87a6975880a74e3e7b6d)) 91 | 92 | ## [1.0.1](https://github.com/browser-actions/setup-firefox/compare/setup-firefox-v1.0.0...setup-firefox-v1.0.1) (2023-01-29) 93 | 94 | 95 | ### Bug Fixes 96 | 97 | * use new set-output ([#378](https://github.com/browser-actions/setup-firefox/issues/378)) ([e85ec80](https://github.com/browser-actions/setup-firefox/commit/e85ec80fd078bd4ef19bcede476f6f69ecdf3152)) 98 | 99 | ## 1.0.0 (2023-01-22) 100 | 101 | 102 | ### Miscellaneous Chores 103 | 104 | * use v1 ([baabf1a](https://github.com/browser-actions/setup-firefox/commit/baabf1a3410cf8af9ec825a31e7721680a165dd1)) 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Shin'ya Ueoka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![build-test](https://github.com/browser-actions/setup-firefox/workflows/build-test/badge.svg) 2 | 3 | # setup-firefox 4 | 5 | This action sets by Firefox for use in actions by: 6 | 7 | - downloading and caching a version of Firefox by version and add to PATH 8 | 9 | ## Usage 10 | 11 | See [action.yml](action.yml) 12 | 13 | Basic usage: 14 | 15 | ```yaml 16 | steps: 17 | - uses: browser-actions/setup-firefox@v1 18 | - run: firefox --version 19 | ``` 20 | 21 | Use in the matrix: 22 | ```yaml 23 | jobs: 24 | build: 25 | runs-on: ubuntu-latest 26 | strategy: 27 | matrix: 28 | firefox: [ '84.0', 'devedition-84.0b1', 'latest-beta', 'latest-devedition', 'latest-nightly', 'latest-esr', 'latest' ] 29 | name: Firefox ${{ matrix.firefox }} sample 30 | steps: 31 | - name: Setup firefox 32 | id: setup-firefox 33 | uses: browser-actions/setup-firefox@v1 34 | with: 35 | firefox-version: ${{ matrix.firefox }} 36 | - run: | 37 | echo Installed firefox versions: ${{ steps.setup-firefox.outputs.firefox-version }} 38 | ${{ steps.setup-firefox.outputs.firefox-path }} --version 39 | ``` 40 | 41 | ## License 42 | 43 | [MIT](LICENSE) 44 | -------------------------------------------------------------------------------- /__test__/DownloadURL.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "vitest"; 2 | import { ArchiveDownloadURL, LatestDownloadURL } from "../src/DownloadURL"; 3 | import { UnsupportedPlatformError } from "../src/errors"; 4 | import { Arch, OS } from "../src/platform"; 5 | import { LatestVersion } from "../src/versions"; 6 | 7 | describe("ArchiveDownloadURL", () => { 8 | describe.each([ 9 | [ 10 | { os: OS.LINUX, arch: Arch.I686 }, 11 | "https://ftp.mozilla.org/pub/firefox/releases/134.0/linux-i686/en-US/firefox-134.0.tar.bz2", 12 | ], 13 | [ 14 | { os: OS.LINUX, arch: Arch.AMD64 }, 15 | "https://ftp.mozilla.org/pub/firefox/releases/134.0/linux-x86_64/en-US/firefox-134.0.tar.bz2", 16 | ], 17 | [ 18 | { os: OS.MACOS, arch: Arch.AMD64 }, 19 | "https://ftp.mozilla.org/pub/firefox/releases/134.0/mac/en-US/Firefox%20134.0.dmg", 20 | ], 21 | [ 22 | { os: OS.WINDOWS, arch: Arch.I686 }, 23 | "https://ftp.mozilla.org/pub/firefox/releases/134.0/win32/en-US/Firefox%20Setup%20134.0.exe", 24 | ], 25 | [ 26 | { os: OS.WINDOWS, arch: Arch.AMD64 }, 27 | "https://ftp.mozilla.org/pub/firefox/releases/134.0/win64/en-US/Firefox%20Setup%20134.0.exe", 28 | ], 29 | [ 30 | { os: OS.WINDOWS, arch: Arch.ARM64 }, 31 | "https://ftp.mozilla.org/pub/firefox/releases/134.0/win64-aarch64/en-US/Firefox%20Setup%20134.0.exe", 32 | ], 33 | ])("platform %s", ({ os, arch }, expected) => { 34 | test(`returns URL ${expected}`, () => { 35 | const sut = new ArchiveDownloadURL("134.0", { os, arch }, "en-US"); 36 | expect(sut.getURL()).toEqual(expected); 37 | }); 38 | }); 39 | 40 | describe.each([ 41 | [ 42 | { version: "firefox-80.0", os: OS.LINUX, arch: Arch.I686 }, 43 | "https://ftp.mozilla.org/pub/firefox/releases/80.0/linux-i686/en-US/firefox-80.0.tar.bz2", 44 | ], 45 | [ 46 | { version: "devedition-135.0b1", os: OS.LINUX, arch: Arch.AMD64 }, 47 | "https://ftp.mozilla.org/pub/devedition/releases/135.0b1/linux-x86_64/en-US/firefox-135.0b1.tar.xz", 48 | ], 49 | [ 50 | { version: "beta-135.0b1", os: OS.LINUX, arch: Arch.AMD64 }, 51 | "https://ftp.mozilla.org/pub/firefox/releases/135.0b1/linux-x86_64/en-US/firefox-135.0b1.tar.xz", 52 | ], 53 | ])("version %s", ({ version, os, arch }, expected) => { 54 | test(`returns URL ${expected}`, () => { 55 | const sut = new ArchiveDownloadURL(version, { os, arch }, "en-US"); 56 | expect(sut.getURL()).toEqual(expected); 57 | }); 58 | }); 59 | 60 | describe.each([[OS.MACOS, Arch.I686]])("platform %s %s", (os, arch) => { 61 | test("throws an error", () => { 62 | const sut = new ArchiveDownloadURL("80.0", { os, arch }, "en-US"); 63 | expect(() => sut.getURL()).toThrowError(UnsupportedPlatformError); 64 | }); 65 | }); 66 | }); 67 | 68 | describe("LatestDownloadURL", () => { 69 | describe.each([ 70 | [ 71 | LatestVersion.LATEST, 72 | { os: OS.LINUX, arch: Arch.I686 }, 73 | "https://download.mozilla.org/?product=firefox-latest&os=linux&lang=en-US", 74 | ], 75 | [ 76 | LatestVersion.LATEST, 77 | { os: OS.LINUX, arch: Arch.AMD64 }, 78 | "https://download.mozilla.org/?product=firefox-latest&os=linux64&lang=en-US", 79 | ], 80 | [ 81 | LatestVersion.LATEST_DEVEDITION, 82 | { os: OS.LINUX, arch: Arch.AMD64 }, 83 | "https://download.mozilla.org/?product=firefox-devedition-latest&os=linux64&lang=en-US", 84 | ], 85 | [ 86 | LatestVersion.LATEST_NIGHTLY, 87 | { os: OS.LINUX, arch: Arch.AMD64 }, 88 | "https://download.mozilla.org/?product=firefox-nightly-latest&os=linux64&lang=en-US", 89 | ], 90 | [ 91 | LatestVersion.LATEST_ESR, 92 | { os: OS.MACOS, arch: Arch.AMD64 }, 93 | "https://download.mozilla.org/?product=firefox-esr-latest&os=osx&lang=en-US", 94 | ], 95 | [ 96 | LatestVersion.LATEST_ESR, 97 | { os: OS.MACOS, arch: Arch.ARM64 }, 98 | "https://download.mozilla.org/?product=firefox-esr-latest&os=osx&lang=en-US", 99 | ], 100 | [ 101 | LatestVersion.LATEST_ESR, 102 | { os: OS.WINDOWS, arch: Arch.I686 }, 103 | "https://download.mozilla.org/?product=firefox-esr-latest&os=win&lang=en-US", 104 | ], 105 | [ 106 | LatestVersion.LATEST_ESR, 107 | { os: OS.WINDOWS, arch: Arch.AMD64 }, 108 | "https://download.mozilla.org/?product=firefox-esr-latest&os=win64&lang=en-US", 109 | ], 110 | ])("platform %s %s", (version, { os, arch }, expected) => { 111 | test(`returns URL ${expected}`, () => { 112 | const sut = new LatestDownloadURL(version, { os, arch }, "en-US"); 113 | expect(sut.getURL()).toEqual(expected); 114 | }); 115 | }); 116 | 117 | describe.each([[OS.MACOS, Arch.I686]])("platform %s %s", (os, arch) => { 118 | test("throws an error", () => { 119 | const sut = new LatestDownloadURL( 120 | LatestVersion.LATEST, 121 | { os, arch }, 122 | "en-US", 123 | ); 124 | expect(() => sut.getURL()).toThrowError(UnsupportedPlatformError); 125 | }); 126 | }); 127 | }); 128 | -------------------------------------------------------------------------------- /__test__/DownloadURLFactory.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "vitest"; 2 | import { ArchiveDownloadURL, LatestDownloadURL } from "../src/DownloadURL"; 3 | import { DownloadURLFactory } from "../src/DownloadURLFactory"; 4 | import { Arch, OS } from "../src/platform"; 5 | 6 | describe("DownloadURLFactory", () => { 7 | describe.each([ 8 | ["80.0", ArchiveDownloadURL], 9 | ["firefox-80.0", ArchiveDownloadURL], 10 | ["beta-80.0b1", ArchiveDownloadURL], 11 | ["devedition-80.0b1", ArchiveDownloadURL], 12 | ["latest", LatestDownloadURL], 13 | ["latest-beta", LatestDownloadURL], 14 | ["latest-devedition", LatestDownloadURL], 15 | ["latest-nightly", LatestDownloadURL], 16 | ["latest-esr", LatestDownloadURL], 17 | ])("for version %s", (version, expected) => { 18 | test(`returns ${String(expected.name)}`, () => { 19 | const sut = new DownloadURLFactory( 20 | version, 21 | { os: OS.LINUX, arch: Arch.AMD64 }, 22 | "en-US", 23 | ); 24 | expect(sut.create()).toBeInstanceOf(expected); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /__test__/InstallerFactory.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "vitest"; 2 | import { 3 | LinuxInstaller, 4 | MacOSInstaller, 5 | WindowsInstaller, 6 | } from "../src/Installer"; 7 | import InstallerFactory from "../src/InstallerFactory"; 8 | import { Arch, OS } from "../src/platform"; 9 | 10 | describe("InstallerFactory", () => { 11 | describe.each([ 12 | [OS.LINUX, LinuxInstaller], 13 | [OS.MACOS, MacOSInstaller], 14 | [OS.WINDOWS, WindowsInstaller], 15 | ])("for platform %s", (os, expected) => { 16 | test(`returns ${String(expected.name)}`, () => { 17 | const sut = new InstallerFactory(); 18 | expect(sut.create({ os, arch: Arch.AMD64 })).toBeInstanceOf(expected); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Setup Firefox' 2 | description: 'Install and setup Firefox' 3 | author: "Shin'ya Ueoka" 4 | inputs: 5 | firefox-version: 6 | description: 'The Firefox version to install and use. Examples: 84.0, 84.0.1, latest-esr' 7 | outputs: 8 | firefox-version: 9 | description: 'The installed Firefox version. Useful when given a latest version.' 10 | firefox-path: 11 | description: 'The installed Firefox path.' 12 | runs: 13 | using: 'node20' 14 | main: 'index.js' 15 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "ignore": ["package.json"] 4 | }, 5 | "linter": { 6 | "enabled": true, 7 | "rules": { 8 | "recommended": true, 9 | "complexity": { 10 | "useLiteralKeys": "off" 11 | }, 12 | "style": { 13 | "noUselessElse": "off" 14 | } 15 | } 16 | }, 17 | "formatter": { 18 | "enabled": true, 19 | "indentStyle": "space" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "setup-firefox", 3 | "version": "1.5.4", 4 | "description": "Set up your GitHub Actions workflow with a specific version of firefox", 5 | "main": "dist/index.js", 6 | "packageManager": "pnpm@8.7.5", 7 | "engines": { 8 | "node": "20.6.1" 9 | }, 10 | "dependencies": { 11 | "@actions/core": "^1.10.1", 12 | "@actions/exec": "^1.1.1", 13 | "@actions/io": "^1.1.3", 14 | "@actions/tool-cache": "^2.0.1" 15 | }, 16 | "devDependencies": { 17 | "@biomejs/biome": "^1.8.3", 18 | "@types/node": "^20.6.2", 19 | "@vercel/ncc": "^0.38.0", 20 | "typescript": "^5.2.2", 21 | "vitest": "^2.0.2" 22 | }, 23 | "scripts": { 24 | "build": "ncc build src/index.ts", 25 | "test": "vitest", 26 | "package": "cp action.yml README.md dist/", 27 | "lint": "biome ci .", 28 | "lint:fix": "biome check --apply ." 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/browser-actions/setup-firefox.git" 33 | }, 34 | "keywords": [ 35 | "actions", 36 | "node", 37 | "setup", 38 | "firefox" 39 | ], 40 | "author": "Shin'ya Ueoka", 41 | "license": "MIT", 42 | "bugs": { 43 | "url": "https://github.com/browser-actions/setup-firefox/issues" 44 | }, 45 | "homepage": "https://github.com/browser-actions/setup-firefox#readme" 46 | } 47 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | '@actions/core': 9 | specifier: ^1.10.1 10 | version: 1.10.1 11 | '@actions/exec': 12 | specifier: ^1.1.1 13 | version: 1.1.1 14 | '@actions/io': 15 | specifier: ^1.1.3 16 | version: 1.1.3 17 | '@actions/tool-cache': 18 | specifier: ^2.0.1 19 | version: 2.0.1 20 | 21 | devDependencies: 22 | '@biomejs/biome': 23 | specifier: ^1.8.3 24 | version: 1.8.3 25 | '@types/node': 26 | specifier: ^20.6.2 27 | version: 20.6.2 28 | '@vercel/ncc': 29 | specifier: ^0.38.0 30 | version: 0.38.0 31 | typescript: 32 | specifier: ^5.2.2 33 | version: 5.2.2 34 | vitest: 35 | specifier: ^2.0.2 36 | version: 2.0.2(@types/node@20.6.2) 37 | 38 | packages: 39 | 40 | /@actions/core@1.10.1: 41 | resolution: {integrity: sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==} 42 | dependencies: 43 | '@actions/http-client': 2.1.1 44 | uuid: 8.3.2 45 | dev: false 46 | 47 | /@actions/exec@1.1.1: 48 | resolution: {integrity: sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==} 49 | dependencies: 50 | '@actions/io': 1.1.3 51 | dev: false 52 | 53 | /@actions/http-client@2.0.1: 54 | resolution: {integrity: sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==} 55 | dependencies: 56 | tunnel: 0.0.6 57 | dev: false 58 | 59 | /@actions/http-client@2.1.1: 60 | resolution: {integrity: sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==} 61 | dependencies: 62 | tunnel: 0.0.6 63 | dev: false 64 | 65 | /@actions/io@1.1.3: 66 | resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==} 67 | dev: false 68 | 69 | /@actions/tool-cache@2.0.1: 70 | resolution: {integrity: sha512-iPU+mNwrbA8jodY8eyo/0S/QqCKDajiR8OxWTnSk/SnYg0sj8Hp4QcUEVC1YFpHWXtrfbQrE13Jz4k4HXJQKcA==} 71 | dependencies: 72 | '@actions/core': 1.10.1 73 | '@actions/exec': 1.1.1 74 | '@actions/http-client': 2.0.1 75 | '@actions/io': 1.1.3 76 | semver: 6.3.0 77 | uuid: 3.4.0 78 | dev: false 79 | 80 | /@ampproject/remapping@2.3.0: 81 | resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} 82 | engines: {node: '>=6.0.0'} 83 | dependencies: 84 | '@jridgewell/gen-mapping': 0.3.5 85 | '@jridgewell/trace-mapping': 0.3.25 86 | dev: true 87 | 88 | /@biomejs/biome@1.8.3: 89 | resolution: {integrity: sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w==} 90 | engines: {node: '>=14.21.3'} 91 | hasBin: true 92 | requiresBuild: true 93 | optionalDependencies: 94 | '@biomejs/cli-darwin-arm64': 1.8.3 95 | '@biomejs/cli-darwin-x64': 1.8.3 96 | '@biomejs/cli-linux-arm64': 1.8.3 97 | '@biomejs/cli-linux-arm64-musl': 1.8.3 98 | '@biomejs/cli-linux-x64': 1.8.3 99 | '@biomejs/cli-linux-x64-musl': 1.8.3 100 | '@biomejs/cli-win32-arm64': 1.8.3 101 | '@biomejs/cli-win32-x64': 1.8.3 102 | dev: true 103 | 104 | /@biomejs/cli-darwin-arm64@1.8.3: 105 | resolution: {integrity: sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A==} 106 | engines: {node: '>=14.21.3'} 107 | cpu: [arm64] 108 | os: [darwin] 109 | requiresBuild: true 110 | dev: true 111 | optional: true 112 | 113 | /@biomejs/cli-darwin-x64@1.8.3: 114 | resolution: {integrity: sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw==} 115 | engines: {node: '>=14.21.3'} 116 | cpu: [x64] 117 | os: [darwin] 118 | requiresBuild: true 119 | dev: true 120 | optional: true 121 | 122 | /@biomejs/cli-linux-arm64-musl@1.8.3: 123 | resolution: {integrity: sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ==} 124 | engines: {node: '>=14.21.3'} 125 | cpu: [arm64] 126 | os: [linux] 127 | requiresBuild: true 128 | dev: true 129 | optional: true 130 | 131 | /@biomejs/cli-linux-arm64@1.8.3: 132 | resolution: {integrity: sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw==} 133 | engines: {node: '>=14.21.3'} 134 | cpu: [arm64] 135 | os: [linux] 136 | requiresBuild: true 137 | dev: true 138 | optional: true 139 | 140 | /@biomejs/cli-linux-x64-musl@1.8.3: 141 | resolution: {integrity: sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA==} 142 | engines: {node: '>=14.21.3'} 143 | cpu: [x64] 144 | os: [linux] 145 | requiresBuild: true 146 | dev: true 147 | optional: true 148 | 149 | /@biomejs/cli-linux-x64@1.8.3: 150 | resolution: {integrity: sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw==} 151 | engines: {node: '>=14.21.3'} 152 | cpu: [x64] 153 | os: [linux] 154 | requiresBuild: true 155 | dev: true 156 | optional: true 157 | 158 | /@biomejs/cli-win32-arm64@1.8.3: 159 | resolution: {integrity: sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ==} 160 | engines: {node: '>=14.21.3'} 161 | cpu: [arm64] 162 | os: [win32] 163 | requiresBuild: true 164 | dev: true 165 | optional: true 166 | 167 | /@biomejs/cli-win32-x64@1.8.3: 168 | resolution: {integrity: sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg==} 169 | engines: {node: '>=14.21.3'} 170 | cpu: [x64] 171 | os: [win32] 172 | requiresBuild: true 173 | dev: true 174 | optional: true 175 | 176 | /@esbuild/aix-ppc64@0.20.2: 177 | resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} 178 | engines: {node: '>=12'} 179 | cpu: [ppc64] 180 | os: [aix] 181 | requiresBuild: true 182 | dev: true 183 | optional: true 184 | 185 | /@esbuild/android-arm64@0.20.2: 186 | resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} 187 | engines: {node: '>=12'} 188 | cpu: [arm64] 189 | os: [android] 190 | requiresBuild: true 191 | dev: true 192 | optional: true 193 | 194 | /@esbuild/android-arm@0.20.2: 195 | resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} 196 | engines: {node: '>=12'} 197 | cpu: [arm] 198 | os: [android] 199 | requiresBuild: true 200 | dev: true 201 | optional: true 202 | 203 | /@esbuild/android-x64@0.20.2: 204 | resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} 205 | engines: {node: '>=12'} 206 | cpu: [x64] 207 | os: [android] 208 | requiresBuild: true 209 | dev: true 210 | optional: true 211 | 212 | /@esbuild/darwin-arm64@0.20.2: 213 | resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} 214 | engines: {node: '>=12'} 215 | cpu: [arm64] 216 | os: [darwin] 217 | requiresBuild: true 218 | dev: true 219 | optional: true 220 | 221 | /@esbuild/darwin-x64@0.20.2: 222 | resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} 223 | engines: {node: '>=12'} 224 | cpu: [x64] 225 | os: [darwin] 226 | requiresBuild: true 227 | dev: true 228 | optional: true 229 | 230 | /@esbuild/freebsd-arm64@0.20.2: 231 | resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} 232 | engines: {node: '>=12'} 233 | cpu: [arm64] 234 | os: [freebsd] 235 | requiresBuild: true 236 | dev: true 237 | optional: true 238 | 239 | /@esbuild/freebsd-x64@0.20.2: 240 | resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} 241 | engines: {node: '>=12'} 242 | cpu: [x64] 243 | os: [freebsd] 244 | requiresBuild: true 245 | dev: true 246 | optional: true 247 | 248 | /@esbuild/linux-arm64@0.20.2: 249 | resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} 250 | engines: {node: '>=12'} 251 | cpu: [arm64] 252 | os: [linux] 253 | requiresBuild: true 254 | dev: true 255 | optional: true 256 | 257 | /@esbuild/linux-arm@0.20.2: 258 | resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} 259 | engines: {node: '>=12'} 260 | cpu: [arm] 261 | os: [linux] 262 | requiresBuild: true 263 | dev: true 264 | optional: true 265 | 266 | /@esbuild/linux-ia32@0.20.2: 267 | resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} 268 | engines: {node: '>=12'} 269 | cpu: [ia32] 270 | os: [linux] 271 | requiresBuild: true 272 | dev: true 273 | optional: true 274 | 275 | /@esbuild/linux-loong64@0.20.2: 276 | resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} 277 | engines: {node: '>=12'} 278 | cpu: [loong64] 279 | os: [linux] 280 | requiresBuild: true 281 | dev: true 282 | optional: true 283 | 284 | /@esbuild/linux-mips64el@0.20.2: 285 | resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} 286 | engines: {node: '>=12'} 287 | cpu: [mips64el] 288 | os: [linux] 289 | requiresBuild: true 290 | dev: true 291 | optional: true 292 | 293 | /@esbuild/linux-ppc64@0.20.2: 294 | resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} 295 | engines: {node: '>=12'} 296 | cpu: [ppc64] 297 | os: [linux] 298 | requiresBuild: true 299 | dev: true 300 | optional: true 301 | 302 | /@esbuild/linux-riscv64@0.20.2: 303 | resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} 304 | engines: {node: '>=12'} 305 | cpu: [riscv64] 306 | os: [linux] 307 | requiresBuild: true 308 | dev: true 309 | optional: true 310 | 311 | /@esbuild/linux-s390x@0.20.2: 312 | resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} 313 | engines: {node: '>=12'} 314 | cpu: [s390x] 315 | os: [linux] 316 | requiresBuild: true 317 | dev: true 318 | optional: true 319 | 320 | /@esbuild/linux-x64@0.20.2: 321 | resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} 322 | engines: {node: '>=12'} 323 | cpu: [x64] 324 | os: [linux] 325 | requiresBuild: true 326 | dev: true 327 | optional: true 328 | 329 | /@esbuild/netbsd-x64@0.20.2: 330 | resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} 331 | engines: {node: '>=12'} 332 | cpu: [x64] 333 | os: [netbsd] 334 | requiresBuild: true 335 | dev: true 336 | optional: true 337 | 338 | /@esbuild/openbsd-x64@0.20.2: 339 | resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} 340 | engines: {node: '>=12'} 341 | cpu: [x64] 342 | os: [openbsd] 343 | requiresBuild: true 344 | dev: true 345 | optional: true 346 | 347 | /@esbuild/sunos-x64@0.20.2: 348 | resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} 349 | engines: {node: '>=12'} 350 | cpu: [x64] 351 | os: [sunos] 352 | requiresBuild: true 353 | dev: true 354 | optional: true 355 | 356 | /@esbuild/win32-arm64@0.20.2: 357 | resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} 358 | engines: {node: '>=12'} 359 | cpu: [arm64] 360 | os: [win32] 361 | requiresBuild: true 362 | dev: true 363 | optional: true 364 | 365 | /@esbuild/win32-ia32@0.20.2: 366 | resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} 367 | engines: {node: '>=12'} 368 | cpu: [ia32] 369 | os: [win32] 370 | requiresBuild: true 371 | dev: true 372 | optional: true 373 | 374 | /@esbuild/win32-x64@0.20.2: 375 | resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} 376 | engines: {node: '>=12'} 377 | cpu: [x64] 378 | os: [win32] 379 | requiresBuild: true 380 | dev: true 381 | optional: true 382 | 383 | /@jridgewell/gen-mapping@0.3.5: 384 | resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} 385 | engines: {node: '>=6.0.0'} 386 | dependencies: 387 | '@jridgewell/set-array': 1.2.1 388 | '@jridgewell/sourcemap-codec': 1.4.15 389 | '@jridgewell/trace-mapping': 0.3.25 390 | dev: true 391 | 392 | /@jridgewell/resolve-uri@3.1.2: 393 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 394 | engines: {node: '>=6.0.0'} 395 | dev: true 396 | 397 | /@jridgewell/set-array@1.2.1: 398 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 399 | engines: {node: '>=6.0.0'} 400 | dev: true 401 | 402 | /@jridgewell/sourcemap-codec@1.4.15: 403 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} 404 | dev: true 405 | 406 | /@jridgewell/trace-mapping@0.3.25: 407 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 408 | dependencies: 409 | '@jridgewell/resolve-uri': 3.1.2 410 | '@jridgewell/sourcemap-codec': 1.4.15 411 | dev: true 412 | 413 | /@rollup/rollup-android-arm-eabi@4.17.2: 414 | resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==} 415 | cpu: [arm] 416 | os: [android] 417 | requiresBuild: true 418 | dev: true 419 | optional: true 420 | 421 | /@rollup/rollup-android-arm64@4.17.2: 422 | resolution: {integrity: sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==} 423 | cpu: [arm64] 424 | os: [android] 425 | requiresBuild: true 426 | dev: true 427 | optional: true 428 | 429 | /@rollup/rollup-darwin-arm64@4.17.2: 430 | resolution: {integrity: sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==} 431 | cpu: [arm64] 432 | os: [darwin] 433 | requiresBuild: true 434 | dev: true 435 | optional: true 436 | 437 | /@rollup/rollup-darwin-x64@4.17.2: 438 | resolution: {integrity: sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==} 439 | cpu: [x64] 440 | os: [darwin] 441 | requiresBuild: true 442 | dev: true 443 | optional: true 444 | 445 | /@rollup/rollup-linux-arm-gnueabihf@4.17.2: 446 | resolution: {integrity: sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==} 447 | cpu: [arm] 448 | os: [linux] 449 | requiresBuild: true 450 | dev: true 451 | optional: true 452 | 453 | /@rollup/rollup-linux-arm-musleabihf@4.17.2: 454 | resolution: {integrity: sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==} 455 | cpu: [arm] 456 | os: [linux] 457 | requiresBuild: true 458 | dev: true 459 | optional: true 460 | 461 | /@rollup/rollup-linux-arm64-gnu@4.17.2: 462 | resolution: {integrity: sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==} 463 | cpu: [arm64] 464 | os: [linux] 465 | requiresBuild: true 466 | dev: true 467 | optional: true 468 | 469 | /@rollup/rollup-linux-arm64-musl@4.17.2: 470 | resolution: {integrity: sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==} 471 | cpu: [arm64] 472 | os: [linux] 473 | requiresBuild: true 474 | dev: true 475 | optional: true 476 | 477 | /@rollup/rollup-linux-powerpc64le-gnu@4.17.2: 478 | resolution: {integrity: sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==} 479 | cpu: [ppc64] 480 | os: [linux] 481 | requiresBuild: true 482 | dev: true 483 | optional: true 484 | 485 | /@rollup/rollup-linux-riscv64-gnu@4.17.2: 486 | resolution: {integrity: sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==} 487 | cpu: [riscv64] 488 | os: [linux] 489 | requiresBuild: true 490 | dev: true 491 | optional: true 492 | 493 | /@rollup/rollup-linux-s390x-gnu@4.17.2: 494 | resolution: {integrity: sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==} 495 | cpu: [s390x] 496 | os: [linux] 497 | requiresBuild: true 498 | dev: true 499 | optional: true 500 | 501 | /@rollup/rollup-linux-x64-gnu@4.17.2: 502 | resolution: {integrity: sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==} 503 | cpu: [x64] 504 | os: [linux] 505 | requiresBuild: true 506 | dev: true 507 | optional: true 508 | 509 | /@rollup/rollup-linux-x64-musl@4.17.2: 510 | resolution: {integrity: sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==} 511 | cpu: [x64] 512 | os: [linux] 513 | requiresBuild: true 514 | dev: true 515 | optional: true 516 | 517 | /@rollup/rollup-win32-arm64-msvc@4.17.2: 518 | resolution: {integrity: sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==} 519 | cpu: [arm64] 520 | os: [win32] 521 | requiresBuild: true 522 | dev: true 523 | optional: true 524 | 525 | /@rollup/rollup-win32-ia32-msvc@4.17.2: 526 | resolution: {integrity: sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==} 527 | cpu: [ia32] 528 | os: [win32] 529 | requiresBuild: true 530 | dev: true 531 | optional: true 532 | 533 | /@rollup/rollup-win32-x64-msvc@4.17.2: 534 | resolution: {integrity: sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==} 535 | cpu: [x64] 536 | os: [win32] 537 | requiresBuild: true 538 | dev: true 539 | optional: true 540 | 541 | /@types/estree@1.0.5: 542 | resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} 543 | dev: true 544 | 545 | /@types/node@20.6.2: 546 | resolution: {integrity: sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==} 547 | dev: true 548 | 549 | /@vercel/ncc@0.38.0: 550 | resolution: {integrity: sha512-B4YKZMm/EqMptKSFyAq4q2SlgJe+VCmEH6Y8gf/E1pTlWbsUJpuH1ymik2Ex3aYO5mCWwV1kaSYHSQOT8+4vHA==} 551 | hasBin: true 552 | dev: true 553 | 554 | /@vitest/expect@2.0.2: 555 | resolution: {integrity: sha512-nKAvxBYqcDugYZ4nJvnm5OR8eDJdgWjk4XM9owQKUjzW70q0icGV2HVnQOyYsp906xJaBDUXw0+9EHw2T8e0mQ==} 556 | dependencies: 557 | '@vitest/spy': 2.0.2 558 | '@vitest/utils': 2.0.2 559 | chai: 5.1.1 560 | tinyrainbow: 1.2.0 561 | dev: true 562 | 563 | /@vitest/pretty-format@2.0.2: 564 | resolution: {integrity: sha512-SBCyOXfGVvddRd9r2PwoVR0fonQjh9BMIcBMlSzbcNwFfGr6ZhOhvBzurjvi2F4ryut2HcqiFhNeDVGwru8tLg==} 565 | dependencies: 566 | tinyrainbow: 1.2.0 567 | dev: true 568 | 569 | /@vitest/runner@2.0.2: 570 | resolution: {integrity: sha512-OCh437Vi8Wdbif1e0OvQcbfM3sW4s2lpmOjAE7qfLrpzJX2M7J1IQlNvEcb/fu6kaIB9n9n35wS0G2Q3en5kHg==} 571 | dependencies: 572 | '@vitest/utils': 2.0.2 573 | pathe: 1.1.2 574 | dev: true 575 | 576 | /@vitest/snapshot@2.0.2: 577 | resolution: {integrity: sha512-Yc2ewhhZhx+0f9cSUdfzPRcsM6PhIb+S43wxE7OG0kTxqgqzo8tHkXFuFlndXeDMp09G3sY/X5OAo/RfYydf1g==} 578 | dependencies: 579 | '@vitest/pretty-format': 2.0.2 580 | magic-string: 0.30.10 581 | pathe: 1.1.2 582 | dev: true 583 | 584 | /@vitest/spy@2.0.2: 585 | resolution: {integrity: sha512-MgwJ4AZtCgqyp2d7WcQVE8aNG5vQ9zu9qMPYQHjsld/QVsrvg78beNrXdO4HYkP0lDahCO3P4F27aagIag+SGQ==} 586 | dependencies: 587 | tinyspy: 3.0.0 588 | dev: true 589 | 590 | /@vitest/utils@2.0.2: 591 | resolution: {integrity: sha512-pxCY1v7kmOCWYWjzc0zfjGTA3Wmn8PKnlPvSrsA643P1NHl1fOyXj2Q9SaNlrlFE+ivCsxM80Ov3AR82RmHCWQ==} 592 | dependencies: 593 | '@vitest/pretty-format': 2.0.2 594 | estree-walker: 3.0.3 595 | loupe: 3.1.1 596 | tinyrainbow: 1.2.0 597 | dev: true 598 | 599 | /assertion-error@2.0.1: 600 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 601 | engines: {node: '>=12'} 602 | dev: true 603 | 604 | /cac@6.7.14: 605 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 606 | engines: {node: '>=8'} 607 | dev: true 608 | 609 | /chai@5.1.1: 610 | resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} 611 | engines: {node: '>=12'} 612 | dependencies: 613 | assertion-error: 2.0.1 614 | check-error: 2.1.1 615 | deep-eql: 5.0.2 616 | loupe: 3.1.1 617 | pathval: 2.0.0 618 | dev: true 619 | 620 | /check-error@2.1.1: 621 | resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} 622 | engines: {node: '>= 16'} 623 | dev: true 624 | 625 | /cross-spawn@7.0.3: 626 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 627 | engines: {node: '>= 8'} 628 | dependencies: 629 | path-key: 3.1.1 630 | shebang-command: 2.0.0 631 | which: 2.0.2 632 | dev: true 633 | 634 | /debug@4.3.5: 635 | resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} 636 | engines: {node: '>=6.0'} 637 | peerDependencies: 638 | supports-color: '*' 639 | peerDependenciesMeta: 640 | supports-color: 641 | optional: true 642 | dependencies: 643 | ms: 2.1.2 644 | dev: true 645 | 646 | /deep-eql@5.0.2: 647 | resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} 648 | engines: {node: '>=6'} 649 | dev: true 650 | 651 | /esbuild@0.20.2: 652 | resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} 653 | engines: {node: '>=12'} 654 | hasBin: true 655 | requiresBuild: true 656 | optionalDependencies: 657 | '@esbuild/aix-ppc64': 0.20.2 658 | '@esbuild/android-arm': 0.20.2 659 | '@esbuild/android-arm64': 0.20.2 660 | '@esbuild/android-x64': 0.20.2 661 | '@esbuild/darwin-arm64': 0.20.2 662 | '@esbuild/darwin-x64': 0.20.2 663 | '@esbuild/freebsd-arm64': 0.20.2 664 | '@esbuild/freebsd-x64': 0.20.2 665 | '@esbuild/linux-arm': 0.20.2 666 | '@esbuild/linux-arm64': 0.20.2 667 | '@esbuild/linux-ia32': 0.20.2 668 | '@esbuild/linux-loong64': 0.20.2 669 | '@esbuild/linux-mips64el': 0.20.2 670 | '@esbuild/linux-ppc64': 0.20.2 671 | '@esbuild/linux-riscv64': 0.20.2 672 | '@esbuild/linux-s390x': 0.20.2 673 | '@esbuild/linux-x64': 0.20.2 674 | '@esbuild/netbsd-x64': 0.20.2 675 | '@esbuild/openbsd-x64': 0.20.2 676 | '@esbuild/sunos-x64': 0.20.2 677 | '@esbuild/win32-arm64': 0.20.2 678 | '@esbuild/win32-ia32': 0.20.2 679 | '@esbuild/win32-x64': 0.20.2 680 | dev: true 681 | 682 | /estree-walker@3.0.3: 683 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 684 | dependencies: 685 | '@types/estree': 1.0.5 686 | dev: true 687 | 688 | /execa@8.0.1: 689 | resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} 690 | engines: {node: '>=16.17'} 691 | dependencies: 692 | cross-spawn: 7.0.3 693 | get-stream: 8.0.1 694 | human-signals: 5.0.0 695 | is-stream: 3.0.0 696 | merge-stream: 2.0.0 697 | npm-run-path: 5.3.0 698 | onetime: 6.0.0 699 | signal-exit: 4.1.0 700 | strip-final-newline: 3.0.0 701 | dev: true 702 | 703 | /fsevents@2.3.3: 704 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 705 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 706 | os: [darwin] 707 | requiresBuild: true 708 | dev: true 709 | optional: true 710 | 711 | /get-func-name@2.0.2: 712 | resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} 713 | dev: true 714 | 715 | /get-stream@8.0.1: 716 | resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} 717 | engines: {node: '>=16'} 718 | dev: true 719 | 720 | /human-signals@5.0.0: 721 | resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} 722 | engines: {node: '>=16.17.0'} 723 | dev: true 724 | 725 | /is-stream@3.0.0: 726 | resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} 727 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 728 | dev: true 729 | 730 | /isexe@2.0.0: 731 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 732 | dev: true 733 | 734 | /loupe@3.1.1: 735 | resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} 736 | dependencies: 737 | get-func-name: 2.0.2 738 | dev: true 739 | 740 | /magic-string@0.30.10: 741 | resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} 742 | dependencies: 743 | '@jridgewell/sourcemap-codec': 1.4.15 744 | dev: true 745 | 746 | /merge-stream@2.0.0: 747 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} 748 | dev: true 749 | 750 | /mimic-fn@4.0.0: 751 | resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} 752 | engines: {node: '>=12'} 753 | dev: true 754 | 755 | /ms@2.1.2: 756 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 757 | dev: true 758 | 759 | /nanoid@3.3.7: 760 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 761 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 762 | hasBin: true 763 | dev: true 764 | 765 | /npm-run-path@5.3.0: 766 | resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} 767 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 768 | dependencies: 769 | path-key: 4.0.0 770 | dev: true 771 | 772 | /onetime@6.0.0: 773 | resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} 774 | engines: {node: '>=12'} 775 | dependencies: 776 | mimic-fn: 4.0.0 777 | dev: true 778 | 779 | /path-key@3.1.1: 780 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 781 | engines: {node: '>=8'} 782 | dev: true 783 | 784 | /path-key@4.0.0: 785 | resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} 786 | engines: {node: '>=12'} 787 | dev: true 788 | 789 | /pathe@1.1.2: 790 | resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} 791 | dev: true 792 | 793 | /pathval@2.0.0: 794 | resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} 795 | engines: {node: '>= 14.16'} 796 | dev: true 797 | 798 | /picocolors@1.0.0: 799 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 800 | dev: true 801 | 802 | /postcss@8.4.38: 803 | resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} 804 | engines: {node: ^10 || ^12 || >=14} 805 | dependencies: 806 | nanoid: 3.3.7 807 | picocolors: 1.0.0 808 | source-map-js: 1.2.0 809 | dev: true 810 | 811 | /rollup@4.17.2: 812 | resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==} 813 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 814 | hasBin: true 815 | dependencies: 816 | '@types/estree': 1.0.5 817 | optionalDependencies: 818 | '@rollup/rollup-android-arm-eabi': 4.17.2 819 | '@rollup/rollup-android-arm64': 4.17.2 820 | '@rollup/rollup-darwin-arm64': 4.17.2 821 | '@rollup/rollup-darwin-x64': 4.17.2 822 | '@rollup/rollup-linux-arm-gnueabihf': 4.17.2 823 | '@rollup/rollup-linux-arm-musleabihf': 4.17.2 824 | '@rollup/rollup-linux-arm64-gnu': 4.17.2 825 | '@rollup/rollup-linux-arm64-musl': 4.17.2 826 | '@rollup/rollup-linux-powerpc64le-gnu': 4.17.2 827 | '@rollup/rollup-linux-riscv64-gnu': 4.17.2 828 | '@rollup/rollup-linux-s390x-gnu': 4.17.2 829 | '@rollup/rollup-linux-x64-gnu': 4.17.2 830 | '@rollup/rollup-linux-x64-musl': 4.17.2 831 | '@rollup/rollup-win32-arm64-msvc': 4.17.2 832 | '@rollup/rollup-win32-ia32-msvc': 4.17.2 833 | '@rollup/rollup-win32-x64-msvc': 4.17.2 834 | fsevents: 2.3.3 835 | dev: true 836 | 837 | /semver@6.3.0: 838 | resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} 839 | hasBin: true 840 | dev: false 841 | 842 | /shebang-command@2.0.0: 843 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 844 | engines: {node: '>=8'} 845 | dependencies: 846 | shebang-regex: 3.0.0 847 | dev: true 848 | 849 | /shebang-regex@3.0.0: 850 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 851 | engines: {node: '>=8'} 852 | dev: true 853 | 854 | /siginfo@2.0.0: 855 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 856 | dev: true 857 | 858 | /signal-exit@4.1.0: 859 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 860 | engines: {node: '>=14'} 861 | dev: true 862 | 863 | /source-map-js@1.2.0: 864 | resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} 865 | engines: {node: '>=0.10.0'} 866 | dev: true 867 | 868 | /stackback@0.0.2: 869 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 870 | dev: true 871 | 872 | /std-env@3.7.0: 873 | resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} 874 | dev: true 875 | 876 | /strip-final-newline@3.0.0: 877 | resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} 878 | engines: {node: '>=12'} 879 | dev: true 880 | 881 | /tinybench@2.8.0: 882 | resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} 883 | dev: true 884 | 885 | /tinypool@1.0.0: 886 | resolution: {integrity: sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==} 887 | engines: {node: ^18.0.0 || >=20.0.0} 888 | dev: true 889 | 890 | /tinyrainbow@1.2.0: 891 | resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} 892 | engines: {node: '>=14.0.0'} 893 | dev: true 894 | 895 | /tinyspy@3.0.0: 896 | resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==} 897 | engines: {node: '>=14.0.0'} 898 | dev: true 899 | 900 | /tunnel@0.0.6: 901 | resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} 902 | engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} 903 | dev: false 904 | 905 | /typescript@5.2.2: 906 | resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} 907 | engines: {node: '>=14.17'} 908 | hasBin: true 909 | dev: true 910 | 911 | /uuid@3.4.0: 912 | resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} 913 | deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. 914 | hasBin: true 915 | dev: false 916 | 917 | /uuid@8.3.2: 918 | resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} 919 | hasBin: true 920 | dev: false 921 | 922 | /vite-node@2.0.2(@types/node@20.6.2): 923 | resolution: {integrity: sha512-w4vkSz1Wo+NIQg8pjlEn0jQbcM/0D+xVaYjhw3cvarTanLLBh54oNiRbsT8PNK5GfuST0IlVXjsNRoNlqvY/fw==} 924 | engines: {node: ^18.0.0 || >=20.0.0} 925 | hasBin: true 926 | dependencies: 927 | cac: 6.7.14 928 | debug: 4.3.5 929 | pathe: 1.1.2 930 | tinyrainbow: 1.2.0 931 | vite: 5.2.11(@types/node@20.6.2) 932 | transitivePeerDependencies: 933 | - '@types/node' 934 | - less 935 | - lightningcss 936 | - sass 937 | - stylus 938 | - sugarss 939 | - supports-color 940 | - terser 941 | dev: true 942 | 943 | /vite@5.2.11(@types/node@20.6.2): 944 | resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==} 945 | engines: {node: ^18.0.0 || >=20.0.0} 946 | hasBin: true 947 | peerDependencies: 948 | '@types/node': ^18.0.0 || >=20.0.0 949 | less: '*' 950 | lightningcss: ^1.21.0 951 | sass: '*' 952 | stylus: '*' 953 | sugarss: '*' 954 | terser: ^5.4.0 955 | peerDependenciesMeta: 956 | '@types/node': 957 | optional: true 958 | less: 959 | optional: true 960 | lightningcss: 961 | optional: true 962 | sass: 963 | optional: true 964 | stylus: 965 | optional: true 966 | sugarss: 967 | optional: true 968 | terser: 969 | optional: true 970 | dependencies: 971 | '@types/node': 20.6.2 972 | esbuild: 0.20.2 973 | postcss: 8.4.38 974 | rollup: 4.17.2 975 | optionalDependencies: 976 | fsevents: 2.3.3 977 | dev: true 978 | 979 | /vitest@2.0.2(@types/node@20.6.2): 980 | resolution: {integrity: sha512-WlpZ9neRIjNBIOQwBYfBSr0+of5ZCbxT2TVGKW4Lv0c8+srCFIiRdsP7U009t8mMn821HQ4XKgkx5dVWpyoyLw==} 981 | engines: {node: ^18.0.0 || >=20.0.0} 982 | hasBin: true 983 | peerDependencies: 984 | '@edge-runtime/vm': '*' 985 | '@types/node': ^18.0.0 || >=20.0.0 986 | '@vitest/browser': 2.0.2 987 | '@vitest/ui': 2.0.2 988 | happy-dom: '*' 989 | jsdom: '*' 990 | peerDependenciesMeta: 991 | '@edge-runtime/vm': 992 | optional: true 993 | '@types/node': 994 | optional: true 995 | '@vitest/browser': 996 | optional: true 997 | '@vitest/ui': 998 | optional: true 999 | happy-dom: 1000 | optional: true 1001 | jsdom: 1002 | optional: true 1003 | dependencies: 1004 | '@ampproject/remapping': 2.3.0 1005 | '@types/node': 20.6.2 1006 | '@vitest/expect': 2.0.2 1007 | '@vitest/pretty-format': 2.0.2 1008 | '@vitest/runner': 2.0.2 1009 | '@vitest/snapshot': 2.0.2 1010 | '@vitest/spy': 2.0.2 1011 | '@vitest/utils': 2.0.2 1012 | chai: 5.1.1 1013 | debug: 4.3.5 1014 | execa: 8.0.1 1015 | magic-string: 0.30.10 1016 | pathe: 1.1.2 1017 | std-env: 3.7.0 1018 | tinybench: 2.8.0 1019 | tinypool: 1.0.0 1020 | tinyrainbow: 1.2.0 1021 | vite: 5.2.11(@types/node@20.6.2) 1022 | vite-node: 2.0.2(@types/node@20.6.2) 1023 | why-is-node-running: 2.2.2 1024 | transitivePeerDependencies: 1025 | - less 1026 | - lightningcss 1027 | - sass 1028 | - stylus 1029 | - sugarss 1030 | - supports-color 1031 | - terser 1032 | dev: true 1033 | 1034 | /which@2.0.2: 1035 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1036 | engines: {node: '>= 8'} 1037 | hasBin: true 1038 | dependencies: 1039 | isexe: 2.0.0 1040 | dev: true 1041 | 1042 | /why-is-node-running@2.2.2: 1043 | resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} 1044 | engines: {node: '>=8'} 1045 | hasBin: true 1046 | dependencies: 1047 | siginfo: 2.0.0 1048 | stackback: 0.0.2 1049 | dev: true 1050 | -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": { 3 | ".": { 4 | "changelog-path": "CHANGELOG.md", 5 | "release-type": "node", 6 | "bump-minor-pre-major": false, 7 | "bump-patch-for-minor-pre-major": false 8 | } 9 | }, 10 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" 11 | } 12 | -------------------------------------------------------------------------------- /src/DownloadURL.ts: -------------------------------------------------------------------------------- 1 | import * as core from "@actions/core"; 2 | import { UnsupportedPlatformError } from "./errors"; 3 | import { Arch, OS, type Platform } from "./platform"; 4 | import { LatestVersion } from "./versions"; 5 | 6 | export default interface DownloadURL { 7 | getURL(): string; 8 | } 9 | 10 | export class ArchiveDownloadURL implements DownloadURL { 11 | constructor( 12 | private readonly version: string, 13 | private readonly platform: Platform, 14 | private readonly language: string, 15 | ) {} 16 | 17 | getURL(): string { 18 | return `https://ftp.mozilla.org/pub/${this.productPart()}/releases/${this.versionPart()}/${this.platformPart()}/${ 19 | this.language 20 | }/${this.filename()}`; 21 | } 22 | 23 | private productPart(): string { 24 | const lastIndex = this.version.lastIndexOf("-"); 25 | const productName = this.version.slice(0, lastIndex); 26 | switch (productName) { 27 | case "firefox": 28 | return "firefox"; 29 | case "beta": 30 | return "firefox"; 31 | case "devedition": 32 | return "devedition"; 33 | default: // nightly, esr are unsupported 34 | return "firefox"; 35 | } 36 | } 37 | 38 | private versionPart(): string { 39 | const lastIndex = this.version.lastIndexOf("-"); 40 | const version = this.version.slice(lastIndex + 1); 41 | return version; 42 | } 43 | 44 | private platformPart(): string { 45 | const { os, arch } = this.platform; 46 | 47 | if (os === OS.MACOS && arch === Arch.AMD64) { 48 | return "mac"; 49 | } else if (os === OS.MACOS && arch === Arch.ARM64) { 50 | return "mac"; 51 | } else if (os === OS.LINUX && arch === Arch.I686) { 52 | return "linux-i686"; 53 | } else if (os === OS.LINUX && arch === Arch.AMD64) { 54 | return "linux-x86_64"; 55 | } else if (os === OS.WINDOWS && arch === Arch.I686) { 56 | return "win32"; 57 | } else if (os === OS.WINDOWS && arch === Arch.AMD64) { 58 | return "win64"; 59 | } else if (os === OS.WINDOWS && arch === Arch.ARM64) { 60 | return "win64-aarch64"; 61 | } 62 | throw new UnsupportedPlatformError({ os, arch }, this.versionPart()); 63 | } 64 | 65 | private filename(): string { 66 | const { os } = this.platform; 67 | core.info( 68 | `This is the version number:${Number.parseInt(this.versionPart(), 10)}`, 69 | ); 70 | switch (os) { 71 | case OS.MACOS: 72 | return `Firefox%20${this.versionPart()}.dmg`; 73 | case OS.LINUX: 74 | if (Number.parseInt(this.versionPart(), 10) <= 134) { 75 | return `firefox-${this.versionPart()}.tar.bz2`; 76 | } else { 77 | return `firefox-${this.versionPart()}.tar.xz`; 78 | } 79 | case OS.WINDOWS: 80 | return `Firefox%20Setup%20${this.versionPart()}.exe`; 81 | } 82 | } 83 | } 84 | 85 | export class LatestDownloadURL implements DownloadURL { 86 | constructor( 87 | private readonly version: LatestVersion, 88 | private readonly platform: Platform, 89 | private readonly language: string, 90 | ) {} 91 | 92 | getURL(): string { 93 | return `https://download.mozilla.org/?product=${this.productPart()}&os=${this.platformPart()}&lang=${ 94 | this.language 95 | }`; 96 | } 97 | 98 | private productPart(): string { 99 | switch (this.version) { 100 | case LatestVersion.LATEST: 101 | return "firefox-latest"; 102 | case LatestVersion.LATEST_BETA: 103 | return "firefox-beta-latest"; 104 | case LatestVersion.LATEST_DEVEDITION: 105 | return "firefox-devedition-latest"; 106 | case LatestVersion.LATEST_NIGHTLY: 107 | return "firefox-nightly-latest"; 108 | case LatestVersion.LATEST_ESR: 109 | return "firefox-esr-latest"; 110 | } 111 | } 112 | 113 | private platformPart(): string { 114 | const { os, arch } = this.platform; 115 | if (os === OS.MACOS && arch === Arch.AMD64) { 116 | return "osx"; 117 | } else if (os === OS.MACOS && arch === Arch.ARM64) { 118 | return "osx"; 119 | } else if (os === OS.LINUX && arch === Arch.I686) { 120 | return "linux"; 121 | } else if (os === OS.LINUX && arch === Arch.AMD64) { 122 | return "linux64"; 123 | } else if (os === OS.WINDOWS && arch === Arch.I686) { 124 | return "win"; 125 | } else if (os === OS.WINDOWS && arch === Arch.AMD64) { 126 | return "win64"; 127 | } 128 | throw new UnsupportedPlatformError({ os, arch }, this.version); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/DownloadURLFactory.ts: -------------------------------------------------------------------------------- 1 | import type DownloadURL from "./DownloadURL"; 2 | import { ArchiveDownloadURL, LatestDownloadURL } from "./DownloadURL"; 3 | import type { Platform } from "./platform"; 4 | import { isLatestVersion } from "./versions"; 5 | 6 | export class DownloadURLFactory { 7 | constructor( 8 | private readonly version: string, 9 | private readonly platform: Platform, 10 | private readonly language: string, 11 | ) {} 12 | create(): DownloadURL { 13 | if (isLatestVersion(this.version)) { 14 | return new LatestDownloadURL(this.version, this.platform, this.language); 15 | } 16 | return new ArchiveDownloadURL(this.version, this.platform, this.language); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Installer.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | import * as core from "@actions/core"; 4 | import * as exec from "@actions/exec"; 5 | import * as io from "@actions/io"; 6 | import * as tc from "@actions/tool-cache"; 7 | import { DownloadURLFactory } from "./DownloadURLFactory"; 8 | import type { Platform } from "./platform"; 9 | import { LatestVersion } from "./versions"; 10 | 11 | export type InstallSpec = { 12 | version: string; 13 | platform: Platform; 14 | language: string; 15 | }; 16 | 17 | export default interface Installer { 18 | install(spec: InstallSpec): Promise<string>; 19 | 20 | testVersion(bin: string): Promise<string>; 21 | } 22 | 23 | const commonTestVersion = async (bin: string): Promise<string> => { 24 | const output = await exec.getExecOutput(`"${bin}"`, ["--version"]); 25 | if (output.exitCode !== 0) { 26 | throw new Error( 27 | `firefox exits with status ${output.exitCode}: ${output.stderr}`, 28 | ); 29 | } 30 | if (!output.stdout.startsWith("Mozilla Firefox ")) { 31 | throw new Error(`firefox outputs unexpected results: ${output.stdout}`); 32 | } 33 | return output.stdout.trimEnd().replace("Mozilla Firefox ", ""); 34 | }; 35 | 36 | export class LinuxInstaller implements Installer { 37 | async install(spec: InstallSpec): Promise<string> { 38 | return this.download(spec); 39 | } 40 | 41 | private async download({ 42 | version, 43 | platform, 44 | language, 45 | }: InstallSpec): Promise<string> { 46 | const toolPath = tc.find("firefox", version); 47 | if (toolPath) { 48 | core.info(`Found in cache @ ${toolPath}`); 49 | return toolPath; 50 | } 51 | core.info(`Attempting to download firefox ${version}...`); 52 | 53 | const url = new DownloadURLFactory(version, platform, language) 54 | .create() 55 | .getURL(); 56 | core.info(`Acquiring ${version} from ${url}`); 57 | 58 | const archivePath = await tc.downloadTool(url); 59 | core.info("Extracting Firefox..."); 60 | const handle = await fs.promises.open(archivePath, "r"); 61 | const firstBytes = new Int8Array(3); 62 | if (handle !== null) { 63 | await handle.read(firstBytes, 0, 3, null); 64 | core.debug( 65 | `Extracted ${firstBytes[0]}, ${firstBytes[1]} and ${firstBytes[2]}`, 66 | ); 67 | } 68 | const options = 69 | firstBytes[0] === 66 && firstBytes[1] === 90 && firstBytes[2] === 104 70 | ? "xj" 71 | : "xJ"; 72 | const extPath = await tc.extractTar(archivePath, "", [ 73 | options, 74 | "--strip-components=1", 75 | ]); 76 | core.info(`Successfully extracted firefox ${version} to ${extPath}`); 77 | 78 | core.info("Adding to the cache ..."); 79 | const cachedDir = await tc.cacheDir(extPath, "firefox", version); 80 | core.info(`Successfully cached firefox ${version} to ${cachedDir}`); 81 | return cachedDir; 82 | } 83 | 84 | testVersion = commonTestVersion; 85 | } 86 | 87 | export class MacOSInstaller implements Installer { 88 | async install(spec: InstallSpec): Promise<string> { 89 | const installPath = await this.download(spec); 90 | return path.join(installPath, "Contents", "MacOS"); 91 | } 92 | 93 | async download({ 94 | version, 95 | platform, 96 | language, 97 | }: InstallSpec): Promise<string> { 98 | const toolPath = tc.find("firefox", version); 99 | if (toolPath) { 100 | core.info(`Found in cache @ ${toolPath}`); 101 | return toolPath; 102 | } 103 | core.info(`Attempting to download firefox ${version}...`); 104 | 105 | const url = new DownloadURLFactory(version, platform, language) 106 | .create() 107 | .getURL(); 108 | core.info(`Acquiring ${version} from ${url}`); 109 | 110 | const archivePath = await tc.downloadTool(url); 111 | core.info("Extracting Firefox..."); 112 | 113 | const mountpoint = path.join("/Volumes", path.basename(archivePath)); 114 | const appPath = (() => { 115 | if (version === LatestVersion.LATEST_NIGHTLY) { 116 | return path.join(mountpoint, "Firefox Nightly.app"); 117 | } else if (version.includes("devedition")) { 118 | return path.join(mountpoint, "Firefox Developer Edition.app"); 119 | } else { 120 | return path.join(mountpoint, "Firefox.app"); 121 | } 122 | })(); 123 | 124 | await exec.exec("hdiutil", [ 125 | "attach", 126 | "-quiet", 127 | "-noautofsck", 128 | "-noautoopen", 129 | "-mountpoint", 130 | mountpoint, 131 | archivePath, 132 | ]); 133 | core.info(`Successfully extracted firefox ${version} to ${appPath}`); 134 | 135 | core.info("Adding to the cache ..."); 136 | const cachedDir = await tc.cacheDir(appPath, "firefox", version); 137 | core.info(`Successfully cached firefox ${version} to ${cachedDir}`); 138 | return cachedDir; 139 | } 140 | 141 | testVersion = commonTestVersion; 142 | } 143 | 144 | export class WindowsInstaller implements Installer { 145 | install(spec: InstallSpec): Promise<string> { 146 | return this.download(spec); 147 | } 148 | 149 | async download({ 150 | version, 151 | platform, 152 | language, 153 | }: InstallSpec): Promise<string> { 154 | const installPath = `C:\\Program Files\\Firefox_${version}`; 155 | if (await this.checkInstall(installPath)) { 156 | core.info(`Already installed @ ${installPath}`); 157 | return installPath; 158 | } 159 | 160 | core.info(`Attempting to download firefox ${version}...`); 161 | 162 | const url = new DownloadURLFactory(version, platform, language) 163 | .create() 164 | .getURL(); 165 | core.info(`Acquiring ${version} from ${url}`); 166 | 167 | const installerPath = await tc.downloadTool(url); 168 | await io.mv(installerPath, `${installerPath}.exe`); 169 | core.info("Extracting Firefox..."); 170 | 171 | await exec.exec(installerPath, [ 172 | "/S", 173 | `/InstallDirectoryName=${path.basename(installPath)}`, 174 | ]); 175 | core.info(`Successfully installed firefox ${version} to ${installPath}`); 176 | 177 | return installPath; 178 | } 179 | 180 | private async checkInstall(dir: string): Promise<boolean> { 181 | try { 182 | await fs.promises.access(dir, fs.constants.F_OK); 183 | } catch (err) { 184 | return false; 185 | } 186 | return true; 187 | } 188 | 189 | testVersion = commonTestVersion; 190 | } 191 | -------------------------------------------------------------------------------- /src/InstallerFactory.ts: -------------------------------------------------------------------------------- 1 | import type Installer from "./Installer"; 2 | import { LinuxInstaller, MacOSInstaller, WindowsInstaller } from "./Installer"; 3 | import { OS, type Platform } from "./platform"; 4 | 5 | export default class InstallerFactory { 6 | create(platform: Platform): Installer { 7 | switch (platform.os) { 8 | case OS.LINUX: 9 | return new LinuxInstaller(); 10 | case OS.MACOS: 11 | return new MacOSInstaller(); 12 | case OS.WINDOWS: 13 | return new WindowsInstaller(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/errors.ts: -------------------------------------------------------------------------------- 1 | import type { Platform } from "./platform"; 2 | 3 | export class UnsupportedPlatformError extends Error { 4 | constructor( 5 | private readonly platform: Platform, 6 | private readonly version?: string, 7 | ) { 8 | super( 9 | version 10 | ? `Unsupported platform ${platform.os} ${platform.arch} for version ${version}` 11 | : `Unsupported platform ${platform.os} ${platform.arch}`, 12 | ); 13 | 14 | this.name = "UnsupportedPlatform"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from "node:path"; 2 | import * as core from "@actions/core"; 3 | import InstallerFactory from "./InstallerFactory"; 4 | import { getPlatform } from "./platform"; 5 | import { LatestVersion } from "./versions"; 6 | 7 | const hasErrorMessage = (e: unknown): e is { message: string | Error } => { 8 | return typeof e === "object" && e !== null && "message" in e; 9 | }; 10 | 11 | const run = async (): Promise<void> => { 12 | try { 13 | const version = core.getInput("firefox-version") || LatestVersion.LATEST; 14 | const platform = getPlatform(); 15 | const language = core.getInput("firefox-language") || "en-US"; 16 | 17 | core.info(`Setup firefox ${version} (${language})`); 18 | 19 | const installer = new InstallerFactory().create(platform); 20 | const installDir = await installer.install({ version, platform, language }); 21 | 22 | core.addPath(installDir); 23 | 24 | const installedBinPath = path.join(installDir, "firefox"); 25 | const installedVersion = await installer.testVersion(installedBinPath); 26 | core.info(`Successfully setup firefox version ${installedVersion}`); 27 | core.setOutput("firefox-version", installedVersion); 28 | core.setOutput("firefox-path", installedBinPath); 29 | } catch (error) { 30 | if (hasErrorMessage(error)) { 31 | core.setFailed(error.message); 32 | } else { 33 | core.setFailed(String(error)); 34 | } 35 | } 36 | }; 37 | 38 | run(); 39 | -------------------------------------------------------------------------------- /src/platform.ts: -------------------------------------------------------------------------------- 1 | import os from "node:os"; 2 | 3 | export type Platform = { 4 | os: OS; 5 | arch: Arch; 6 | }; 7 | 8 | export const OS = { 9 | MACOS: "macos", 10 | LINUX: "linux", 11 | WINDOWS: "windows", 12 | } as const; 13 | 14 | export type OS = (typeof OS)[keyof typeof OS]; 15 | 16 | export const Arch = { 17 | AMD64: "amd64", 18 | I686: "i686", 19 | ARM64: "arm64", 20 | } as const; 21 | 22 | export type Arch = (typeof Arch)[keyof typeof Arch]; 23 | 24 | export const getOS = (): OS => { 25 | const platform = os.platform(); 26 | switch (platform) { 27 | case "linux": 28 | return OS.LINUX; 29 | case "darwin": 30 | return OS.MACOS; 31 | case "win32": 32 | return OS.WINDOWS; 33 | } 34 | throw new Error(`Unsupported platform: ${platform}`); 35 | }; 36 | 37 | export const getArch = (): Arch => { 38 | const arch = os.arch(); 39 | switch (arch) { 40 | case "arm64": 41 | return Arch.ARM64; 42 | case "x32": 43 | return Arch.I686; 44 | case "x64": 45 | return Arch.AMD64; 46 | } 47 | throw new Error(`Unsupported arch: ${arch}`); 48 | }; 49 | 50 | export const getPlatform = (): Platform => { 51 | return { 52 | os: getOS(), 53 | arch: getArch(), 54 | }; 55 | }; 56 | -------------------------------------------------------------------------------- /src/versions.ts: -------------------------------------------------------------------------------- 1 | export const LatestVersion = { 2 | LATEST_DEVEDITION: "latest-devedition", 3 | LATEST_NIGHTLY: "latest-nightly", 4 | LATEST_BETA: "latest-beta", 5 | LATEST_ESR: "latest-esr", 6 | LATEST: "latest", 7 | } as const; 8 | 9 | export type LatestVersion = (typeof LatestVersion)[keyof typeof LatestVersion]; 10 | 11 | export const isLatestVersion = (version: string): version is LatestVersion => { 12 | return ( 13 | version === LatestVersion.LATEST || 14 | version === LatestVersion.LATEST_BETA || 15 | version === LatestVersion.LATEST_DEVEDITION || 16 | version === LatestVersion.LATEST_NIGHTLY || 17 | version === LatestVersion.LATEST_ESR 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "outDir": "./dist", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "noImplicitAny": true, 10 | "esModuleInterop": true 11 | }, 12 | "exclude": ["node_modules", "**/*.test.ts"] 13 | } 14 | --------------------------------------------------------------------------------