feat: self managed versioning

This commit is contained in:
daimond113 2024-07-26 18:47:53 +02:00
parent 0fcddedad6
commit 7f8b2761ab
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
29 changed files with 1011 additions and 571 deletions

View file

@ -3,6 +3,8 @@ on:
push: push:
tags: tags:
- v* - v*
env:
BIN_NAME: pesde
jobs: jobs:
# Better to check first, runners other than ubuntu-latest take up more free minutes # Better to check first, runners other than ubuntu-latest take up more free minutes
check: check:
@ -14,29 +16,29 @@ jobs:
run: cargo check --all-features --locked run: cargo check --all-features --locked
build: build:
needs: [check] needs: [ check ]
strategy: strategy:
matrix: matrix:
include: include:
- os: ubuntu-latest - os: ubuntu-latest
host: linux host: linux
arch: x86_64 arch: x86_64
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu
- os: windows-latest - os: windows-latest
host: windows host: windows
arch: x86_64 arch: x86_64
target: x86_64-pc-windows-msvc target: x86_64-pc-windows-msvc
- os: macos-13 - os: macos-13
host: macos host: macos
arch: x86_64 arch: x86_64
target: x86_64-apple-darwin target: x86_64-apple-darwin
- os: macos-latest - os: macos-latest
host: macos host: macos
arch: aarch64 arch: aarch64
target: aarch64-apple-darwin target: aarch64-apple-darwin
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
name: Build for ${{ matrix.host }}-${{ matrix.arch }} name: Build for ${{ matrix.host }}-${{ matrix.arch }}
steps: steps:
@ -44,10 +46,8 @@ jobs:
- name: Set env - name: Set env
shell: bash shell: bash
run: | run: |
BIN_NAME=pesde ARCHIVE_NAME=${{ env.BIN_NAME }}-$(echo ${{ github.ref_name }} | cut -c 2-)-${{ matrix.host }}-${{ matrix.arch }}
ARCHIVE_NAME=$BIN_NAME-$(echo ${{ github.ref_name }} | cut -c 2-)-${{ matrix.host }}-${{ matrix.arch }}.zip
echo "BIN_NAME=$BIN_NAME" >> $GITHUB_ENV
echo "ARCHIVE_NAME=$ARCHIVE_NAME" >> $GITHUB_ENV echo "ARCHIVE_NAME=$ARCHIVE_NAME" >> $GITHUB_ENV
- name: Build - name: Build
@ -57,23 +57,31 @@ jobs:
shell: bash shell: bash
run: | run: |
if [ ${{ matrix.host }} = "windows" ]; then if [ ${{ matrix.host }} = "windows" ]; then
cp target/${{ matrix.target }}/release/${{ env.BIN_NAME }}.exe ${{ env.BIN_NAME }}.exe mv target/${{ matrix.target }}/release/${{ env.BIN_NAME }}.exe ${{ env.BIN_NAME }}.exe
7z a ${{ env.ARCHIVE_NAME }} ${{ env.BIN_NAME }}.exe 7z a ${{ env.ARCHIVE_NAME }}.zip ${{ env.BIN_NAME }}.exe
tar -czf ${{ env.ARCHIVE_NAME }}.tar.gz ${{ env.BIN_NAME }}.exe
else else
cp target/${{ matrix.target }}/release/${{ env.BIN_NAME }} ${{ env.BIN_NAME }} mv target/${{ matrix.target }}/release/${{ env.BIN_NAME }} ${{ env.BIN_NAME }}
zip -r ${{ env.ARCHIVE_NAME }} ${{ env.BIN_NAME }} zip -r ${{ env.ARCHIVE_NAME }}.zip ${{ env.BIN_NAME }}
tar -czf ${{ env.ARCHIVE_NAME }}.tar.gz ${{ env.BIN_NAME }}
fi fi
- name: Upload assets - name: Upload zip artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ${{ env.ARCHIVE_NAME }} name: ${{ env.ARCHIVE_NAME }}.zip
path: ${{ env.ARCHIVE_NAME }} path: ${{ env.ARCHIVE_NAME }}.zip
- name: Upload tar.gz artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.ARCHIVE_NAME }}.tar.gz
path: ${{ env.ARCHIVE_NAME }}.tar.gz
publish: publish:
name: Publish to crates.io name: Publish to crates.io
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build] needs: [ build ]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Publish - name: Publish
@ -85,7 +93,7 @@ jobs:
permissions: permissions:
contents: write contents: write
pull-requests: read pull-requests: read
needs: [build, publish] needs: [ build, publish ]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:

234
Cargo.lock generated
View file

