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

234
Cargo.lock generated
View file

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

View file

@ -1,4 +1,4 @@
use crate::cli::read_config;
use crate::cli::config::read_config;
use anyhow::Context;
use clap::Args;
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 clap::Args;
use indicatif::MultiProgress;
@ -55,7 +55,12 @@ end
}
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 manifest = project
@ -133,7 +138,7 @@ impl InstallCommand {
.download_graph(
&graph,
&mut refreshed_sources,
&reqwest_client(project.data_dir())?,
&reqwest,
self.threads as usize,
)
.context("failed to download dependencies")?;
@ -174,22 +179,16 @@ impl InstallCommand {
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"));
std::fs::write(&bin_file, bin_link_file(alias))
.context("failed to write bin link file")?;
// TODO: test if this actually works
#[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")?;
}
make_executable(&bin_file).context("failed to make bin link executable")?;
#[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 clap::Args;
use colored::Colorize;
@ -16,7 +16,7 @@ pub struct 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)? {
project.deser_lockfile()?.graph
} else {
@ -39,12 +39,7 @@ impl PatchCommand {
.join(chrono::Utc::now().timestamp().to_string());
std::fs::create_dir_all(&directory)?;
source.download(
&node.node.pkg_ref,
&directory,
&project,
&reqwest_client(project.data_dir())?,
)?;
source.download(&node.node.pkg_ref, &directory, &project, &reqwest)?;
// 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 gix::remote::Direction;
use indicatif::MultiProgress;
use keyring::Entry;
use pesde::{lockfile::DownloadedGraph, names::PackageNames, source::VersionId, Project};
use serde::{Deserialize, Serialize};
use std::{collections::HashSet, path::Path, str::FromStr};
use std::{collections::HashSet, str::FromStr};
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;
#[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 const HOME_DIR: &str = concat!(".", env!("CARGO_PKG_NAME"));
pub fn home_dir() -> anyhow::Result<std::path::PathBuf> {
Ok(directories::UserDirs::new()
Ok(dirs::home_dir()
.context("failed to get home directory")?
.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(())
.join(HOME_DIR))
}
pub trait IsUpToDate {
@ -346,55 +121,3 @@ impl VersionedPackageName {
pub fn parse_gix_url(s: &str) -> Result<gix::Url, gix::url::parse::Error> {
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 colored::Colorize;
use indicatif::MultiProgress;
@ -18,30 +23,39 @@ struct Cli {
version: (),
#[command(subcommand)]
subcommand: cli::Subcommand,
subcommand: cli::commands::Subcommand,
}
fn main() {
fn run() -> anyhow::Result<()> {
#[cfg(windows)]
{
'scripts: {
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_name.file_name().unwrap();
if exe_name != env!("CARGO_BIN_NAME") {
let args = std::env::args_os();
if exe_name == env!("CARGO_BIN_NAME") {
break 'scripts;
}
let status = std::process::Command::new("lune")
.arg("run")
.arg(exe.with_extension("luau"))
.args(args.skip(1))
.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 logger = pretty_env_logger::formatted_builder()
@ -54,21 +68,82 @@ fn main() {
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 cli = Cli::parse();
let data_dir = project_dirs.data_dir();
create_dir_all(data_dir).expect("failed to create data directory");
let data_dir = home_dir()?.join("data");
create_dir_all(&data_dir).expect("failed to create data directory");
if let Err(err) = get_token(data_dir).and_then(|token| {
cli.subcommand.run(
Project::new(cwd, data_dir, AuthConfig::new().with_pesde_token(token)),
multi,
)
}) {
let token = get_token(&data_dir)?;
let project = Project::new(
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());
let cause = err.chain().skip(1).collect::<Vec<_>>();

View file

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