diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e925d93..40f03f9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-13] + os: [ubuntu-latest, windows-latest, macos-26] steps: - name: Checkout uses: actions/checkout@v2 @@ -27,8 +27,8 @@ jobs: - name: Setup go uses: actions/setup-go@v2 with: - go-version: 1.18.0-beta1 - stable: false + go-version: 1.25.6 + stable: true - name: Install and run dependencies (xvfb libx11-dev) @@ -38,11 +38,11 @@ jobs: sudo apt install -y xvfb libx11-dev x11-utils libegl1-mesa-dev libgles2-mesa-dev - name: Install dependencies on macOS - if: matrix.os == 'macos-13' + if: matrix.os == 'macos-26' run: | - brew install filosottile/musl-cross/musl-cross + # brew install filosottile/musl-cross/musl-cross brew install libx11 - ln -s /opt/X11/include/X11 /usr/local/include/X11 + # ln -s /opt/homebrew/include/X11 /usr/local/include/X11 - name: Go Test run: go test @@ -60,7 +60,7 @@ jobs: run: make windows - name: Build on macOS - if: matrix.os == 'macos-13' + if: matrix.os == 'macos-26' run: make macos - name: Upload build artifacts @@ -71,7 +71,7 @@ jobs: tag_and_release: needs: build - runs-on: macos-13 + runs-on: macos-26 steps: - name: Checkout uses: actions/checkout@v2 @@ -79,21 +79,21 @@ jobs: fetch-depth: 0 - name: Download build artifacts from Linux - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v7 with: name: build-ubuntu-latest path: dist/linux - name: Download build artifacts from Windows - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v7 with: name: build-windows-latest path: dist/windows - name: Download build artifacts from macOS - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v7 with: - name: build-macos-13 + name: build-macos-26 path: dist/macos - name: Create Tag @@ -116,7 +116,7 @@ jobs: EOF - name: Release & Assets - uses: Hs1r1us/Release-AIO@v1.0 + uses: Hs1r1us/Release-AIO@v2.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/README.md b/README.md index 32d16b1..8e764f6 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,10 @@ For Windows user: | -- `{fname_hash}` | -- MD5 Hash in hex of `{fname}` | | -- `{fname_hash4}` | -- MD5 Hash in hex of `{fname}`, first 4 digits | | -- `{fname_hash8}` | -- MD5 Hash in hex of `{fname}`, first 8 digits | +| -- `{hmac}` | -- HMAC-SHA256 hash of `hmac_format`, truncated to `hmac_len` | +| hmac_key | Secret key for calculation `{hmac}` | +| hmac_format | Format string for `{hmac}` calculation. Supporting all above placeholders. | +| hmac_len | Length of `{hmac}` hash. 0 means no truncation. | Here is a simplist sample config file: diff --git a/config.sample.toml b/config.sample.toml index 5a35c0f..2ad07e9 100644 --- a/config.sample.toml +++ b/config.sample.toml @@ -15,7 +15,12 @@ default_uploader = "github" # {fname_hash} MD5 hash value of {fname} # {ext} File name suffix, for example: .png # The following example generates a file name like: 2022/01/upgit_20220131_1643617626.png -rename = "{year}/{month}/upgit_{year}{month}{day}_{unix_ts}{ext}" +rename = "{year}/{month}/upgit_{year}{month}{day}_{unix_ts}_{hmac}{ext}" + +hmac_key = "74d11935-b2ad-5a3f-8184-5ecdf4f4906b" +hmac_format = "{year}_{month}_{day}_{unix_ts}{ext}" +hmac_len = 31 + # ----------------------------------------------------------------------------- # Custom extra output formats diff --git a/config.sample.zh-CN.toml b/config.sample.zh-CN.toml index c30dc9b..056b1db 100644 --- a/config.sample.zh-CN.toml +++ b/config.sample.zh-CN.toml @@ -16,8 +16,11 @@ default_uploader = "github" # {ext} 文件后缀名, 例如.png # 下面的例子生成的文件名预览: 2022/01/upgit_20220131_1643617626.png # 如果目录不存在将会被程序自动创建 -rename = "{year}/{month}/upgit_{year}{month}{day}_{unix_ts}{ext}" +rename = "{year}/{month}/upgit_{year}{month}{day}_{unix_ts}_{hmac}{ext}" +hmac_key = "74d11935-b2ad-5a3f-8184-5ecdf4f4906b" +hmac_format = "{year}_{month}_{day}_{unix_ts}{ext}" +hmac_len = 31 # ----------------------------------------------------------------------------- # 自定义输出格式 diff --git a/go.mod b/go.mod index 924cab6..51aead6 100644 --- a/go.mod +++ b/go.mod @@ -1,26 +1,27 @@ module github.com/pluveto/upgit -go 1.18 +go 1.24.0 require ( - github.com/alexflint/go-arg v1.4.3 + github.com/alexflint/go-arg v1.6.1 github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible - github.com/aws/aws-sdk-go v1.54.6 - github.com/fatih/color v1.13.0 - github.com/mitchellh/mapstructure v1.4.3 - github.com/pelletier/go-toml/v2 v2.0.6 - golang.design/x/clipboard v0.6.0 - golang.org/x/image v0.0.0-20220302094943-723b81ca9867 - gopkg.in/validator.v2 v2.0.0-20210331031555-b37d688a7fb0 + github.com/aws/aws-sdk-go v1.55.8 + github.com/fatih/color v1.18.0 + github.com/mitchellh/mapstructure v1.5.0 + github.com/pelletier/go-toml/v2 v2.2.4 + golang.design/x/clipboard v0.7.1 + golang.org/x/image v0.35.0 + gopkg.in/validator.v2 v2.0.1 ) require ( - github.com/alexflint/go-scalar v1.1.0 // indirect + github.com/alexflint/go-scalar v1.2.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect - golang.org/x/mobile v0.0.0-20220224134551-8a0a1e50732f // indirect - golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect - golang.org/x/time v0.11.0 // indirect + golang.org/x/exp/shiny v0.0.0-20260112195511-716be5621a96 // indirect + golang.org/x/mobile v0.0.0-20260112195712-5b9ecdfb8721 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/time v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index 2b70da3..8055f13 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,25 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/alexflint/go-arg v1.4.3 h1:9rwwEBpMXfKQKceuZfYcwuc/7YY7tWJbFsgG5cAU/uo= github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA= +github.com/alexflint/go-arg v1.6.1 h1:uZogJ6VDBjcuosydKgvYYRhh9sRCusjOvoOLZopBlnA= +github.com/alexflint/go-arg v1.6.1/go.mod h1:nQ0LFYftLJ6njcaee0sU+G0iS2+2XJQfA8I062D0LGc= github.com/alexflint/go-scalar v1.1.0 h1:aaAouLLzI9TChcPXotr6gUhq+Scr8rl0P9P4PnltbhM= github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o= +github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw= +github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o= github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/aws/aws-sdk-go v1.54.6 h1:HEYUib3yTt8E6vxjMWM3yAq5b+qjj/6aKA62mkgux9g= github.com/aws/aws-sdk-go v1.54.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= +github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -19,19 +27,28 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -41,20 +58,28 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.design/x/clipboard v0.6.0 h1:+U/e2KDBdpIjkRdxO8GwlD6dKD3Jx5zlNNzQjxte4A0= golang.design/x/clipboard v0.6.0/go.mod h1:ep0pB+/4DGJK3ayLxweWJFHhHGGv3npJJHMXAjtLTUM= +golang.design/x/clipboard v0.7.1 h1:OEG3CmcYRBNnRwpDp7+uWLiZi3hrMRJpE9JkkkYtz2c= +golang.design/x/clipboard v0.7.1/go.mod h1:i5SiIqj0wLFw9P/1D7vfILFK0KHMk7ydE72HRrUIgkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= +golang.org/x/exp/shiny v0.0.0-20260112195511-716be5621a96 h1:wJ3cDLvYRAWzRt6f3e2VwVlziH3httfx2PGMa8hqqWo= +golang.org/x/exp/shiny v0.0.0-20260112195511-716be5621a96/go.mod h1:hq/Ge0xSczE7aHicXVhn3Kd0j3hOtWQR4KEgAwemgdk= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20220302094943-723b81ca9867 h1:TcHcE0vrmgzNH1v3ppjcMGbhG5+9fMuvOmUYwNEF4q4= golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I= +golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E= golang.org/x/mobile v0.0.0-20220224134551-8a0a1e50732f h1:G/wQ/Mbs60nXhRM80J4DOzy7FEIZjNprzOneArSgOl0= golang.org/x/mobile v0.0.0-20220224134551-8a0a1e50732f/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= +golang.org/x/mobile v0.0.0-20260112195712-5b9ecdfb8721 h1:nzIKPiUdGHmOrf0Zj4ww4DB65fsHcSL4+oibimRnI5I= +golang.org/x/mobile v0.0.0-20260112195712-5b9ecdfb8721/go.mod h1:yHJY0EGzMJ0i5ONrrhdpDSSnoyres5LO7D2hSIbJJ5I= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -77,12 +102,17 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -95,6 +125,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/validator.v2 v2.0.0-20210331031555-b37d688a7fb0 h1:EFLtLCwd8tGN+r/ePz3cvRtdsfYNhDEdt/vp6qsT+0A= gopkg.in/validator.v2 v2.0.0-20210331031555-b37d688a7fb0/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc= +gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY= +gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lib/xapp/cfg.go b/lib/xapp/cfg.go index f080088..b2bf458 100644 --- a/lib/xapp/cfg.go +++ b/lib/xapp/cfg.go @@ -5,6 +5,9 @@ type Config struct { Rename string `toml:"rename,omitempty"` Replacements map[string]string `toml:"replacements,omitempty"` OutputFormats map[string]string `toml:"output_formats,omitempty"` + HmacKey string `toml:"hmac_key,omitempty"` + HmacFormat string `toml:"hmac_format,omitempty"` + HmacLen int `toml:"hmac_len,omitempty"` } var AppCfg Config diff --git a/lib/xapp/rename_test.go b/lib/xapp/rename_test.go new file mode 100644 index 0000000..00f90d6 --- /dev/null +++ b/lib/xapp/rename_test.go @@ -0,0 +1,60 @@ +package xapp + +import ( + "testing" + "time" +) + +func TestRename(t *testing.T) { + // Fixed time for reproducible tests + fixedTime := time.Date(2023, 10, 27, 10, 30, 0, 0, time.UTC) + + tests := []struct { + name string + cfg Config + path string + wantFormat string // simpler check than exact string due to time components if not careful, but we mocked time + }{ + { + name: "Normal Rename", + cfg: Config{ + Rename: "{year}/{fname}{ext}", + }, + path: "test/file.png", + wantFormat: "2023/file.png", + }, + { + name: "HMAC Rename Full", + cfg: Config{ + Rename: "{hmac}/{fname}{ext}", + HmacKey: "secret_key", + HmacFormat: "{fname}{ext}", + }, + path: "test/image.jpg", + // hmac_sha256("image.jpg", "secret_key") = 9614d443a1f6bd260dc20455b008e3e7a35a4e8bd11951082821dffd27ccb070 + wantFormat: "9614d443a1f6bd260dc20455b008e3e7a35a4e8bd11951082821dffd27ccb070/image.jpg", + }, + { + name: "HMAC Rename Truncated", + cfg: Config{ + Rename: "{hmac}/{fname}{ext}", + HmacKey: "secret_key", + HmacFormat: "{fname}{ext}", + HmacLen: 8, + }, + path: "test/image.jpg", + // first 8 chars of hash + wantFormat: "9614d443/image.jpg", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + AppCfg = tt.cfg + got := Rename(tt.path, fixedTime) + if got != tt.wantFormat { + t.Errorf("Rename() = %v, want %v", got, tt.wantFormat) + } + }) + } +} diff --git a/lib/xapp/shared.go b/lib/xapp/shared.go index 45297d8..715b3b7 100644 --- a/lib/xapp/shared.go +++ b/lib/xapp/shared.go @@ -1,7 +1,10 @@ package xapp import ( + "crypto/hmac" "crypto/md5" + "crypto/sha256" + "encoding/hex" "fmt" "io/ioutil" "path/filepath" @@ -28,7 +31,7 @@ func Rename(path string, time time.Time) (ret string) { base := xpath.Basename(path) ext := filepath.Ext(path) md5HashStr := fmt.Sprintf("%x", md5.Sum([]byte(base))) - r := strings.NewReplacer( + replacementList := []string{ "{year}", time.Format("2006"), "{month}", time.Format("01"), "{day}", time.Format("02"), @@ -38,14 +41,28 @@ func Rename(path string, time time.Time) (ret string) { "{unixts}", fmt.Sprint(time.Unix()), "{unixtsms}", fmt.Sprint(time.UnixMicro()), "{ext}", ext, - "{fullname}", base+ext, + "{fullname}", base + ext, "{filename}", base, "{fname}", base, "{filenamehash}", md5HashStr, "{fnamehash}", md5HashStr, "{fnamehash4}", md5HashStr[:4], "{fnamehash8}", md5HashStr[:8], - ) + } + + if len(AppCfg.HmacKey) > 0 && len(AppCfg.HmacFormat) > 0 && strings.Contains(AppCfg.Rename, "{hmac}") { + r := strings.NewReplacer(replacementList...) + param := r.Replace(AppCfg.HmacFormat) + h := hmac.New(sha256.New, []byte(AppCfg.HmacKey)) + h.Write([]byte(param)) + hmacHashStr := hex.EncodeToString(h.Sum(nil)) + if AppCfg.HmacLen > 0 && AppCfg.HmacLen < len(hmacHashStr) { + hmacHashStr = hmacHashStr[:AppCfg.HmacLen] + } + replacementList = append(replacementList, "{hmac}", hmacHashStr) + } + + r := strings.NewReplacer(replacementList...) ret = r.Replace(AppCfg.Rename) return } diff --git a/main.go b/main.go index 0c2dd7a..7a9bdc0 100644 --- a/main.go +++ b/main.go @@ -167,7 +167,7 @@ func outputFormat(r model.Task) (content string, err error) { func validArgs() { if errs := validator.Validate(xapp.AppCfg); errs != nil { - xlog.AbortErr(fmt.Errorf("incorrect config: " + errs.Error())) + xlog.AbortErr(fmt.Errorf("incorrect config: %s", errs.Error())) } for _, path := range xapp.AppOpt.LocalPaths { @@ -223,7 +223,7 @@ func loadConfig(cfg *xapp.Config) { err = toml.Unmarshal(optRawBytes, &cfg) } if err != nil { - xlog.AbortErr(fmt.Errorf("invalid config: " + err.Error())) + xlog.AbortErr(fmt.Errorf("invalid config: %s", err.Error())) } xapp.ConfigFilePath = configFile break @@ -396,7 +396,7 @@ func handleClipboard() { if label == xapp.ClipboardPlaceholder { err := clipboard.Init() if err != nil { - xlog.AbortErr(fmt.Errorf("failed to init clipboard: " + err.Error())) + xlog.AbortErr(fmt.Errorf("failed to init clipboard: %s", err.Error())) } tmpFileName := fmt.Sprint(os.TempDir(), "/upgit_tmp_", time.Now().UnixMicro(), ".png")