@ -72,9 +72,9 @@ dependencies = [
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.14" version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse",
@ -87,33 +87,33 @@ dependencies = [
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "1.0.7" version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
version = "0.2.4" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [ dependencies = [
"utf8parse", "utf8parse",
] ]
[[package]] [[package]]
name = "anstyle-query" name = "anstyle-query"
version = "1.1.0" version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-wincon"
version = "3.0.3" version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"windows-sys 0.52.0", "windows-sys 0.52.0",
@ -222,7 +222,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -257,7 +257,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -338,9 +338,9 @@ dependencies = [
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "1.9.1" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
dependencies = [ dependencies = [
"memchr", "memchr",
"regex-automata", "regex-automata",
@ -450,9 +450,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.9" version = "4.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -460,9 +460,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.9" version = "4.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -472,21 +472,21 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.8" version = "4.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.7.1" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]] [[package]]
name = "clru" name = "clru"
@ -496,9 +496,9 @@ checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.1" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]] [[package]]
name = "colored" name = "colored"
@ -608,7 +608,7 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"crossterm_winapi", "crossterm_winapi",
"libc", "libc",
"mio", "mio 0.8.11",
"parking_lot", "parking_lot",
"signal-hook", "signal-hook",
"signal-hook-mio", "signal-hook-mio",
@ -655,7 +655,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -666,7 +666,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -682,9 +682,9 @@ dependencies = [
[[package]] [[package]]
name = "dbus-secret-service" name = "dbus-secret-service"
version = "4.0.1" version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3ae92ba157c1fd7e3453bf8fad9cd2e62783fe5f65047fd3b52fcbf6594aef" checksum = "1caa0c241c01ad8d99a78d553567d38f873dd3ac16eca33a5370d650ab25584e"
dependencies = [ dependencies = [
"aes", "aes",
"block-padding", "block-padding",
@ -722,7 +722,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -735,7 +735,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustc_version", "rustc_version",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -750,10 +750,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "directories" name = "dirs"
version = "5.0.1" version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [ dependencies = [
"dirs-sys", "dirs-sys",
] ]
@ -778,7 +778,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -832,7 +832,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -1004,7 +1004,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -1161,7 +1161,7 @@ dependencies = [
"gix-utils", "gix-utils",
"itoa", "itoa",
"thiserror", "thiserror",
"winnow 0.6.14", "winnow 0.6.16",
] ]
[[package]] [[package]]
@ -1243,7 +1243,7 @@ dependencies = [
"smallvec", "smallvec",
"thiserror", "thiserror",
"unicode-bom", "unicode-bom",
"winnow 0.6.14", "winnow 0.6.16",
] ]
[[package]] [[package]]
@ -1261,9 +1261,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-credentials" name = "gix-credentials"
version = "0.24.3" version = "0.24.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91b446df0841c9d74b3f98f21657b892581a4af78904a22e0cbc6144da972eea" checksum = "198588f532e4d1202e04e6c3f50e4d7c060dffc66801c6f53cc246f1d234739e"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-command", "gix-command",
@ -1461,7 +1461,7 @@ checksum = "999ce923619f88194171a67fb3e6d613653b8d4d6078b529b15a765da0edcc17"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -1496,7 +1496,7 @@ dependencies = [
"itoa", "itoa",
"smallvec", "smallvec",
"thiserror", "thiserror",
"winnow 0.6.14", "winnow 0.6.16",
] ]
[[package]] [[package]]
@ -1619,7 +1619,7 @@ dependencies = [
"gix-utils", "gix-utils",
"maybe-async", "maybe-async",
"thiserror", "thiserror",
"winnow 0.6.14", "winnow 0.6.16",
] ]
[[package]] [[package]]
@ -1651,7 +1651,7 @@ dependencies = [
"gix-validate", "gix-validate",
"memmap2", "memmap2",
"thiserror", "thiserror",
"winnow 0.6.14", "winnow 0.6.16",
] ]
[[package]] [[package]]
@ -1728,9 +1728,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-tempfile" name = "gix-tempfile"
version = "14.0.0" version = "14.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3b0e276cd08eb2a22e9f286a4f13a222a01be2defafa8621367515375644b99" checksum = "006acf5a613e0b5cf095d8e4b3f48c12a60d9062aa2b2dd105afaf8344a5600c"
dependencies = [ dependencies = [
"gix-fs", "gix-fs",
"libc", "libc",
@ -2212,9 +2212,9 @@ dependencies = [
[[package]] [[package]]
name = "is_terminal_polyfill" name = "is_terminal_polyfill"
version = "1.70.0" version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]] [[package]]
name = "itoa" name = "itoa"
@ -2224,9 +2224,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]] [[package]]
name = "jobserver" name = "jobserver"
version = "0.1.31" version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -2242,9 +2242,9 @@ dependencies = [
[[package]] [[package]]
name = "keyring" name = "keyring"
version = "3.0.3" version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9961b98f55dc0b2737000132505bdafa249abab147ee9de43c50ae04a054aa6c" checksum = "c118b1bc529b034aad851808f41f49a69a337d10e112039e7f342e5fd514635b"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"dbus-secret-service", "dbus-secret-service",
@ -2256,9 +2256,9 @@ dependencies = [
[[package]] [[package]]
name = "kstring" name = "kstring"
version = "2.0.0" version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1"
dependencies = [ dependencies = [
"static_assertions", "static_assertions",
] ]
@ -2390,7 +2390,7 @@ checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -2454,6 +2454,18 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "mio"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
dependencies = [
"hermit-abi 0.3.9",
"libc",
"wasi",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "newline-converter" name = "newline-converter"
version = "0.3.0" version = "0.3.0"
@ -2582,9 +2594,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]] [[package]]
name = "object" name = "object"
version = "0.36.1" version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@ -2705,7 +2717,7 @@ dependencies = [
"chrono", "chrono",
"clap", "clap",
"colored", "colored",
"directories", "dirs",
"flate2", "flate2",
"full_moon", "full_moon",
"git2", "git2",
@ -2728,8 +2740,9 @@ dependencies = [
"thiserror", "thiserror",
"threadpool", "threadpool",
"toml", "toml",
"toml_edit 0.22.16", "toml_edit 0.22.17",
"url", "url",
"winreg",
"zip", "zip",
] ]
@ -2750,7 +2763,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -2885,14 +2898,13 @@ dependencies = [
[[package]] [[package]]
name = "quinn-udp" name = "quinn-udp"
version = "0.5.2" version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285"
dependencies = [ dependencies = [
"libc", "libc",
"once_cell", "once_cell",
"socket2", "socket2",
"tracing",
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
@ -3100,9 +3112,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.11" version = "0.23.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"ring", "ring",
@ -3130,9 +3142,9 @@ checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.102.5" version = "0.102.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e"
dependencies = [ dependencies = [
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
@ -3228,7 +3240,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -3250,14 +3262,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "0.6.6" version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -3301,7 +3313,7 @@ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -3355,7 +3367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [ dependencies = [
"libc", "libc",
"mio", "mio 0.8.11",
"signal-hook", "signal-hook",
] ]
@ -3445,9 +3457,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.71" version = "2.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3530,7 +3542,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -3602,17 +3614,17 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.38.1" version = "1.39.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
"libc", "libc",
"mio", "mio 1.0.1",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"windows-sys 0.48.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -3641,21 +3653,21 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.8.15" version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c"
dependencies = [ dependencies = [
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
"toml_edit 0.22.16", "toml_edit 0.22.17",
] ]
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.6" version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -3673,15 +3685,15 @@ dependencies = [
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.22.16" version = "0.22.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16"
dependencies = [ dependencies = [
"indexmap 2.2.6", "indexmap 2.2.6",
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
"winnow 0.6.14", "winnow 0.6.16",
] ]
[[package]] [[package]]
@ -3730,7 +3742,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -3845,9 +3857,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "walkdir" name = "walkdir"
@ -3895,7 +3907,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -3929,7 +3941,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -4149,9 +4161,9 @@ dependencies = [
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.6.14" version = "0.6.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f" checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@ -4189,9 +4201,9 @@ dependencies = [
[[package]] [[package]]
name = "zbus" name = "zbus"
version = "4.3.1" version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "851238c133804e0aa888edf4a0229481c753544ca12a60fd1c3230c8a500fe40" checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
dependencies = [ dependencies = [
"async-broadcast", "async-broadcast",
"async-process", "async-process",
@ -4221,14 +4233,14 @@ dependencies = [
[[package]] [[package]]
name = "zbus_macros" name = "zbus_macros"
version = "4.3.1" version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d5a3f12c20bd473be3194af6b49d50d7bb804ef3192dc70eddedb26b85d9da7" checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
"zvariant_utils", "zvariant_utils",
] ]
@ -4260,7 +4272,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -4280,7 +4292,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -4356,9 +4368,9 @@ dependencies = [
[[package]] [[package]]
name = "zvariant" name = "zvariant"
version = "4.1.2" version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1724a2b330760dc7d2a8402d841119dc869ef120b139d29862d6980e9c75bfc9" checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
dependencies = [ dependencies = [
"endi", "endi",
"enumflags2", "enumflags2",
@ -4369,24 +4381,24 @@ dependencies = [
[[package]] [[package]]
name = "zvariant_derive" name = "zvariant_derive"
version = "4.1.2" version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55025a7a518ad14518fb243559c058a2e5b848b015e31f1d90414f36e3317859" checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
"zvariant_utils", "zvariant_utils",
] ]
[[package]] [[package]]
name = "zvariant_utils" name = "zvariant_utils"
version = "2.0.0" version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc242db087efc22bd9ade7aa7809e4ba828132edc312871584a6b4391bdf8786" checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.71", "syn 2.0.72",
] ]

View file

@ -10,7 +10,24 @@ repository = "https://github.com/daimond113/pesde"
include = ["src/**/*", "Cargo.toml", "Cargo.lock", "README.md", "LICENSE", "CHANGELOG.md"] include = ["src/**/*", "Cargo.toml", "Cargo.lock", "README.md", "LICENSE", "CHANGELOG.md"]
[features] [features]
bin = ["clap", "directories", "pretty_env_logger", "reqwest/json", "reqwest/multipart", "indicatif", "indicatif-log-bridge", "inquire", "toml_edit", "colored", "anyhow", "keyring", "open", "gix/worktree-mutation", "serde_json"] bin = [
"clap",
"dirs",
"pretty_env_logger",
"reqwest/json",
"reqwest/multipart",
"indicatif",
"indicatif-log-bridge",
"inquire",
"toml_edit",
"colored",
"anyhow",
"keyring",
"open",
"gix/worktree-mutation",
"serde_json",
"winreg"
]
wally-compat = ["zip", "serde_json", "roblox"] wally-compat = ["zip", "serde_json", "roblox"]
roblox = [] roblox = []
lune = [] lune = []
@ -27,7 +44,7 @@ uninlined_format_args = "warn"
[dependencies] [dependencies]
serde = { version = "1.0.204", features = ["derive"] } serde = { version = "1.0.204", features = ["derive"] }
toml = "0.8.15" toml = "0.8.16"
serde_with = "3.9.0" serde_with = "3.9.0"
gix = { version = "0.64.0", default-features = false, features = ["blocking-http-transport-reqwest-rust-tls", "revparse-regex", "credentials"] } gix = { version = "0.64.0", default-features = false, features = ["blocking-http-transport-reqwest-rust-tls", "revparse-regex", "credentials"] }
semver = { version = "1.0.23", features = ["serde"] } semver = { version = "1.0.23", features = ["serde"] }
@ -53,16 +70,19 @@ serde_json = { version = "1.0.120", optional = true }
anyhow = { version = "1.0.86", optional = true } anyhow = { version = "1.0.86", optional = true }
open = { version = "5.3.0", optional = true } open = { version = "5.3.0", optional = true }
keyring = { version = "3.0.3", features = ["crypto-rust", "windows-native", "apple-native", "linux-native"], optional = true } keyring = { version = "3.0.4", features = ["crypto-rust", "windows-native", "apple-native", "linux-native"], optional = true }
colored = { version = "2.1.0", optional = true } colored = { version = "2.1.0", optional = true }
toml_edit = { version = "0.22.16", optional = true } toml_edit = { version = "0.22.17", optional = true }
clap = { version = "4.5.9", features = ["derive"], optional = true } clap = { version = "4.5.11", features = ["derive"], optional = true }
directories = { version = "5.0.1", optional = true } dirs = { version = "5.0.1", optional = true }
pretty_env_logger = { version = "0.5.0", optional = true } pretty_env_logger = { version = "0.5.0", optional = true }
indicatif = { version = "0.17.8", optional = true } indicatif = { version = "0.17.8", optional = true }
indicatif-log-bridge = { version = "0.2.2", optional = true } indicatif-log-bridge = { version = "0.2.2", optional = true }
inquire = { version = "0.7.5", optional = true } inquire = { version = "0.7.5", optional = true }
[target.'cfg(target_os = "windows")'.dependencies]
winreg = { version = "0.52.0", optional = true }
[workspace] [workspace]
resolver = "2" resolver = "2"
members = [] members = []

75
src/cli/auth.rs Normal file
View file

@ -0,0 +1,75 @@
use crate::cli::config::{read_config, write_config};
use anyhow::Context;
use keyring::Entry;
use serde::Deserialize;
use std::path::Path;
pub fn get_token<P: AsRef<Path>>(data_dir: P) -> anyhow::Result<Option<String>> {
match std::env::var("PESDE_TOKEN") {
Ok(token) => return Ok(Some(token)),
Err(std::env::VarError::NotPresent) => {}
Err(e) => return Err(e.into()),
}
let config = read_config(data_dir)?;
if let Some(token) = config.token {
return Ok(Some(token));
}
match Entry::new("token", env!("CARGO_PKG_NAME")) {
Ok(entry) => match entry.get_password() {
Ok(token) => return Ok(Some(token)),
Err(keyring::Error::PlatformFailure(_) | keyring::Error::NoEntry) => {}
Err(e) => return Err(e.into()),
},
Err(keyring::Error::PlatformFailure(_)) => {}
Err(e) => return Err(e.into()),
}
Ok(None)
}
pub fn set_token<P: AsRef<Path>>(data_dir: P, token: Option<&str>) -> anyhow::Result<()> {
let entry = match Entry::new("token", env!("CARGO_PKG_NAME")) {
Ok(entry) => entry,
Err(e) => return Err(e.into()),
};
let result = if let Some(token) = token {
entry.set_password(token)
} else {
entry.delete_credential()
};
match result {
Ok(()) => return Ok(()),
Err(keyring::Error::PlatformFailure(_) | keyring::Error::NoEntry) => {}
Err(e) => return Err(e.into()),
}
let mut config = read_config(&data_dir)?;
config.token = token.map(|s| s.to_string());
write_config(data_dir, &config)?;
Ok(())
}
#[derive(Debug, Deserialize)]
struct UserResponse {
login: String,
}
pub fn get_token_login(
reqwest: &reqwest::blocking::Client,
access_token: &str,
) -> anyhow::Result<String> {
let response = reqwest
.get("https://api.github.com/user")
.header("Authorization", format!("Bearer {access_token}"))
.send()
.context("failed to send user request")?
.json::<UserResponse>()
.context("failed to parse user response")?;
Ok(response.login)
}

View file

@ -1,49 +0,0 @@
use anyhow::Context;
use clap::Subcommand;
use pesde::Project;
use serde::Deserialize;
mod login;
mod logout;
mod whoami;
#[derive(Debug, Deserialize)]
struct UserResponse {
login: String,
}
pub fn get_token_login(
reqwest: &reqwest::blocking::Client,
access_token: &str,
) -> anyhow::Result<String> {
let response = reqwest
.get("https://api.github.com/user")
.header("Authorization", format!("Bearer {access_token}"))
.send()
.context("failed to send user request")?
.json::<UserResponse>()
.context("failed to parse user response")?;
Ok(response.login)
}
#[derive(Debug, Subcommand)]
pub enum AuthCommands {
/// Logs in into GitHub, and stores the token
Login(login::LoginCommand),
/// Removes the stored token
Logout(logout::LogoutCommand),
/// Prints the username of the currently logged-in user
#[clap(name = "whoami")]
WhoAmI(whoami::WhoAmICommand),
}
impl AuthCommands {
pub fn run(self, project: Project) -> anyhow::Result<()> {
match self {
AuthCommands::Login(login) => login.run(project),
AuthCommands::Logout(logout) => logout.run(project),
AuthCommands::WhoAmI(whoami) => whoami.run(project),
}
}
}

View file

@ -1,4 +1,7 @@
use crate::cli::{auth::get_token_login, read_config, reqwest_client, set_token}; use crate::cli::{
auth::{get_token_login, set_token},
config::read_config,
};
use anyhow::Context; use anyhow::Context;
use clap::Args; use clap::Args;
use colored::Colorize; use colored::Colorize;
@ -171,9 +174,7 @@ impl LoginCommand {
anyhow::bail!("code expired, please re-run the login command"); anyhow::bail!("code expired, please re-run the login command");
} }
pub fn run(self, project: Project) -> anyhow::Result<()> { pub fn run(self, project: Project, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> {
let reqwest = reqwest_client(project.data_dir())?;
let token = match self.token { let token = match self.token {
Some(token) => token, Some(token) => token,
None => self.authenticate_device_flow(&project, &reqwest)?, None => self.authenticate_device_flow(&project, &reqwest)?,

View file

@ -1,4 +1,4 @@
use crate::cli::set_token; use crate::cli::auth::set_token;
use clap::Args; use clap::Args;
use pesde::Project; use pesde::Project;

View file

@ -0,0 +1,27 @@
use clap::Subcommand;
use pesde::Project;
mod login;
mod logout;
mod whoami;
#[derive(Debug, Subcommand)]
pub enum AuthCommands {
/// Logs in into GitHub, and stores the token
Login(login::LoginCommand),
/// Removes the stored token
Logout(logout::LogoutCommand),
/// Prints the username of the currently logged-in user
#[clap(name = "whoami")]
WhoAmI(whoami::WhoAmICommand),
}
impl AuthCommands {
pub fn run(self, project: Project, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> {
match self {
AuthCommands::Login(login) => login.run(project, reqwest),
AuthCommands::Logout(logout) => logout.run(project),
AuthCommands::WhoAmI(whoami) => whoami.run(project, reqwest),
}
}
}

View file

@ -1,4 +1,4 @@
use crate::cli::{auth::get_token_login, get_token, reqwest_client}; use crate::cli::{auth::get_token_login, get_token};
use clap::Args; use clap::Args;
use colored::Colorize; use colored::Colorize;
use pesde::Project; use pesde::Project;
@ -7,7 +7,7 @@ use pesde::Project;
pub struct WhoAmICommand {} pub struct WhoAmICommand {}
impl WhoAmICommand { impl WhoAmICommand {
pub fn run(self, project: Project) -> anyhow::Result<()> { pub fn run(self, project: Project, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> {
let token = match get_token(project.data_dir())? { let token = match get_token(project.data_dir())? {
Some(token) => token, Some(token) => token,
None => { None => {
@ -16,10 +16,7 @@ impl WhoAmICommand {
} }
}; };
println!( println!("logged in as {}", get_token_login(&reqwest, &token)?.bold());
"logged in as {}",
get_token_login(&reqwest_client(project.data_dir())?, &token)?.bold()
);
Ok(()) Ok(())
} }

View file

@ -1,4 +1,4 @@
use crate::cli::{read_config, write_config, CliConfig}; use crate::cli::config::{read_config, write_config, CliConfig};
use clap::Args; use clap::Args;
use pesde::Project; use pesde::Project;

View file

@ -1,4 +1,4 @@
use crate::cli::{read_config, write_config, CliConfig}; use crate::cli::config::{read_config, write_config, CliConfig};
use clap::Args; use clap::Args;
use pesde::Project; use pesde::Project;

View file

@ -1,4 +1,4 @@
use crate::cli::read_config; use crate::cli::config::read_config;
use anyhow::Context; use anyhow::Context;
use clap::Args; use clap::Args;
use colored::Colorize; use colored::Colorize;

View file

@ -1,4 +1,4 @@
use crate::cli::{home_dir, reqwest_client, IsUpToDate}; use crate::cli::{files::make_executable, home_dir, IsUpToDate};
use anyhow::Context; use anyhow::Context;
use clap::Args; use clap::Args;
use indicatif::MultiProgress; use indicatif::MultiProgress;
@ -55,7 +55,12 @@ end
} }
impl InstallCommand { impl InstallCommand {
pub fn run(self, project: Project, multi: MultiProgress) -> anyhow::Result<()> { pub fn run(
self,
project: Project,
multi: MultiProgress,
reqwest: reqwest::blocking::Client,
) -> anyhow::Result<()> {
let mut refreshed_sources = HashSet::new(); let mut refreshed_sources = HashSet::new();
let manifest = project let manifest = project
@ -133,7 +138,7 @@ impl InstallCommand {
.download_graph( .download_graph(
&graph, &graph,
&mut refreshed_sources, &mut refreshed_sources,
&reqwest_client(project.data_dir())?, &reqwest,
self.threads as usize, self.threads as usize,
) )
.context("failed to download dependencies")?; .context("failed to download dependencies")?;
@ -174,22 +179,16 @@ impl InstallCommand {
continue; continue;
}; };
if alias == env!("CARGO_BIN_NAME") {
log::warn!("package {alias} has the same name as the CLI, skipping bin link");
continue;
}
let bin_file = bin_folder.join(format!("{alias}.luau")); let bin_file = bin_folder.join(format!("{alias}.luau"));
std::fs::write(&bin_file, bin_link_file(alias)) std::fs::write(&bin_file, bin_link_file(alias))
.context("failed to write bin link file")?; .context("failed to write bin link file")?;
// TODO: test if this actually works make_executable(&bin_file).context("failed to make bin link executable")?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(&bin_file)
.context("failed to get bin link file metadata")?
.permissions();
perms.set_mode(perms.mode() | 0o111);
std::fs::set_permissions(&bin_file, perms)
.context("failed to set bin link file permissions")?;
}
#[cfg(windows)] #[cfg(windows)]
{ {

76
src/cli/commands/mod.rs Normal file
View file

@ -0,0 +1,76 @@
use indicatif::MultiProgress;
use pesde::Project;
mod auth;
mod config;
mod init;
mod install;
#[cfg(feature = "patches")]
mod patch;
#[cfg(feature = "patches")]
mod patch_commit;
mod publish;
mod run;
mod self_install;
mod self_upgrade;
#[derive(Debug, clap::Subcommand)]
pub enum Subcommand {
/// Authentication-related commands
#[command(subcommand)]
Auth(auth::AuthCommands),
/// Configuration-related commands
#[command(subcommand)]
Config(config::ConfigCommands),
/// Initializes a manifest file in the current directory
Init(init::InitCommand),
/// Runs a script, an executable package, or a file with Lune
Run(run::RunCommand),
/// Installs all dependencies for the project
Install(install::InstallCommand),
/// Publishes the project to the registry
Publish(publish::PublishCommand),
/// Installs the pesde binary and scripts
SelfInstall(self_install::SelfInstallCommand),
/// Sets up a patching environment for a package
#[cfg(feature = "patches")]
Patch(patch::PatchCommand),
/// Finalizes a patching environment for a package
#[cfg(feature = "patches")]
PatchCommit(patch_commit::PatchCommitCommand),
/// Installs the latest version of pesde
SelfUpgrade(self_upgrade::SelfUpgradeCommand),
}
impl Subcommand {
pub fn run(
self,
project: Project,
multi: MultiProgress,
reqwest: reqwest::blocking::Client,
) -> anyhow::Result<()> {
match self {
Subcommand::Auth(auth) => auth.run(project, reqwest),
Subcommand::Config(config) => config.run(project),
Subcommand::Init(init) => init.run(project),
Subcommand::Run(run) => run.run(project),
Subcommand::Install(install) => install.run(project, multi, reqwest),
Subcommand::Publish(publish) => publish.run(project),
Subcommand::SelfInstall(self_install) => self_install.run(project),
#[cfg(feature = "patches")]
Subcommand::Patch(patch) => patch.run(project, reqwest),
#[cfg(feature = "patches")]
Subcommand::PatchCommit(patch_commit) => patch_commit.run(project),
Subcommand::SelfUpgrade(self_upgrade) => self_upgrade.run(project, reqwest),
}
}
}

View file

@ -1,4 +1,4 @@
use crate::cli::{reqwest_client, IsUpToDate, VersionedPackageName}; use crate::cli::{IsUpToDate, VersionedPackageName};
use anyhow::Context; use anyhow::Context;
use clap::Args; use clap::Args;
use colored::Colorize; use colored::Colorize;
@ -16,7 +16,7 @@ pub struct PatchCommand {
} }
impl PatchCommand { impl PatchCommand {
pub fn run(self, project: Project) -> anyhow::Result<()> { pub fn run(self, project: Project, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> {
let graph = if project.is_up_to_date(true)? { let graph = if project.is_up_to_date(true)? {
project.deser_lockfile()?.graph project.deser_lockfile()?.graph
} else { } else {
@ -39,12 +39,7 @@ impl PatchCommand {
.join(chrono::Utc::now().timestamp().to_string()); .join(chrono::Utc::now().timestamp().to_string());
std::fs::create_dir_all(&directory)?; std::fs::create_dir_all(&directory)?;
source.download( source.download(&node.node.pkg_ref, &directory, &project, &reqwest)?;
&node.node.pkg_ref,
&directory,
&project,
&reqwest_client(project.data_dir())?,
)?;
// TODO: if MANIFEST_FILE_NAME does not exist, try to convert it // TODO: if MANIFEST_FILE_NAME does not exist, try to convert it

View file

@ -0,0 +1,88 @@
use crate::cli::{files::make_executable, home_dir, scripts::update_scripts_folder, HOME_DIR};
use anyhow::Context;
use clap::Args;
use colored::Colorize;
use pesde::Project;
use std::fs::create_dir_all;
#[derive(Debug, Args)]
pub struct SelfInstallCommand {
#[cfg(windows)]
#[arg(short, long)]
skip_add_to_path: bool,
}
impl SelfInstallCommand {
pub fn run(self, project: Project) -> anyhow::Result<()> {
update_scripts_folder(&project)?;
let bin_dir = home_dir()?.join("bin");
create_dir_all(&bin_dir).context("failed to create bin folder")?;
#[cfg(windows)]
if !self.skip_add_to_path {
use winreg::{enums::HKEY_CURRENT_USER, RegKey};
let current_user = RegKey::predef(HKEY_CURRENT_USER);
let env = current_user
.create_subkey("Environment")
.context("failed to open Environment key")?
.0;
let path: String = env.get_value("Path").context("failed to get Path value")?;
let exists = path
.split(';')
.any(|part| part == bin_dir.to_string_lossy().as_ref());
if !exists {
let new_path = format!("{path};{}", bin_dir.to_string_lossy());
env.set_value("Path", &new_path)
.context("failed to set Path value")?;
}
println!(
"installed {} {}!",
env!("CARGO_PKG_NAME").cyan(),
env!("CARGO_PKG_VERSION").yellow(),
);
if !exists {
println!(
"\nin order to allow binary exports as executables {}.\n\n{}",
format!("`~/{HOME_DIR}/bin` was added to PATH").green(),
"please restart your shell for this to take effect"
.yellow()
.bold()
);
}
}
#[cfg(unix)]
{
println!(
r#"installed {} {}! in order to be able to run binary exports as programs, add the following line to your shell profile:
{}
and then restart your shell.
"#,
env!("CARGO_PKG_NAME").cyan(),
env!("CARGO_PKG_VERSION").yellow(),
format!(r#"export PATH="$PATH:~/{}/bin""#, HOME_DIR)
.bold()
.green()
);
}
let copy_to = bin_dir
.join(env!("CARGO_BIN_NAME"))
.with_extension(std::env::consts::EXE_EXTENSION);
std::fs::copy(std::env::current_exe()?, &copy_to)
.context("failed to copy executable to bin folder")?;
make_executable(&copy_to)?;
Ok(())
}
}

View file

@ -0,0 +1,20 @@
use crate::cli::{config::read_config, version::get_or_download_version};
use clap::Args;
use pesde::Project;
#[derive(Debug, Args)]
pub struct SelfUpgradeCommand {
#[cfg(windows)]
#[arg(short, long)]
skip_add_to_path: bool,
}
impl SelfUpgradeCommand {
pub fn run(self, project: Project, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> {
let config = read_config(project.data_dir())?;
get_or_download_version(&reqwest, &config.last_checked_updates.unwrap().1)?;
Ok(())
}
}

60
src/cli/config.rs Normal file
View file

@ -0,0 +1,60 @@
use anyhow::Context;
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CliConfig {
#[serde(
serialize_with = "crate::util::serialize_gix_url",
deserialize_with = "crate::util::deserialize_gix_url"
)]
pub default_index: gix::Url,
#[serde(
serialize_with = "crate::util::serialize_gix_url",
deserialize_with = "crate::util::deserialize_gix_url"
)]
pub scripts_repo: gix::Url,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub token: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_checked_updates: Option<(chrono::DateTime<chrono::Utc>, semver::Version)>,
}
impl Default for CliConfig {
fn default() -> Self {
Self {
default_index: "https://github.com/daimond113/pesde-index"
.try_into()
.unwrap(),
scripts_repo: "https://github.com/daimond113/pesde-scripts"
.try_into()
.unwrap(),
token: None,
last_checked_updates: None,
}
}
}
pub fn read_config<P: AsRef<Path>>(data_dir: P) -> anyhow::Result<CliConfig> {
let config_string = match std::fs::read_to_string(data_dir.as_ref().join("config.toml")) {
Ok(config_string) => config_string,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return Ok(CliConfig::default());
}
Err(e) => return Err(e).context("failed to read config file"),
};
let config = toml::from_str(&config_string).context("failed to parse config file")?;
Ok(config)
}
pub fn write_config<P: AsRef<Path>>(data_dir: P, config: &CliConfig) -> anyhow::Result<()> {
let config_string = toml::to_string(config).context("failed to serialize config")?;
std::fs::write(data_dir.as_ref().join("config.toml"), config_string)
.context("failed to write config file")?;
Ok(())
}

19
src/cli/files.rs Normal file
View file

@ -0,0 +1,19 @@
use std::path::Path;
pub fn make_executable<P: AsRef<Path>>(_path: P) -> anyhow::Result<()> {
// TODO: test if this actually works
#[cfg(unix)]
{
use anyhow::Context;
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(&_path)
.context("failed to get bin link file metadata")?
.permissions();
perms.set_mode(perms.mode() | 0o111);
std::fs::set_permissions(&_path, perms)
.context("failed to set bin link file permissions")?;
}
Ok(())
}

View file

@ -1,246 +1,21 @@
use crate::util::authenticate_conn; pub mod auth;
pub mod commands;
pub mod config;
pub mod files;
pub mod scripts;
pub mod version;
use crate::cli::auth::get_token;
use anyhow::Context; use anyhow::Context;
use gix::remote::Direction;
use indicatif::MultiProgress;
use keyring::Entry;
use pesde::{lockfile::DownloadedGraph, names::PackageNames, source::VersionId, Project}; use pesde::{lockfile::DownloadedGraph, names::PackageNames, source::VersionId, Project};
use serde::{Deserialize, Serialize}; use std::{collections::HashSet, str::FromStr};
use std::{collections::HashSet, path::Path, str::FromStr};
mod auth; pub const HOME_DIR: &str = concat!(".", env!("CARGO_PKG_NAME"));
mod config;
mod init;
mod install;
#[cfg(feature = "patches")]
mod patch;
#[cfg(feature = "patches")]
mod patch_commit;
mod publish;
mod run;
mod self_install;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CliConfig {
#[serde(
serialize_with = "crate::util::serialize_gix_url",
deserialize_with = "crate::util::deserialize_gix_url"
)]
pub default_index: gix::Url,
#[serde(
serialize_with = "crate::util::serialize_gix_url",
deserialize_with = "crate::util::deserialize_gix_url"
)]
pub scripts_repo: gix::Url,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub token: Option<String>,
}
impl Default for CliConfig {
fn default() -> Self {
Self {
default_index: "https://github.com/daimond113/pesde-index"
.try_into()
.unwrap(),
scripts_repo: "https://github.com/daimond113/pesde-scripts"
.try_into()
.unwrap(),
token: None,
}
}
}
pub fn read_config(data_dir: &Path) -> anyhow::Result<CliConfig> {
let config_string = match std::fs::read_to_string(data_dir.join("config.toml")) {
Ok(config_string) => config_string,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return Ok(CliConfig::default());
}
Err(e) => return Err(e).context("failed to read config file"),
};
let config = toml::from_str(&config_string).context("failed to parse config file")?;
Ok(config)
}
pub fn write_config(data_dir: &Path, config: &CliConfig) -> anyhow::Result<()> {
let config_string = toml::to_string(config).context("failed to serialize config")?;
std::fs::write(data_dir.join("config.toml"), config_string)
.context("failed to write config file")?;
Ok(())
}
pub fn get_token(data_dir: &Path) -> anyhow::Result<Option<String>> {
match std::env::var("PESDE_TOKEN") {
Ok(token) => return Ok(Some(token)),
Err(std::env::VarError::NotPresent) => {}
Err(e) => return Err(e.into()),
}
let config = read_config(data_dir)?;
if let Some(token) = config.token {
return Ok(Some(token));
}
match Entry::new("token", env!("CARGO_PKG_NAME")) {
Ok(entry) => match entry.get_password() {
Ok(token) => return Ok(Some(token)),
Err(keyring::Error::PlatformFailure(_) | keyring::Error::NoEntry) => {}
Err(e) => return Err(e.into()),
},
Err(keyring::Error::PlatformFailure(_)) => {}
Err(e) => return Err(e.into()),
}
Ok(None)
}
pub fn set_token(data_dir: &Path, token: Option<&str>) -> anyhow::Result<()> {
let entry = match Entry::new("token", env!("CARGO_PKG_NAME")) {
Ok(entry) => entry,
Err(e) => return Err(e.into()),
};
let result = if let Some(token) = token {
entry.set_password(token)
} else {
entry.delete_credential()
};
match result {
Ok(()) => return Ok(()),
Err(keyring::Error::PlatformFailure(_) | keyring::Error::NoEntry) => {}
Err(e) => return Err(e.into()),
}
let mut config = read_config(data_dir)?;
config.token = token.map(|s| s.to_string());
write_config(data_dir, &config)?;
Ok(())
}
pub fn reqwest_client(data_dir: &Path) -> anyhow::Result<reqwest::blocking::Client> {
let mut headers = reqwest::header::HeaderMap::new();
if let Some(token) = get_token(data_dir)? {
headers.insert(
reqwest::header::AUTHORIZATION,
format!("Bearer {token}")
.parse()
.context("failed to create auth header")?,
);
}
headers.insert(
reqwest::header::ACCEPT,
"application/json"
.parse()
.context("failed to create accept header")?,
);
Ok(reqwest::blocking::Client::builder()
.user_agent(concat!(
env!("CARGO_PKG_NAME"),
"/",
env!("CARGO_PKG_VERSION")
))
.default_headers(headers)
.build()?)
}
pub fn home_dir() -> anyhow::Result<std::path::PathBuf> { pub fn home_dir() -> anyhow::Result<std::path::PathBuf> {
Ok(directories::UserDirs::new() Ok(dirs::home_dir()
.context("failed to get home directory")? .context("failed to get home directory")?
.home_dir() .join(HOME_DIR))
.join(concat!(".", env!("CARGO_PKG_NAME"))))
}
pub fn update_scripts_folder(project: &Project) -> anyhow::Result<()> {
let scripts_dir = home_dir()?.join("scripts");
if scripts_dir.exists() {
let repo = gix::open(&scripts_dir).context("failed to open scripts repository")?;
let remote = repo
.find_default_remote(Direction::Fetch)
.context("missing default remote of scripts repository")?
.context("failed to find default remote of scripts repository")?;
let mut connection = remote
.connect(Direction::Fetch)
.context("failed to connect to default remote of scripts repository")?;
authenticate_conn(&mut connection, project.auth_config());
let results = connection
.prepare_fetch(gix::progress::Discard, Default::default())
.context("failed to prepare scripts repository fetch")?
.receive(gix::progress::Discard, &false.into())
.context("failed to receive new scripts repository contents")?;
let remote_ref = results
.ref_map
.remote_refs
.first()
.context("failed to get remote refs of scripts repository")?;
let unpacked = remote_ref.unpack();
let oid = unpacked
.1
.or(unpacked.2)
.context("couldn't find oid in remote ref")?;
let tree = repo
.find_object(oid)
.context("failed to find scripts repository tree")?
.peel_to_tree()
.context("failed to peel scripts repository object to tree")?;
let mut index = gix::index::File::from_state(
gix::index::State::from_tree(&tree.id, &repo.objects, Default::default())
.context("failed to create index state from scripts repository tree")?,
repo.index_path(),
);
let opts = gix::worktree::state::checkout::Options {
overwrite_existing: true,
destination_is_initially_empty: false,
..Default::default()
};
gix::worktree::state::checkout(
&mut index,
repo.work_dir().context("scripts repo is bare")?,
repo.objects
.clone()
.into_arc()
.context("failed to clone objects")?,
&gix::progress::Discard,
&gix::progress::Discard,
&false.into(),
opts,
)
.context("failed to checkout scripts repository")?;
index
.write(gix::index::write::Options::default())
.context("failed to write index")?;
} else {
std::fs::create_dir_all(&scripts_dir).context("failed to create scripts directory")?;
let cli_config = read_config(project.data_dir())?;
gix::prepare_clone(cli_config.scripts_repo, &scripts_dir)
.context("failed to prepare scripts repository clone")?
.fetch_then_checkout(gix::progress::Discard, &false.into())
.context("failed to fetch and checkout scripts repository")?
.0
.main_worktree(gix::progress::Discard, &false.into())
.context("failed to set scripts repository as main worktree")?;
};
Ok(())
} }
pub trait IsUpToDate { pub trait IsUpToDate {
@ -346,55 +121,3 @@ impl VersionedPackageName {
pub fn parse_gix_url(s: &str) -> Result<gix::Url, gix::url::parse::Error> { pub fn parse_gix_url(s: &str) -> Result<gix::Url, gix::url::parse::Error> {
s.try_into() s.try_into()
} }
#[derive(Debug, clap::Subcommand)]
pub enum Subcommand {
/// Authentication-related commands
#[command(subcommand)]
Auth(auth::AuthCommands),
/// Configuration-related commands
#[command(subcommand)]
Config(config::ConfigCommands),
/// Initializes a manifest file in the current directory
Init(init::InitCommand),
/// Runs a script, an executable package, or a file with Lune
Run(run::RunCommand),
/// Installs all dependencies for the project
Install(install::InstallCommand),
/// Publishes the project to the registry
Publish(publish::PublishCommand),
/// Installs the pesde binary and scripts
SelfInstall(self_install::SelfInstallCommand),
/// Sets up a patching environment for a package
#[cfg(feature = "patches")]
Patch(patch::PatchCommand),
/// Finalizes a patching environment for a package
#[cfg(feature = "patches")]
PatchCommit(patch_commit::PatchCommitCommand),
}
impl Subcommand {
pub fn run(self, project: Project, multi: MultiProgress) -> anyhow::Result<()> {
match self {
Subcommand::Auth(auth) => auth.run(project),
Subcommand::Config(config) => config.run(project),
Subcommand::Init(init) => init.run(project),
Subcommand::Run(run) => run.run(project),
Subcommand::Install(install) => install.run(project, multi),
Subcommand::Publish(publish) => publish.run(project),
Subcommand::SelfInstall(self_install) => self_install.run(project),
#[cfg(feature = "patches")]
Subcommand::Patch(patch) => patch.run(project),
#[cfg(feature = "patches")]
Subcommand::PatchCommit(patch_commit) => patch_commit.run(project),
}
}
}

94
src/cli/scripts.rs Normal file
View file

@ -0,0 +1,94 @@
use crate::{
cli::{config::read_config, home_dir},
util::authenticate_conn,
};
use anyhow::Context;
use gix::remote::Direction;
use pesde::Project;
pub fn update_scripts_folder(project: &Project) -> anyhow::Result<()> {
let scripts_dir = home_dir()?.join("scripts");
if scripts_dir.exists() {
let repo = gix::open(&scripts_dir).context("failed to open scripts repository")?;
let remote = repo
.find_default_remote(Direction::Fetch)
.context("missing default remote of scripts repository")?
.context("failed to find default remote of scripts repository")?;
let mut connection = remote
.connect(Direction::Fetch)
.context("failed to connect to default remote of scripts repository")?;
authenticate_conn(&mut connection, project.auth_config());
let results = connection
.prepare_fetch(gix::progress::Discard, Default::default())
.context("failed to prepare scripts repository fetch")?
.receive(gix::progress::Discard, &false.into())
.context("failed to receive new scripts repository contents")?;
let remote_ref = results
.ref_map
.remote_refs
.first()
.context("failed to get remote refs of scripts repository")?;
let unpacked = remote_ref.unpack();
let oid = unpacked
.1
.or(unpacked.2)
.context("couldn't find oid in remote ref")?;
let tree = repo
.find_object(oid)
.context("failed to find scripts repository tree")?
.peel_to_tree()
.context("failed to peel scripts repository object to tree")?;
let mut index = gix::index::File::from_state(
gix::index::State::from_tree(&tree.id, &repo.objects, Default::default())
.context("failed to create index state from scripts repository tree")?,
repo.index_path(),
);
let opts = gix::worktree::state::checkout::Options {
overwrite_existing: true,
destination_is_initially_empty: false,
..Default::default()
};
gix::worktree::state::checkout(
&mut index,
repo.work_dir().context("scripts repo is bare")?,
repo.objects
.clone()
.into_arc()
.context("failed to clone objects")?,
&gix::progress::Discard,
&gix::progress::Discard,
&false.into(),
opts,
)
.context("failed to checkout scripts repository")?;
index
.write(gix::index::write::Options::default())
.context("failed to write index")?;
} else {
std::fs::create_dir_all(&scripts_dir).context("failed to create scripts directory")?;
let cli_config = read_config(project.data_dir())?;
gix::prepare_clone(cli_config.scripts_repo, &scripts_dir)
.context("failed to prepare scripts repository clone")?
.fetch_then_checkout(gix::progress::Discard, &false.into())
.context("failed to fetch and checkout scripts repository")?
.0
.main_worktree(gix::progress::Discard, &false.into())
.context("failed to set scripts repository as main worktree")?;
};
Ok(())
}

View file

@ -1,20 +0,0 @@
use crate::cli::{home_dir, update_scripts_folder};
use anyhow::Context;
use clap::Args;
use pesde::Project;
use std::fs::create_dir_all;
#[derive(Debug, Args)]
pub struct SelfInstallCommand {}
impl SelfInstallCommand {
pub fn run(self, project: Project) -> anyhow::Result<()> {
update_scripts_folder(&project)?;
create_dir_all(home_dir()?.join("bin")).context("failed to create bin folder")?;
// TODO: add the bin folder to the PATH
Ok(())
}
}

218
src/cli/version.rs Normal file
View file

@ -0,0 +1,218 @@
use std::{
fs::create_dir_all,
io::Read,
path::{Path, PathBuf},
};
use anyhow::Context;
use colored::Colorize;
use reqwest::header::ACCEPT;
use semver::Version;
use serde::Deserialize;
use crate::cli::{
config::{read_config, write_config, CliConfig},
files::make_executable,
home_dir,
};
pub fn current_version() -> Version {
Version::parse(env!("CARGO_PKG_VERSION")).unwrap()
}
#[derive(Debug, Deserialize)]
struct Release {
tag_name: String,
assets: Vec<Asset>,
}
#[derive(Debug, Deserialize)]
struct Asset {
name: String,
url: url::Url,
}
fn get_repo() -> (String, String) {
let mut parts = env!("CARGO_PKG_REPOSITORY").split('/').skip(3);
(
parts.next().unwrap().to_string(),
parts.next().unwrap().to_string(),
)
}
const CHECK_INTERVAL: chrono::Duration = chrono::Duration::seconds(30);
pub fn check_for_updates<P: AsRef<Path>>(
reqwest: &reqwest::blocking::Client,
data_dir: P,
) -> anyhow::Result<()> {
let (owner, repo) = get_repo();
let config = read_config(&data_dir)?;
let version = if let Some((_, version)) = config
.last_checked_updates
.filter(|(time, _)| chrono::Utc::now() - *time < CHECK_INTERVAL)
{
version
} else {
let releases = reqwest
.get(format!(
"https://api.github.com/repos/{owner}/{repo}/releases",
))
.send()
.context("failed to send request to GitHub API")?
.json::<Vec<Release>>()
.context("failed to parse GitHub API response")?;
let version = releases
.into_iter()
.map(|release| Version::parse(release.tag_name.trim_start_matches('v')).unwrap())
.max()
.context("failed to find latest version")?;
write_config(
&data_dir,
&CliConfig {
last_checked_updates: Some((chrono::Utc::now(), version.clone())),
..config
},
)?;
version
};
if version > current_version() {
let name = env!("CARGO_PKG_NAME");
let unformatted_message = format!("a new version of {name} is available: {version}");
let message = format!(
"a new version of {} is available: {}",
name.cyan(),
version.to_string().yellow().bold()
);
let stars = "-"
.repeat(unformatted_message.len() + 4)
.bright_magenta()
.bold();
let column = "|".bright_magenta().bold();
println!("\n{stars}\n{column} {message} {column}\n{stars}\n",);
}
Ok(())
}
pub fn download_github_release(
reqwest: &reqwest::blocking::Client,
version: &Version,
) -> anyhow::Result<Vec<u8>> {
let (owner, repo) = get_repo();
let release = reqwest
.get(format!(
"https://api.github.com/repos/{owner}/{repo}/releases/tags/v{version}",
))
.send()
.context("failed to send request to GitHub API")?
.json::<Release>()
.context("failed to parse GitHub API response")?;
let asset = release
.assets
.into_iter()
.find(|asset| {
asset.name.ends_with(&format!(
"-{}-{}.tar.gz",
std::env::consts::OS,
std::env::consts::ARCH
))
})
.context("failed to find asset for current platform")?;
let bytes = reqwest
.get(asset.url)
.header(ACCEPT, "application/octet-stream")
.send()
.context("failed to send request to download asset")?
.bytes()
.context("failed to download asset")?;
let mut decoder = flate2::read::GzDecoder::new(bytes.as_ref());
let mut archive = tar::Archive::new(&mut decoder);
let entry = archive
.entries()
.context("failed to read archive entries")?
.next()
.context("archive has no entry")?
.context("failed to get first archive entry")?;
entry
.bytes()
.collect::<Result<Vec<u8>, std::io::Error>>()
.context("failed to read archive entry bytes")
}
pub fn get_or_download_version(
reqwest: &reqwest::blocking::Client,
version: &Version,
) -> anyhow::Result<Option<PathBuf>> {
#[cfg(debug_assertions)]
// possible hard to debug issues with the versioning system overtaking the debug build
return Ok(None);
let path = home_dir()?.join("versions");
create_dir_all(&path).context("failed to create versions directory")?;
let path = path
.join(version.to_string())
.with_extension(std::env::consts::EXE_EXTENSION);
let is_requested_version = *version == current_version();
if path.exists() {
return Ok(if is_requested_version {
None
} else {
Some(path)
});
}
if is_requested_version {
std::fs::copy(std::env::current_exe()?, &path)
.context("failed to copy current executable to version directory")?;
} else {
let bytes = download_github_release(reqwest, version)?;
std::fs::write(&path, bytes).context("failed to write downloaded version file")?;
}
make_executable(&path).context("failed to make downloaded version executable")?;
Ok(if is_requested_version {
None
} else {
Some(path)
})
}
pub fn max_installed_version() -> anyhow::Result<Version> {
#[cfg(debug_assertions)]
return Ok(current_version());
let versions_dir = home_dir()?.join("versions");
create_dir_all(&versions_dir).context("failed to create versions directory")?;
let max_version = std::fs::read_dir(versions_dir)
.context("failed to read versions directory")?
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.map(|entry| Version::parse(entry.path().file_stem().unwrap().to_str().unwrap()).unwrap())
.max()
.filter(|v| v >= &current_version())
.unwrap_or_else(current_version);
Ok(max_version)
}

View file

@ -1,4 +1,9 @@
use crate::cli::get_token; use crate::cli::{
auth::get_token,
home_dir,
version::{check_for_updates, current_version, get_or_download_version, max_installed_version},
};
use anyhow::Context;
use clap::Parser; use clap::Parser;
use colored::Colorize; use colored::Colorize;
use indicatif::MultiProgress; use indicatif::MultiProgress;
@ -18,29 +23,38 @@ struct Cli {
version: (), version: (),
#[command(subcommand)] #[command(subcommand)]
subcommand: cli::Subcommand, subcommand: cli::commands::Subcommand,
} }
fn main() { fn run() -> anyhow::Result<()> {
#[cfg(windows)] #[cfg(windows)]
{ 'scripts: {
let exe = std::env::current_exe().expect("failed to get current executable path"); let exe = std::env::current_exe().expect("failed to get current executable path");
if exe.parent().is_some_and(|parent| {
parent.as_os_str() != "bin"
|| parent
.parent()
.is_some_and(|parent| parent.as_os_str() != cli::HOME_DIR)
}) {
break 'scripts;
}
let exe_name = exe.with_extension(""); let exe_name = exe.with_extension("");
let exe_name = exe_name.file_name().unwrap(); let exe_name = exe_name.file_name().unwrap();
if exe_name != env!("CARGO_BIN_NAME") { if exe_name == env!("CARGO_BIN_NAME") {
let args = std::env::args_os(); break 'scripts;
let status = std::process::Command::new("lune")
.arg("run")
.arg(exe.with_extension("luau"))
.args(args.skip(1))
.current_dir(std::env::current_dir().unwrap())
.status()
.expect("failed to run lune");
std::process::exit(status.code().unwrap());
} }
let status = std::process::Command::new("lune")
.arg("run")
.arg(exe.with_extension("luau"))
.args(std::env::args_os().skip(1))
.current_dir(std::env::current_dir().unwrap())
.status()
.expect("failed to run lune");
std::process::exit(status.code().unwrap());
} }
let multi = { let multi = {
@ -54,21 +68,82 @@ fn main() {
multi multi
}; };
let project_dirs =
directories::ProjectDirs::from("com", env!("CARGO_PKG_NAME"), env!("CARGO_BIN_NAME"))
.expect("couldn't get home directory");
let cwd = std::env::current_dir().expect("failed to get current working directory"); let cwd = std::env::current_dir().expect("failed to get current working directory");
let cli = Cli::parse();
let data_dir = project_dirs.data_dir(); let data_dir = home_dir()?.join("data");
create_dir_all(data_dir).expect("failed to create data directory"); create_dir_all(&data_dir).expect("failed to create data directory");
if let Err(err) = get_token(data_dir).and_then(|token| { let token = get_token(&data_dir)?;
cli.subcommand.run(
Project::new(cwd, data_dir, AuthConfig::new().with_pesde_token(token)), let project = Project::new(
multi, cwd,
) &data_dir,
}) { AuthConfig::new().with_pesde_token(token.as_ref()),
);
let reqwest = {
let mut headers = reqwest::header::HeaderMap::new();
if let Some(token) = token {
headers.insert(
reqwest::header::AUTHORIZATION,
format!("Bearer {token}")
.parse()
.context("failed to create auth header")?,
);
}
headers.insert(
reqwest::header::ACCEPT,
"application/json"
.parse()
.context("failed to create accept header")?,
);
reqwest::blocking::Client::builder()
.user_agent(concat!(
env!("CARGO_PKG_NAME"),
"/",
env!("CARGO_PKG_VERSION")
))
.default_headers(headers)
.build()?
};
check_for_updates(&reqwest, &data_dir)?;
let target_version = project
.deser_manifest()
.ok()
.and_then(|manifest| manifest.pesde_version);
// store the current version in case it needs to be used later
get_or_download_version(&reqwest, &current_version())?;
let exe_path = if let Some(version) = target_version {
Some(get_or_download_version(&reqwest, &version)?)
} else {
None
};
let exe_path = if let Some(exe_path) = exe_path {
exe_path
} else {
get_or_download_version(&reqwest, &max_installed_version()?)?
};
if let Some(exe_path) = exe_path {
let status = std::process::Command::new(exe_path)
.args(std::env::args_os().skip(1))
.status()
.expect("failed to run new version");
std::process::exit(status.code().unwrap());
}
Cli::parse().subcommand.run(project, multi, reqwest)
}
fn main() {
if let Err(err) = run() {
eprintln!("{}: {err}\n", "error".red().bold()); eprintln!("{}: {err}\n", "error".red().bold());
let cause = err.chain().skip(1).collect::<Vec<_>>(); let cause = err.chain().skip(1).collect::<Vec<_>>();

View file

@ -51,6 +51,8 @@ pub struct Manifest {
#[cfg(feature = "patches")] #[cfg(feature = "patches")]
#[serde(default, skip_serializing)] #[serde(default, skip_serializing)]
pub patches: BTreeMap<PackageNames, BTreeMap<VersionId, RelativePathBuf>>, pub patches: BTreeMap<PackageNames, BTreeMap<VersionId, RelativePathBuf>>,
#[serde(default, skip_serializing)]
pub pesde_version: Option<Version>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub dependencies: BTreeMap<String, DependencySpecifiers>, pub dependencies: BTreeMap<String, DependencySpecifiers>,