Compare commits

...

51 Commits
0.2.1 ... main

Author SHA1 Message Date
Cadey Ratio e94e032942 argh
continuous-integration/drone/push Build is passing Details
2020-10-25 16:16:56 -04:00
Cadey Ratio 5c48459414 use rust from reg.tulpa.dev
continuous-integration/drone/push Build is failing Details
2020-10-25 16:01:34 -04:00
Cadey Ratio 26999f5f2c minor parsing bug
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/tag Build is passing Details
2020-10-25 15:37:16 -04:00
Cadey Ratio 1a0bd1fcc8 Merge pull request 'Read versions from Cargo.toml ref #3' (#11) from cargo-version into main
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/tag Build was killed Details
Reviewed-on: #11
2020-10-25 19:21:30 +00:00
Cadey Ratio 9ad6a78579 version bump
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2020-10-25 15:06:11 -04:00
Cadey Ratio a49864c419 remove VERSION file 2020-10-25 15:05:07 -04:00
Cadey Ratio 7317c2425e a few fixes for readablilty 2020-10-25 15:04:04 -04:00
Cadey Ratio 26894c7fad add support for reading version data from cargo 2020-10-25 14:53:24 -04:00
Cadey Ratio 8b55a1cfcf oops
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/tag Build is passing Details
2020-09-26 19:14:42 -04:00
Cadey Ratio 613dd68f04 fix vtag logic
continuous-integration/drone/push Build is failing Details
2020-09-26 19:07:04 -04:00
Cadey Ratio 8c4017a3b3 version 0.5.2
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-08-08 16:16:43 -04:00
Cadey Ratio e1db8c4df4 use pretty_env_logger
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-08-06 14:53:07 -04:00
Cadey Ratio e3a9f34178 Merge pull request 'Version 0.5.0: Many improvements' (#8) from refactor-git-mod into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #8
2020-07-09 19:25:57 +00:00
Cadey Ratio 3846c349c4 prepare release
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2020-07-09 13:09:30 -04:00
Cadey Ratio cbf0e9d308 end to end test 2020-07-09 13:06:07 -04:00
Cadey Ratio d5b7b59e3e expose functionality as a crate for testing 2020-07-09 11:46:13 -04:00
Cadey Ratio 2cc49b86db more refactoring 2020-07-09 11:35:49 -04:00
Cadey Ratio bc608f7ddc support pulling, refactor git module 2020-07-09 11:31:09 -04:00
Cadey Ratio 2c098d4f23 support bracketed version numbers
continuous-integration/drone/push Build is passing Details
2020-07-09 10:06:47 -04:00
Cadey Ratio 33019d2548 update to latest version of gitea-release in CI
continuous-integration/drone/push Build is passing Details
2020-07-08 19:30:53 -04:00
Cadey Ratio 99cea1445e Update '.drone.yml'
continuous-integration/drone/push Build is passing Details
2020-07-08 23:25:22 +00:00
Cadey Ratio 5776b802a1 Merge pull request 'pare-down-functionality' (#7) from pare-down-functionality into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is failing Details
2020-07-08 23:19:38 +00:00
Cadey Ratio 524a12305c prepare crates for publishing
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2020-07-08 19:16:09 -04:00
Cadey Ratio 03004b31e7 rename release to cut
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2020-07-08 19:02:31 -04:00
Cadey Ratio 4397f691c9 vtag
continuous-integration/drone/push Build is passing Details
2020-07-08 18:54:43 -04:00
Cadey Ratio 8ef626cbd2 fix
continuous-integration/drone/push Build is passing Details
2020-07-08 18:32:59 -04:00
Cadey Ratio f186ff7ae5 domo gitea token
continuous-integration/drone/push Build is failing Details
2020-07-08 18:26:23 -04:00
Cadey Ratio a45e4ab8df documentation, tests, elfs crate
continuous-integration/drone/push Build encountered an error Details
2020-07-08 18:24:59 -04:00
Cadey Ratio 9e2347d1f8 remove all functionality but release creation 2020-07-08 17:36:53 -04:00
Cadey Ratio 2eec34f267 add gitea crate 2020-07-08 17:35:08 -04:00
Cadey Ratio 3d542a16d6 Update '.drone.yml'
continuous-integration/drone/push Build is passing Details
2020-06-15 16:53:59 +00:00
Cadey Ratio 9967e8f62f Update '.drone.yml'
continuous-integration/drone/push Build is passing Details
2020-06-12 13:50:14 +00:00
Cadey Ratio dc2019b86d Merge pull request 'Automatically pull tags on Drone' (#6) from fetch-tags into master
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-06-12 12:20:14 +00:00
Cadey Ratio 4dd1202195 Automatically pull tags on Drone
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
Closes #4
2020-06-12 08:17:04 -04:00
Cadey Ratio c0aa9787bb Update '.drone.yml'
continuous-integration/drone/push Build is passing Details
2020-06-12 12:14:33 +00:00
Cadey Ratio 927bbcd4d7 v0.3.1: hotfix in json parsing
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-06-12 07:55:31 -04:00
Cadey Ratio 17a35e6480 Update '.drone.yml'
continuous-integration/drone/push Build is passing Details
2020-06-12 11:51:44 +00:00
Cadey Ratio e7672c694b add Creator's Code
continuous-integration/drone/push Build is failing Details
2020-06-12 07:42:17 -04:00
Cadey Ratio 6f0a027530 Update '.drone.yml'
continuous-integration/drone/push Build is failing Details
2020-06-12 11:27:28 +00:00
Cadey Ratio 24b04905ba Allow users to customize the default branch name (#5)
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Allow users to customize the default branch name

Closes #2

CHANGELOG: refer to gitea-release 0.3.0 here

rustfmt

Co-authored-by: Christine Dodrill <me@christine.website>
2020-06-12 11:18:13 +00:00
Cadey Ratio bee79b5ba3 Update '.drone.yml'
continuous-integration/drone/push Build is passing Details
2020-06-11 18:40:07 +00:00
Cadey Ratio 55b530e468 bump
continuous-integration/drone/push Build is failing Details
2020-06-05 19:21:25 -04:00
Cadey Ratio 3c53f42548 Update '.drone.yml'
continuous-integration/drone/push Build encountered an error Details
2020-06-05 14:56:40 +00:00
Cadey Ratio 235560fc71 cmd/release: exit if the tag already exists
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-06-05 10:50:34 -04:00
Cadey Ratio 7d09c0dee0 version 0.2.6
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-06-01 00:14:32 -04:00
Cadey Ratio 02353705b1 Merge pull request 'use FnMut, drop a bunch of refcells' (#1) from kivikakk/gitea-release:use-fnmut into master
continuous-integration/drone/push Build is failing Details
2020-06-01 04:12:55 +00:00
Asherah Connor 5eb6a3c3bf use FnMut, drop a bunch of refcells
continuous-integration/drone/pr Build is passing Details
2020-06-01 12:17:13 +10:00
Cadey Ratio ac2982b2ab docker load -i
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-05-31 16:57:39 -04:00
Cadey Ratio 285d8a7e67 so that's why the build finished so fast
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is failing Details
2020-05-31 16:46:35 -04:00
Cadey Ratio be09d46574 lol oops
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is failing Details
2020-05-31 16:42:12 -04:00
Cadey Ratio f32c927889 version 0.2.2
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is failing Details
2020-05-31 16:35:07 -04:00
37 changed files with 1530 additions and 682 deletions

View File

@ -1,15 +1,23 @@
kind: pipeline
name: tools
name: tests/release
steps:
- name: rust tests
image: "rust:1"
image: "reg.tulpa.dev/rust:1"
pull: always
commands:
- cargo test
- cargo test --all
environment:
GITEA_SERVER: https://tulpa.dev
DOMO_GITEA_TOKEN:
from_secret: DOMO_GITEA_TOKEN
when:
event:
- push
- name: auto-release
image: xena/gitea-release:0.2.1
image: xena/gitea-release
pull: always
settings:
auth_username: cadey
gitea_server: https://tulpa.dev
@ -19,5 +27,83 @@ steps:
event:
- push
branch:
include:
- master
- main
---
kind: pipeline
name: cargo publish
steps:
- name: publish elfs
image: rust:1
commands:
- cd elfs
- cargo login $CARGO_TOKEN
- "cargo publish ||:"
environment:
CARGO_TOKEN:
from_secret: CARGO_TOKEN
when:
event:
- tag
- name: publish gitea
image: rust:1
commands:
- cd gitea
- cargo login $CARGO_TOKEN
- "cargo publish ||:"
environment:
CARGO_TOKEN:
from_secret: CARGO_TOKEN
when:
event:
- tag
---
kind: pipeline
name: docker
steps:
- name: build docker image
image: "monacoremo/nix:2020-04-05-05f09348-circleci"
environment:
USER: root
commands:
- true # cachix use xe
- nix-build docker.nix
- cp $(readlink result) /result/docker.tgz
volumes:
- name: image
path: /result
when:
event:
- tag
- name: push docker image
image: docker:dind
volumes:
- name: image
path: /result
- name: dockersock
path: /var/run/docker.sock
commands:
- docker load -i /result/docker.tgz
- echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin
- docker push xena/gitea-release
environment:
DOCKER_USERNAME:
from_secret: DOCKER_USERNAME
DOCKER_PASSWORD:
from_secret: DOCKER_PASSWORD
when:
event:
- tag
volumes:
- name: image
temp: {}
- name: dockersock
host:
path: /var/run/docker.sock

View File

@ -6,6 +6,127 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
## 0.7.1
Fix a minor parsing bug
## 0.7.0
Support reading version data from `Cargo.toml`
## 0.6.1
Fixed a logic error in release cutting.
## 0.5.2
Detect tags better.
## 0.5.1
Initialize `pretty_env_logger` on app boot. Oops.
## 0.5.0
A lot of internals to this program were exposed to external consumers. This was
used to provide an integration test.
Support for bracketed versions was added, a-la:
```markdown
## [0.1.0]
Hi there this is a test!
### ADDED
- something
```
The gitea crate was brought up to version `0.2.0` and includes a lot more
functionality.
## 0.4.0
This is a functionality-breaking release that removes untested/extraneous parts
of this project.
### ADDED
- The gitea client embedded into this repo is now a more generic crate that can
be reused across other projects.
- Gitea types have been simplified and redundant types have been removed.
- Simple tests for the gitea crate.
- A name generator `elfs` was created for future use in integration testing.
### CHANGED
- `release` is renamed to `cut`, but there is an alias for the old `release`
subcommand name.
### REMOVED
- All functionality but the drone plugin and release commands.
## 0.3.2
Automagically fetch tags when running as a drone plugin.
## 0.3.1
Hotfix in json parsing
## 0.3.0
Allows for a customizable default branch name for the drone plugin using either
a hard-coded value or the Gitea api to fetch it. For compatibility's sake, the
default behavior is to fetch the default branch name from the Gitea api. If you
need to hard-code your default branch name, add the config like this:
```yaml
- name: auto-release
image: xena/gitea-release:0.3.1
settings:
auth_username: cadey
default_branch: trunk
gitea_server: https://tulpa.dev
gitea_token:
from_secret: GITEA_TOKEN
when:
event:
- push
branch:
- trunk
```
Also fixed a suggestion to fetch tags over git before trying to run this in Drone.
## 0.2.7
### FIXED
Exit if this version already exists
## 0.2.6
A fix from @kivikakk to remove the use of RefCells in markdown parsing
## 0.2.5
CD fix
## 0.2.4
I need to make some kind of drone CI validator
## 0.2.3
Typo in the CD script :D
## 0.2.2
Update README, automatically push docker images
## 0.2.1
Hotfix for typos in the docker manifest.

25
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,25 @@
# Code of Conduct
We are sapient.
To be sapient is to be limited.
In our limitation, we make choices that are unwise or are flawed.
If we make unwise choices because of our limitation,
we cannot judge others for the same reason.
So, we cannot judge,
thus we forgive.
This project and its results are intended as:
a place of learning,
a place of understanding,
a place of teaching,
a place of sharing,
a place of creators creating the tools for other creators to create complicated things elegantly.
Be well, Creator. Be well and create.
---
Based on the [Creator's Code v1](https://github.com/Xe/creators-code). Please
read the link for more information.

211
Cargo.lock generated
View File

@ -86,12 +86,6 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byte-unit"
version = "3.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55390dbbf21ce70683f3e926dace00a21da373e35e44a60cafd232e3e9bf2041"
[[package]]
name = "byteorder"
version = "1.3.4"
@ -104,6 +98,17 @@ version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1"
[[package]]
name = "cargo_toml"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "513d17226888c7b8283ac02a1c1b0d8a9d4cbf6db65dfadb79f598f5d7966fe9"
dependencies = [
"serde",
"serde_derive",
"toml",
]
[[package]]
name = "cc"
version = "1.0.54"
@ -134,16 +139,6 @@ dependencies = [
"vec_map",
]
[[package]]
name = "cli-table"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd782cbfda62468ed8f94f2c00496ff909ad4916f4411ab9ec7bdced5414a699"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "comrak"
version = "0.7.0"
@ -192,6 +187,13 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
[[package]]
name = "elfs"
version = "0.1.0"
dependencies = [
"names",
]
[[package]]
name = "encoding_rs"
version = "0.8.23"
@ -207,6 +209,19 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca"
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
@ -234,6 +249,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
@ -325,22 +346,38 @@ dependencies = [
]
[[package]]
name = "gitea-release"
version = "0.2.1"
name = "gitea"
version = "0.2.0"
dependencies = [
"anyhow",
"byte-unit",
"cli-table",
"reqwest",
"serde",
"serde_json",
"thiserror",
"tokio",
]
[[package]]
name = "gitea-release"
version = "0.7.1"
dependencies = [
"anyhow",
"cargo_toml",
"comrak",
"elfs",
"git2",
"gitea",
"http",
"kankyo",
"log",
"pretty_env_logger",
"reqwest",
"serde",
"serde_json",
"structopt",
"tempfile",
"tokio",
"toml",
"url",
]
@ -408,6 +445,15 @@ version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]]
name = "hyper"
version = "0.13.6"
@ -640,6 +686,15 @@ dependencies = [
"ws2_32-sys",
]
[[package]]
name = "names"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef320dab323286b50fb5cdda23f61c796a72a89998ab565ca32525c5c556f2da"
dependencies = [
"rand 0.3.23",
]
[[package]]
name = "native-tls"
version = "0.2.4"
@ -801,6 +856,16 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
[[package]]
name = "pretty_env_logger"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
dependencies = [
"env_logger",
"log",
]
[[package]]
name = "proc-macro-error"
version = "1.0.2"
@ -829,13 +894,19 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.17"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.6"
@ -845,6 +916,29 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
dependencies = [
"libc",
"rand 0.4.6",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi 0.3.8",
]
[[package]]
name = "rand"
version = "0.7.3"
@ -854,7 +948,7 @@ dependencies = [
"getrandom",
"libc",
"rand_chacha",
"rand_core",
"rand_core 0.5.1",
"rand_hc",
]
@ -865,9 +959,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.5.1",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.5.1"
@ -883,7 +992,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
"rand_core 0.5.1",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
@ -995,18 +1113,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.111"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.111"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
dependencies = [
"proc-macro2",
"quote",
@ -1104,9 +1222,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.29"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb37da98a55b1d08529362d9cbb863be17556873df2585904ab9d2bc951291d0"
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
dependencies = [
"proc-macro2",
"quote",
@ -1132,7 +1250,7 @@ checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
dependencies = [
"cfg-if",
"libc",
"rand",
"rand 0.7.3",
"redox_syscall",
"remove_dir_all",
"winapi 0.3.8",
@ -1156,6 +1274,26 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.0.1"
@ -1228,6 +1366,15 @@ dependencies = [
"tokio",
]
[[package]]
name = "toml"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645"
dependencies = [
"serde",
]
[[package]]
name = "tower-service"
version = "0.3.0"

View File

@ -1,6 +1,6 @@
[package]
name = "gitea-release"
version = "0.2.1"
version = "0.7.1"
authors = ["Christine Dodrill <me@christine.website>"]
edition = "2018"
@ -8,21 +8,33 @@ edition = "2018"
[dependencies]
anyhow = "1.0"
byte-unit = "3"
cli-table = "0.3"
cargo_toml = "0.8.1"
comrak = "0.7"
git2 = "0.13"
http = "0.2"
kankyo = "0.3"
log = "0.4"
pretty_env_logger = "0"
reqwest = { version = "0.10", features = ["json"] }
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
structopt = { version = "0.3", default-features = false }
tokio = { version = "0.2", features = ["macros"] }
toml = "0.5.7"
url = "2"
gitea = { path = "./gitea" }
[dev-dependencies]
tempfile = "3"
elfs = { path = "./elfs" }
pretty_env_logger = "0.4"
[profile.release]
lto = true
[workspace]
members = [
"./elfs",
"./gitea"
]

100
README.md
View File

@ -8,3 +8,103 @@ repositories that reads from CHANGELOG and VERSION files. This is a clone of
[github-release](https://github.com/github-release/github-release), but more
suited for my individual needs. This may also turn into a generic webhook
handler, but one thing at a time. :)
## Installation
### With Nix
```console
$ nix-env -if https://tulpa.dev/cadey/gitea-release/archive/master.tar.gz
```
### With cargo
```console
$ cargo install --git https://tulpa.dev/cadey/gitea-release.git
```
## Drone Plugin
To use this as a drone plugin, add the following to your `.drone.yml` under the
`steps` key:
```yaml
- name: auto-release
image: xena/gitea-release:latest
pull: always
settings:
auth_username: cadey
changelog_path: ./CHANGELOG.md
gitea_server: https://tulpa.dev
gitea_token:
from_secret: GITEA_TOKEN
when:
event:
- push
branch:
- master
```
Replace the values of the settings as makes sense for your gitea server. The
`changelog_path` attribute is optional, and will be `./CHANGELOG.md` by default.
The default branch will automatically be derived from the Gitea API. If you need
to hard-code your default branch name for some reason, add the `default_branch`
setting like this:
```yaml
- name: auto-release
image: xena/gitea-release:latest
pull: always
settings:
auth_username: cadey
default_branch: trunk
gitea_server: https://tulpa.dev
gitea_token:
from_secret: GITEA_TOKEN
when:
event:
- push
branch:
- trunk
```
## CHANGELOG.md and VERSION files
The `CHANGELOG.md` file is based on the [Keep a Changelog][kacl] format, but
modified slightly to make it easier for this tool. Here is an example changelog
that this tool accepts:
[kacl]: https://keepachangelog.com/en/1.0.0/
```markdown
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.1.0
Hi there this is a test!
### ADDED
- something
```
The `VERSION` file plays into this as well. The `VERSION` file MUST be a single
line containing a [semantic version][semver] string. When this tool is run with
the `release` subcommand, the following actions take place:
[semver]: https://semver.org/spec/v2.0.0.html
- The `VERSION` file is read and loaded as the desired tag for the repo
- The `CHANGELOG.md` file is read and the changes for the `VERSION` are
cherry-picked out of the file
- The git repo is checked to see if that tag already exists
- If the tag exists, the tool exits and does nothing
- If the tag does not exist, it is created (with the changelog fragment as the
body of the tag) and pushed to the gitea server
- A gitea release is created using the changelog fragment and the release name
is generated from the `VERSION` string

View File

@ -13,8 +13,8 @@
CLOSED: [2020-05-31 Sun 12:50]
** DONE upload
CLOSED: [2020-05-30 Sat 15:15]
** TODO drone plugin
** DONE drone plugin
CLOSED: [2020-06-01 Mon 10:10]
* Core Features
** DONE Gitea API client
CLOSED: [2020-05-30 Sat 10:52]

View File

@ -1 +0,0 @@
0.2.1

View File

@ -8,7 +8,7 @@ let
dockerImage = pkg:
pkgs.dockerTools.buildLayeredImage {
name = "xena/gitea-release";
tag = "${gitea-release.version}";
tag = "latest";
contents = [ pkgs.cacert pkg ];

16
elfs/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "elfs"
version = "0.1.0"
authors = ["Christine Dodrill <me@christine.website>"]
edition = "2018"
homepage = "https://tulpa.dev/cadey/gitea-release/src/branch/main/elfs"
repository = "https://tulpa.dev/cadey/gitea-release"
keywords = ["namegen"]
license = "MIT"
readme = "README.md"
description = "A simple name generator for Rust programs."
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
names = "0.11"

16
elfs/README.md Normal file
View File

@ -0,0 +1,16 @@
# elfs
A simple name generator based on [Pokemon Vietnamese Crystal](https://tvtropes.org/pmwiki/pmwiki.php/VideoGame/PokemonVietnameseCrystal).
## Usage
Add the following to your `Cargo.toml`:
```toml
[dependencies]
elfs = "0.1"
```
```rust
let name = elfs::next();
```

75
elfs/src/lib.rs Normal file
View File

@ -0,0 +1,75 @@
/// This one-function crate generates names based on [Pokemon Vietnamese Crystal](https://tvtropes.org/pmwiki/pmwiki.php/VideoGame/PokemonVietnameseCrystal).
use names::{Generator, Name};
fn moves() -> &'static [&'static str] {
&[
"ABLE", "ABNORMA", "AGAIN", "AIREXPL", "ANG", "ANGER", "ASAIL", "ATTACK", "AURORA", "AWL",
"BAN", "BAND", "BARE", "BEAT", "BEATED", "BELLY", "BIND", "BITE", "BLOC", "BLOOD", "BODY",
"BOOK", "BREATH", "BUMP", "CAST", "CHAM", "CLAMP", "CLAP", "CLAW", "CLEAR", "CLI", "CLIP",
"CLOUD", "CONTRO", "CONVY", "COOLHIT", "CRASH", "CRY", "CUT", "DESCRI", "D-FIGHT", "DIG",
"DITCH", "DIV", "DOZ", "DRE", "DUL", "DU-PIN", "DYE", "EARTH", "EDU", "EG-BOMB", "EGG",
"ELEGY", "ELE-HIT", "EMBODY", "EMPLI", "ENGL", "ERUPT", "EVENS", "EXPLOR", "EYES", "FALL",
"FAST", "F-CAR", "F-DANCE", "FEARS", "F-FIGHT", "FIGHT", "FIR", "FIRE", "FIREHIT", "FLAME",
"FLAP", "FLASH", "FLEW", "FORCE", "FRA", "FREEZE", "FROG", "G-BIRD", "GENKISS", "GIFT",
"G-KISS", "G-MOUSE", "GRADE", "GROW", "HAMMER", "HARD", "HAT", "HATE", "H-BOMB", "HELL-R",
"HEMP", "HINT", "HIT", "HU", "HUNT", "HYPNOSI", "INHA", "IRO", "IRONBAR", "IR-WING",
"J-GUN", "KEE", "KICK", "KNIF", "KNIFE", "KNOCK", "LEVEL", "LIGH", "LIGHHIT", "LIGHT",
"LIVE", "L-WALL", "MAD", "MAJUS", "MEL", "MELO", "MESS", "MILK", "MIMI", "MISS", "MIXING",
"MOVE", "MUD", "NI-BED", "NOISY", "NOONLI", "NULL", "N-WAVE", "PAT", "PEACE", "PIN",
"PLAN", "PLANE", "POIS", "POL", "POWDE", "POWE", "POWER", "PRIZE", "PROTECT", "PROUD",
"RAGE", "RECOR", "REFLAC", "REFREC", "REGR", "RELIV", "RENEW", "R-FIGHT", "RING", "RKICK",
"ROCK", "ROUND", "RUS", "RUSH", "SAND", "SAW", "SCISSOR", "SCRA", "SCRIPT", "SEEN",
"SERVER", "SHADOW", "SHELL", "SHINE", "SHO", "SIGHT", "SIN", "SMALL", "SMELT", "SMOK",
"SNAKE", "SNO", "SNOW", "SOU", "SO-WAVE", "SPAR", "SPEC", "SPID", "S-PIN", "SPRA", "STAM",
"STARE", "STEA", "STONE", "STORM", "STRU", "STRUG", "STUDEN", "SUBS", "SUCID", "SUN-LIG",
"SUNRIS", "SUPLY", "S-WAVE", "TAILS", "TANGL", "TASTE", "TELLI", "THANK", "TONKICK",
"TOOTH", "TORL", "TRAIN", "TRIKICK", "TUNGE", "VOLT", "WA-GUN", "WATCH", "WAVE", "W-BOMB",
"WFALL", "WFING", "WHIP", "WHIRL", "WIND", "WOLF", "WOOD", "WOR", "YUJA",
]
}
fn names() -> &'static [&'static str] {
&[
"SEED", "GRASS", "FLOWE", "SHAD", "CABR", "SNAKE", "GOLD", "COW", "GUIKI", "PEDAL",
"DELAN", "B-FLY", "BIDE", "KEYU", "FORK", "LAP", "PIGE", "PIJIA", "CAML", "LAT", "BIRD",
"BABOO", "VIV", "ABOKE", "PIKAQ", "RYE", "SAN", "BREAD", "LIDEL", "LIDE", "PIP", "PIKEX",
"ROK", "JUGEN", "PUD", "BUDE", "ZHIB", "GELU", "GRAS", "FLOW", "LAFUL", "ATH", "BALA",
"CORN", "MOLUF", "DESP", "DAKED", "MIMI", "BOLUX", "KODA", "GELUD", "MONK", "SUMOY",
"GEDI", "WENDI", "NILEM", "NILE", "NILEC", "KEZI", "YONGL", "HUDE", "WANLI", "GELI",
"GUAIL", "MADAQ", "WUCI", "WUCI", "MUJEF", "JELLY", "SICIB", "GELU", "NELUO", "BOLI",
"JIALE", "YED", "YEDE", "CLO", "SCARE", "AOCO", "DEDE", "DEDEI", "BAWU", "JIUG", "BADEB",
"BADEB", "HOLE", "BALUX", "GES", "FANT", "QUAR", "YIHE", "SWAB", "SLIPP", "CLU", "DEPOS",
"BILIY", "YUANO", "SOME", "NO", "YELA", "EMPT", "ZECUN", "XIAHE", "BOLEL", "DEJI", "MACID",
"XIHON", "XITO", "LUCK", "MENJI", "GELU", "DECI", "XIDE", "DASAJ", "DONGN", "RICUL",
"MINXI", "BALIY", "ZENDA", "LUZEL", "HELE5", "0FENB", "KAIL", "JIAND", "CARP", "JINDE",
"LAPU", "MUDE", "YIFU", "LINLI", "SANDI", "HUSI", "JINC", "OUMU", "OUMUX", "CAP", "KUIZA",
"PUD", "TIAO", "FRMAN", "CLAU", "SPARK", "DRAGO", "BOLIU", "GUAIL", "MIYOU", "MIY",
"QIAOK", "BEIL", "MUKEI", "RIDED", "MADAM", "BAGEP", "CROC", "ALIGE", "OUDAL", "OUD",
"DADA", "HEHE", "YEDEA", "NUXI", "NUXIN", "ROUY", "ALIAD", "STICK", "QIANG", "LAAND",
"PIQI", "PI", "PUPI", "DEKE", "DEKEJ", "NADI", "NADIO", "MALI", "PEA", "ELECT", "FLOWE",
"MAL", "MALI", "HUSHU", "NILEE", "YUZI", "POPOZ", "DUZI", "HEBA", "XIAN", "SHAN", "YEYEA",
"WUY", "LUO", "KEFE", "HULA", "CROW", "YADEH", "MOW", "ANNAN", "SUONI", "KYLI", "HULU",
"HUDEL", "YEHE", "GULAE", "YEHE", "BLU", "GELAN", "BOAT", "NIP", "POIT", "HELAK", "XINL",
"BEAR", "LINB", "MAGEH", "MAGEJ", "WULI", "YIDE", "RIVE", "FISH", "AOGU", "DELIE", "MANTE",
"KONMU", "DELU", "HELU", "HUAN", "HUMA", "DONGF", "JINCA", "HEDE", "DEFU", "LIBY", "JIAPA",
"MEJI", "HELE", "BUHU", "MILK", "HABI", "THUN", "GARD", "DON", "YANGQ", "SANAQ", "BANQ",
"LUJ", "PHIX", "SIEI", "EGG",
]
}
/// Generate a new name based on [Pokemon Vietnamese Crystal](https://tvtropes.org/pmwiki/pmwiki.php/VideoGame/PokemonVietnameseCrystal)
///
/// ```rust
/// let name = elfs::next();
/// ```
pub fn next() -> String {
let mut generator = Generator::new(moves(), names(), Name::Numbered);
generator.next().unwrap()
}
#[cfg(test)]
#[test]
fn name() {
assert_ne!(next(), "".to_string());
}

23
gitea/Cargo.toml Normal file
View File

@ -0,0 +1,23 @@
[package]
name = "gitea"
version = "0.2.0"
authors = ["Christine Dodrill <me@christine.website>"]
edition = "2018"
homepage = "https://tulpa.dev/cadey/gitea-release/src/branch/main/gitea"
repository = "https://tulpa.dev/cadey/gitea-release"
keywords = ["gitea", "api", "http"]
license = "MIT"
readme = "README.md"
description = "A Gitea client for Rust programs."
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
thiserror = "1"
reqwest = { version = "0.10", features = ["json"] }
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
[dev-dependencies]
anyhow = "1"
tokio = { version = "0.2", features = ["macros"] }

8
gitea/README.md Normal file
View File

@ -0,0 +1,8 @@
# gitea
A simple Gitea client for Rust programs. You will need an API token as described
[here](https://docs.gitea.io/en-us/api-usage/).
```toml
gitea = "0.1.0"
```

425
gitea/src/lib.rs Normal file
View File

@ -0,0 +1,425 @@
/// The main Gitea client. You will need an API token as described [here](https://docs.gitea.io/en-us/api-usage/).
use reqwest::header;
use serde::{Deserialize, Serialize};
use std::result::Result as StdResult;
use thiserror::Error;
/// Error represents all of the possible errors that can happen with the Gitea
/// API. Most of these errors boil down to user error.
#[derive(Error, Debug)]
pub enum Error {
#[error("error from reqwest: {0:#?}")]
Reqwest(#[from] reqwest::Error),
#[error("bad API token: {0:#?}")]
BadAPIToken(#[from] reqwest::header::InvalidHeaderValue),
#[error("error parsing/serializing json: {0:#?}")]
Json(#[from] serde_json::Error),
#[error("tag not found: {0}")]
TagNotFound(String),
}
/// A handy alias for Result like `anyhow::Result`.
pub type Result<T> = StdResult<T, Error>;
/// A repository release.
/// https://try.gitea.io/api/swagger#model-Release
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Release {
pub id: i64,
pub tag_name: String,
pub target_commitish: String,
pub name: String,
pub body: String,
pub url: String,
pub tarball_url: String,
pub zipball_url: String,
pub draft: bool,
pub prerelease: bool,
pub created_at: String,
pub published_at: String,
pub author: User,
pub assets: Vec<Attachment>,
}
/// The inputs to create a repository release.
/// https://try.gitea.io/api/swagger#model-CreateReleaseOption
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CreateRelease {
pub body: String,
pub draft: bool,
pub name: String,
pub prerelease: bool,
pub tag_name: String,
pub target_commitish: String,
}
/// An attachment to a release, such as a pre-built package.
/// https://try.gitea.io/api/swagger#model-Attachment
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Attachment {
pub id: i64,
pub name: String,
pub size: i64,
pub download_count: i64,
pub created_at: String,
pub uuid: String,
pub browser_download_url: String,
}
/// Inputs to create a gitea repo.
/// https://try.gitea.io/api/swagger#model-CreateRepoOption
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CreateRepo {
pub auto_init: bool,
pub description: String,
pub gitignores: String,
pub issue_labels: String,
pub license: String,
pub name: String,
pub private: bool,
pub readme: String,
}
/// A git repository.
/// https://try.gitea.io/api/swagger#model-Repository
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Repo {
pub allow_merge_commits: bool,
pub allow_rebase: bool,
pub allow_rebase_explicit: bool,
pub allow_squash_merge: bool,
pub archived: bool,
pub avatar_url: String,
pub clone_url: String,
pub created_at: String,
pub default_branch: String,
pub description: String,
pub empty: bool,
pub fork: bool,
pub forks_count: i64,
pub full_name: String,
pub has_issues: bool,
pub has_pull_requests: bool,
pub has_wiki: bool,
pub html_url: String,
pub id: i64,
pub ignore_whitespace_conflicts: bool,
pub mirror: bool,
pub name: String,
pub open_issues_count: i64,
pub open_pr_counter: i64,
pub original_url: String,
pub owner: User,
pub permissions: Permissions,
pub private: bool,
pub release_counter: i64,
pub size: i64,
pub ssh_url: String,
pub stars_count: i64,
pub template: bool,
pub updated_at: String,
pub watchers_count: i64,
pub website: String,
}
/// A user.
/// https://try.gitea.io/api/swagger#model-User
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct User {
pub avatar_url: String,
pub created: String,
pub email: String,
pub full_name: String,
pub id: i64,
pub is_admin: bool,
pub language: String,
pub last_login: String,
pub login: String,
}
/// The permission set that a given user has on a Repo.
/// https://try.gitea.io/api/swagger#model-Permission
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Permissions {
pub admin: bool,
pub pull: bool,
pub push: bool,
}
/// The version of Gitea.
/// https://try.gitea.io/api/swagger#model-ServerVersion
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Version {
pub version: String,
}
/// The gitea client that all gitea calls will go through. This wraps
/// [reqwest::Client](https://docs.rs/reqwest/0.10.6/reqwest/struct.Client.html)
/// and operates asyncronously.
pub struct Client {
cli: reqwest::Client,
base_url: String,
}
impl Client {
/// Create a new API client with the given base URL, token and user agent.
/// If you need inspiration for a user agent, try this:
///
/// ```rust
/// const APP_USER_AGENT: &'static str =
/// concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
/// gitea::Client::new("https://tulpa.dev".into(), "ayylmao".into(), APP_USER_AGENT).unwrap();
/// ```
pub fn new<T>(base_url: String, token: String, user_agent: T) -> Result<Self>
where
T: Into<String>,
{
let mut headers = header::HeaderMap::new();
let auth = format!("token {}", token);
let auth = auth.as_str();
headers.insert(header::AUTHORIZATION, header::HeaderValue::from_str(auth)?);
let cli = reqwest::Client::builder()
.user_agent(user_agent.into())
.default_headers(headers)
.build()?;
Ok(Self {
cli: cli,
base_url: base_url,
})
}
/// Gets the current version of gitea.
///
/// ```rust
/// use gitea::Result;
///
/// #[tokio::main]
/// async fn main() -> Result<()> {
/// let cli = gitea::Client::new("https://tulpa.dev".into(), "ayylmao".into(), "test/test")?;
/// println!("{:?}", cli.version().await?);
/// Ok(())
/// }
/// ```
pub async fn version(&self) -> Result<Version> {
Ok(self
.cli
.get(&format!("{}/api/v1/version", self.base_url))
.send()
.await?
.error_for_status()?
.json()
.await?)
}
/// Gets a release of a repo by its tag name.
///
/// ```rust
/// use gitea::Result;
///
/// #[tokio::main]
/// async fn main() -> Result<()> {
/// let cli = gitea::Client::new("https://tulpa.dev".into(), "ayylmao".into(), "test/test")?;
/// let release = cli.get_release_by_tag("cadey".into(), "gitea-release".into(), "0.3.2".into()).await;
/// Ok(())
/// }
/// ```
pub async fn get_release_by_tag(
&self,
owner: String,
repo: String,
tag: String,
) -> Result<Release> {
let releases: Vec<Release> = self.get_releases(owner, repo).await?;
let mut release: Option<Release> = None;
for rls in releases {
if *tag == rls.tag_name {
release = Some(rls);
}
}
match release {
None => Err(Error::TagNotFound(tag)),
Some(release) => Ok(release),
}
}
/// Creates a new gitea repo for the currently authenticated user with given details.
pub async fn create_user_repo(&self, cr: CreateRepo) -> Result<Repo> {
Ok(self
.cli
.post(&format!("{}/api/v1/user/repos", self.base_url))
.json(&cr)
.send()
.await?
.error_for_status()?
.json()
.await?)
}
/// Creates a new gitea repo for a given organization with given details.
pub async fn create_org_repo(&self, org: String, cr: CreateRepo) -> Result<Repo> {
Ok(self
.cli
.post(&format!("{}/api/v1/org/{}/repos", self.base_url, org))
.json(&cr)
.send()
.await?
.error_for_status()?
.json()
.await?)
}
/// Deletes a gitea repo by owner and name.
pub async fn delete_repo(&self, owner: String, repo: String) -> Result<()> {
self.cli
.delete(&format!(
"{}/api/v1/repos/{}/{}",
self.base_url, owner, repo
))
.send()
.await?
.error_for_status()?;
Ok(())
}
/// Gets a gitea repo by name.
///
/// ```rust
/// use gitea::Result;
///
/// #[tokio::main]
/// async fn main() -> Result<()> {
/// let cli = gitea::Client::new("https://tulpa.dev".into(), "ayylmao".into(), "test/test")?;
/// let repo = cli.get_repo("cadey".into(), "gitea-release".into()).await;
/// Ok(())
/// }
/// ```
pub async fn get_repo(&self, owner: String, repo: String) -> Result<Repo> {
Ok(self
.cli
.get(&format!(
"{}/api/v1/repos/{}/{}",
self.base_url, owner, repo
))
.send()
.await?
.error_for_status()?
.json()
.await?)
}
/// Gets all of the releases for a given gitea repo.
///
/// ```rust
/// use gitea::Result;
///
/// #[tokio::main]
/// async fn main() -> Result<()> {
/// let cli = gitea::Client::new("https://tulpa.dev".into(), "ayylmao".into(), "test/test")?;
/// let repo = cli.get_releases("cadey".into(), "gitea-release".into()).await;
/// Ok(())
/// }
/// ```
pub async fn get_releases(&self, owner: String, repo: String) -> Result<Vec<Release>> {
Ok(self
.cli
.get(&format!(
"{}/api/v1/repos/{}/{}/releases",
self.base_url, owner, repo
))
.send()
.await?
.error_for_status()?
.json()
.await?)
}
/// Creates a new gitea release.
///
/// ```rust
/// use gitea::{CreateRelease, Result};
///
/// #[tokio::main]
/// async fn main() -> Result<()> {
/// let cli = gitea::Client::new("https://tulpa.dev".into(), "ayylmao".into(), "test/test")?;
/// let repo = cli.create_release(
/// "cadey".into(),
/// "gitea-release".into(),
/// CreateRelease{
/// body: "This is a cool release".into(),
/// draft: false,
/// name: "test".into(),
/// prerelease: false,
/// tag_name: "v4.2.0".into(),
/// target_commitish: "HEAD".into(),
/// },
/// ).await;
/// Ok(())
/// }
/// ```
pub async fn create_release(
&self,
owner: String,
repo: String,
cr: CreateRelease,
) -> Result<Release> {
Ok(self
.cli
.post(&format!(
"{}/api/v1/repos/{}/{}/releases",
self.base_url, owner, repo
))
.json(&cr)
.send()
.await?
.error_for_status()?
.json()
.await?)
}
/// Deletes a given release by tag name.
///
/// ```rust
/// use gitea::Result;
///
/// #[tokio::main]
/// async fn main() -> Result<()> {
/// let cli = gitea::Client::new("https://tulpa.dev".into(), "ayylmao".into(), "test/test")?;
/// let _ = cli.delete_release("cadey".into(), "gitea-release".into(), "4.2.0".into()).await;
/// Ok(())
/// }
/// ```
pub async fn delete_release(&self, owner: String, repo: String, tag: String) -> Result<()> {
let release = self
.get_release_by_tag(owner.clone(), repo.clone(), tag)
.await?;
self.cli
.delete(&format!(
"{}/api/v1/repos/{}/{}/releases/{}",
self.base_url, owner, repo, release.id
))
.send()
.await?
.error_for_status()?;
Ok(())
}
/// Returns information about the currently authenticated user.
pub async fn whoami(&self) -> Result<User> {
Ok(self
.cli
.get(&format!("{}/api/v1/user", self.base_url))
.send()
.await?
.error_for_status()?
.json()
.await?)
}
}

16
gitea/tests/version.rs Normal file
View File

@ -0,0 +1,16 @@
use anyhow::Result;
use gitea::Client;
#[tokio::test]
async fn version() -> Result<()> {
let cli = Client::new(
std::env::var("GITEA_SERVER")?,
std::env::var("DOMO_GITEA_TOKEN")?,
"gitea/tests",
)?;
let vers = cli.version().await?;
println!("gitea version {}", vers.version);
Ok(())
}

View File

@ -5,10 +5,10 @@
"homepage": "",
"owner": "nmattia",
"repo": "naersk",
"rev": "a82fd7dc31a58c462b6dfa9d9d886fa2cc75dfd4",
"sha256": "00bjwir52y6jbf0b22qy9qxramw35k5fc7kp9hymr1zgpmw9kbwg",
"rev": "529e910a3f423a8211f8739290014b754b2555b6",
"sha256": "0bcy9nmyaan5jvp0wg80wkizc9j166ns685rdr1kbhkvdpywv46y",
"type": "tarball",
"url": "https://github.com/nmattia/naersk/archive/a82fd7dc31a58c462b6dfa9d9d886fa2cc75dfd4.tar.gz",
"url": "https://github.com/nmattia/naersk/archive/529e910a3f423a8211f8739290014b754b2555b6.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"nixpkgs-mozilla": {
@ -17,10 +17,10 @@
"homepage": null,
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "e912ed483e980dfb4666ae0ed17845c4220e5e7c",
"sha256": "08fvzb8w80bkkabc1iyhzd15f4sm7ra10jn32kfch5klgl0gj3j3",
"rev": "efda5b357451dbb0431f983cca679ae3cd9b9829",
"sha256": "11wqrg86g3qva67vnk81ynvqyfj0zxk83cbrf0p9hsvxiwxs8469",
"type": "tarball",
"url": "https://github.com/mozilla/nixpkgs-mozilla/archive/e912ed483e980dfb4666ae0ed17845c4220e5e7c.tar.gz",
"url": "https://github.com/mozilla/nixpkgs-mozilla/archive/efda5b357451dbb0431f983cca679ae3cd9b9829.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
}
}

View File

@ -4,6 +4,7 @@ let
in pkgs.mkShell {
buildInputs = with pkgs; [
latest.rustChannels.stable.rust
cargo-watch
openssl
pkg-config
libgit2

View File

@ -1,7 +1,6 @@
use anyhow::Result;
use comrak::nodes::{AstNode, NodeValue};
use comrak::{format_commonmark, parse_document, Arena, ComrakOptions};
use std::cell::RefCell;
use std::fs::read_to_string;
use std::path::PathBuf;
@ -10,41 +9,35 @@ pub(crate) fn read(fname: PathBuf, tag: String) -> Result<String> {
let arena = Arena::new();
let mut root = parse_document(&arena, &data, &ComrakOptions::default());
let collect = RefCell::new(false);
let buf = RefCell::new(Vec::<u8>::new());
let mut collect = false;
let mut buf = Vec::<u8>::new();
iter_nodes(&mut root, &|node| {
iter_nodes(&mut root, &mut |node| {
let nd = node.data.borrow();
match nd.value {
NodeValue::Heading(ref hdr) => {
if hdr.level == 2 {
if *collect.borrow() {
collect.swap(&RefCell::new(false));
if collect {
collect = false;
}
let found_tag = String::from_utf8(nd.content.clone())?;
if found_tag == tag {
collect.swap(&RefCell::new(true));
if found_tag == tag || found_tag == format!("[{}]", tag) {
collect = true;
}
} else {
if *collect.borrow() {
let mut apd = buf.borrow_mut();
let mut new_buf = Vec::<u8>::new();
format_commonmark(&node, &ComrakOptions::default(), &mut new_buf)?;
apd.append(&mut new_buf);
if collect {
format_commonmark(&node, &ComrakOptions::default(), &mut buf)?;
}
}
Ok(())
}
NodeValue::Item(_) => Ok(()),
_ => {
if *collect.borrow() {
let mut apd = buf.borrow_mut();
let mut new_buf = Vec::<u8>::new();
format_commonmark(&node, &ComrakOptions::default(), &mut new_buf)?;
apd.append(&mut new_buf);
if collect {
format_commonmark(&node, &ComrakOptions::default(), &mut buf)?;
}
Ok(())
@ -52,12 +45,12 @@ pub(crate) fn read(fname: PathBuf, tag: String) -> Result<String> {
}
})?;
Ok(String::from_utf8(buf.into_inner())?)
Ok(String::from_utf8(buf)?)
}
fn iter_nodes<'a, F>(node: &'a AstNode<'a>, f: &F) -> Result<()>
fn iter_nodes<'a, F>(node: &'a AstNode<'a>, f: &mut F) -> Result<()>
where
F: Fn(&'a AstNode<'a>) -> Result<()>,
F: FnMut(&'a AstNode<'a>) -> Result<()>,
{
f(node)?;
for c in node.children() {
@ -77,10 +70,24 @@ where
#[cfg(test)]
mod tests {
#[test]
fn read_changelog() {
fn basic() {
let res = super::read("testdata/basic.md".into(), "0.1.0".into());
assert!(res.is_ok());
let delta = res.unwrap();
assert_eq!(delta, "Hi there this is a test\\!\n### ADDED\n - something\n")
assert_eq!(
delta,
"Hi there this is a test\\!\n### ADDED\n - something\n"
)
}
#[test]
fn brackets() {
let res = super::read("testdata/brackets.md".into(), "0.1.0".into());
assert!(res.is_ok());
let delta = res.unwrap();
assert_eq!(
delta,
"Hi there this is a test\\!\n### ADDED\n - something\n"
)
}
}

40
src/cmd/cut.rs Normal file
View File

@ -0,0 +1,40 @@
use crate::{changelog, cmd::*, git, version};
use anyhow::Result;
use std::path::PathBuf;
pub async fn run(
common: Common,
fname: PathBuf,
tag: Option<String>,
rm: ReleaseMeta,
) -> Result<()> {
let repo = git2::Repository::open(".")?;
let tag = tag.unwrap_or(version::read("VERSION".into())?);
let vtag = format!("v{}", tag);
if git::has_tag(&repo, vtag.clone())? {
println!("release {} already released", vtag);
return Ok(());
}
let desc = changelog::read(fname, tag.clone())?;
let cli = gitea::Client::new(common.server, common.token, crate::APP_USER_AGENT)?;
let repo = cli
.get_repo(common.owner.clone(), common.repo.clone())
.await?;
let cr = gitea::CreateRelease {
body: desc,
draft: rm.draft,
name: rm.name.or(Some(format!("Version {}", tag))).unwrap(),
prerelease: rm.pre_release,
tag_name: vtag,
target_commitish: repo.default_branch,
};
let _ = cli.create_release(common.owner, common.repo, cr).await?;
println!("Created release {}", tag);
Ok(())
}

View File

@ -1,25 +0,0 @@
use crate::{gitea::*, *};
use anyhow::{anyhow, Result};
pub(crate) async fn run(common: Common, tag: String) -> Result<()> {
let cli = client(&common)?;
let release =
get_release_by_tag(&cli, &common.server, &common.owner, &common.repo, &tag).await?;
let resp = cli
.delete(
format!(
"{}/api/v1/repos/{}/{}/releases/{}",
&common.server, &common.owner, &common.repo, release.id
)
.as_str(),
)
.send()
.await?;
if resp.status() != http::StatusCode::from_u16(204)? {
Err(anyhow!("wanted 204, got {}", resp.status()))
} else {
Ok(())
}
}

View File

@ -1,59 +0,0 @@
use crate::{gitea::*, *};
use anyhow::{anyhow, Result};
use cli_table::{Cell, Row, Table};
use std::fs::File;
use std::io::Write;
pub(crate) async fn run(common: Common, fname: Option<PathBuf>, tag: String) -> Result<()> {
let cli = client(&common)?;
let release =
get_release_by_tag(&cli, &common.server, &common.owner, &common.repo, &tag).await?;
let attachments = get_attachments_for_release(
&cli,
&common.server,
&common.owner,
&common.repo,
&release.id,
)
.await?;
match fname {
None => {
let mut rows: Vec<Row> = vec![Row::new(vec![
Cell::new(&"name", Default::default()),
Cell::new(&"size", Default::default()),
Cell::new(&"url", Default::default()),
])];
for attachment in attachments {
rows.push(attachment.row())
}
let table = Table::new(rows, Default::default())?;
table.print_stdout()?;
Ok(())
}
Some(fname) => {
let mut url: Option<String> = None;
let fname = fname.into_os_string().into_string().unwrap();
for attachment in attachments {
if &fname == &attachment.name {
url = Some(attachment.browser_download_url);
}
}
if url.is_none() {
return Err(anyhow!("no attachment named {}", fname));
}
let data = &cli.get(url.unwrap().as_str()).send().await?.bytes().await?;
let mut fout = File::create(&fname)?;
fout.write(data)?;
Ok(())
}
}
}

View File

@ -3,20 +3,36 @@ use anyhow::Result;
use git2::Repository;
use url::Url;
pub(crate) async fn run(env: DroneEnv) -> Result<()> {
if env.branch != "master" {
pub async fn run(env: DroneEnv) -> Result<()> {
let common: Common = env.clone().into();
let default_branch = {
let common = common.clone();
match &env.default_branch {
None => {
let cli =
gitea::Client::new(common.server, common.token, crate::APP_USER_AGENT)?;
let repo = cli.get_repo(common.owner, common.repo).await?;
repo.default_branch
}
Some(branch) => branch.to_string(),
}
};
if env.branch != default_branch {
return Ok(());
}
let common: Common = env.clone().into();
let repo = Repository::open(".")?;
let mut u = Url::parse(&env.push_url)?;
u.set_username(&env.auth_user).unwrap();
u.set_password(Some(&env.token)).unwrap();
repo.remote_delete("origin")?;
repo.remote("origin", u.as_str())?;
let mut origin = repo.remote("origin", u.as_str())?;
origin.connect(git2::Direction::Fetch)?;
origin.fetch(&["refs/tags/*:refs/tags/*"], None, None)?;
release::run(
cut::run(
common,
env.changelog_path,
None,

View File

@ -1,50 +0,0 @@
use crate::{gitea::*, *};
use anyhow::{anyhow, Result};
pub(crate) async fn run(
common: Common,
description: Option<String>,
rm: ReleaseMeta,
tag: String,
) -> Result<()> {
let cli = client(&common)?;
let release =
get_release_by_tag(&cli, &common.server, &common.owner, &common.repo, &tag).await?;
let mut cr = CreateRelease {
body: release.body,
draft: release.draft,
name: release.name,
prerelease: release.prerelease,
tag_name: release.tag_name,
target_commitish: release.target_commitish,
};
if let Some(description) = description {
cr.body = description;
}
if let Some(name) = rm.name {
cr.name = name;
}
cr.draft = rm.draft;
cr.prerelease = rm.pre_release;
let resp = cli
.post(
&format!(
"{}/api/v1/repos/{}/{}/releases/{}",
common.server, common.owner, common.repo, release.id
),
)
.json(&cr)
.send()
.await?;
if !resp.status().is_success() {
return Err(anyhow!("{:?}", resp.status()));
}
Ok(())
}

View File

@ -1,97 +0,0 @@
use crate::{gitea::*, *};
use anyhow::{anyhow, Result};
use cli_table::{Cell, Row, Table};
pub(crate) async fn run(common: Common, json: bool, tag: Option<String>) -> Result<()> {
let cli = client(&common)?;
let releases: Vec<Release> = cli
.get(
format!(
"{}/api/v1/repos/{}/{}/releases",
&common.server, &common.owner, &common.repo
)
.as_str(),
)
.send()
.await?
.json()
.await?;
match tag {
Some(tag) => {
let mut release: Option<Release> = None;
for rls in releases {
if tag == rls.tag_name {
release = Some(rls);
}
}
if release.is_none() {
return Err(anyhow!("tag {} not found", tag));
}
if json {
println!("{}", serde_json::to_string_pretty(&release)?);
} else {
let rls = release.unwrap();
let table = Table::new(
vec![
Row::new(vec![
Cell::new(&"id", Default::default()),
Cell::new(&rls.id, Default::default()),
]),
Row::new(vec![
Cell::new(&"author", Default::default()),
Cell::new(
&format!("{} - {}", rls.author.full_name, rls.author.username),
Default::default(),
),
]),
Row::new(vec![
Cell::new(&"tag", Default::default()),
Cell::new(&rls.tag_name, Default::default()),
]),
Row::new(vec![
Cell::new(&"created at", Default::default()),
Cell::new(&rls.created_at, Default::default()),
]),
Row::new(vec![
Cell::new(&"name", Default::default()),
Cell::new(&rls.name, Default::default()),
]),
Row::new(vec![
Cell::new(&"body", Default::default()),
Cell::new(&rls.body, Default::default()),
]),
],
Default::default(),
)?;
table.print_stdout()?;
}
}
None => {
if json {
println!("{}", serde_json::to_string_pretty(&releases)?);
} else {
let mut rows: Vec<Row> = vec![Row::new(vec![
Cell::new(&"id", Default::default()),
Cell::new(&"tag", Default::default()),
Cell::new(&"created at", Default::default()),
Cell::new(&"commit", Default::default()),
Cell::new(&"author", Default::default()),
Cell::new(&"name", Default::default()),
])];
for release in releases {
rows.push(release.row())
}
let table = Table::new(rows, Default::default())?;
table.print_stdout()?;
}
}
}
Ok(())
}

View File

@ -1,59 +1,87 @@
use std::path::PathBuf;
use structopt::StructOpt;
pub(crate) mod delete;
pub(crate) mod download;
pub(crate) mod drone_plugin;
pub(crate) mod edit;
pub(crate) mod info;
pub(crate) mod release;
pub(crate) mod upload;
pub mod cut;
pub mod drone_plugin;
#[derive(StructOpt, Debug)]
pub(crate) struct Common {
#[derive(StructOpt, Debug, Clone)]
pub struct Common {
/// The gitea server to connect to
#[structopt(short, long, env = "GITEA_SERVER")]
pub server: String,
/// The gitea token to authenticate with
#[structopt(long, env = "GITEA_TOKEN")]
pub token: String,
/// The gitea user to authenticate as
#[structopt(short, long, env = "GITEA_AUTH_USER")]
pub auth_user: String,
/// The owner of the gitea repo
#[structopt(short, long, env = "GITEA_OWNER")]
pub owner: String,
/// The gitea repo to operate on
#[structopt(short, long, env = "GITEA_REPO")]
pub repo: String,
/// Git signature email
#[structopt(long, short = "E", env = "SIGNATURE_EMAIL", default_value = "domo@tulpa.dev")]
pub email: String,
/// Git signature username
#[structopt(long, short = "U", env = "SIGNATURE_NAME", default_value = "Domo Arigato")]
pub username: String,
}
#[derive(StructOpt, Debug, Clone)]
pub(crate) struct DroneEnv {
pub struct DroneEnv {
// Given by drone
/// push URL
#[structopt(env="DRONE_GIT_HTTP_URL")]
#[structopt(long, env = "DRONE_GIT_HTTP_URL")]
pub push_url: String,
/// repo owner
#[structopt(env="DRONE_REPO_OWNER")]
#[structopt(long, env = "DRONE_REPO_OWNER")]
pub owner: String,
/// repo name
#[structopt(env="DRONE_REPO_NAME")]
#[structopt(long, env = "DRONE_REPO_NAME")]
pub repo: String,
/// auth username
#[structopt(env="PLUGIN_AUTH_USERNAME")]
pub auth_user: String,
/// Gitea server
#[structopt(env="PLUGIN_GITEA_SERVER")]
pub server: String,
/// Gitea token
#[structopt(env="PLUGIN_GITEA_TOKEN")]
pub token: String,
/// CHANGELOG path
#[structopt(env="PLUGIN_CHANGELOG_PATH", default_value="./CHANGELOG.md")]
pub changelog_path: PathBuf,
/// branch
#[structopt(env="DRONE_REPO_BRANCH")]
#[structopt(long, env = "DRONE_REPO_BRANCH")]
pub branch: String,
// Given by the user
/// auth username
#[structopt(long, env = "PLUGIN_AUTH_USERNAME")]
pub auth_user: String,
/// Gitea server
#[structopt(long, env = "PLUGIN_GITEA_SERVER")]
pub server: String,
/// Gitea token
#[structopt(long, env = "PLUGIN_GITEA_TOKEN")]
pub token: String,
/// CHANGELOG path
#[structopt(long, env = "PLUGIN_CHANGELOG_PATH", default_value = "./CHANGELOG.md")]
pub changelog_path: PathBuf,
/// Default branch name
#[structopt(long, env = "PLUGIN_DEFAULT_BRANCH")]
pub default_branch: Option<String>,
/// Git signature email
#[structopt(long, short = "E", env = "PLUGIN_SIGNATURE_EMAIL", default_value = "domo@tulpa.dev")]
pub email: String,
/// Git signature username
#[structopt(long, short = "U", env = "PLUGIN_SIGNATURE_NAME", default_value = "Domo Arigato")]
pub username: String,
}
impl Into<Common> for DroneEnv {
@ -64,12 +92,14 @@ impl Into<Common> for DroneEnv {
auth_user: self.auth_user,
owner: self.owner,
repo: self.repo,
email: self.email,
username: self.username,
}
}
}
#[derive(StructOpt, Debug)]
pub(crate) struct ReleaseMeta {
pub struct ReleaseMeta {
/// Release name
#[structopt(short, long)]
pub name: Option<String>,
@ -83,54 +113,10 @@ pub(crate) struct ReleaseMeta {
#[derive(StructOpt, Debug)]
#[structopt(about = "Gitea release assistant")]
pub(crate) enum Cmd {
/// Delete a given release from Gitea
Delete {
#[structopt(flatten)]
common: Common,
/// The version tag to operate on
#[structopt(short, long)]
tag: String,
},
/// Downloads release artifacts
Download {
#[structopt(flatten)]
common: Common,
/// File to download
fname: Option<PathBuf>,
/// The version tag to operate on
#[structopt(short, long)]
tag: String,
},
/// Runs the release process as a drone plugin
DronePlugin {
#[structopt(flatten)]
env: DroneEnv,
},
/// Edits a release's description, name and other flags
Edit {
#[structopt(flatten)]
common: Common,
/// Release description
#[structopt(short, long)]
description: Option<String>,
#[structopt(flatten)]
release_meta: ReleaseMeta,
/// The version tag to operate on
tag: String,
},
/// Gets release info
Info {
#[structopt(flatten)]
common: Common,
#[structopt(long, short)]
json: bool,
/// The version tag to operate on
#[structopt(short, long)]
tag: Option<String>,
},
pub enum Cmd {
/// Create a new tag and release on Gitea
Release {
#[structopt(alias = "release")]
Cut {
#[structopt(flatten)]
common: Common,
/// Changelog file to read from to create the release description
@ -141,14 +127,10 @@ pub(crate) enum Cmd {
#[structopt(flatten)]
release_meta: ReleaseMeta,
},
/// Uploads release artifacts to Gitea
Upload {
/// Runs the release process as a drone plugin
DronePlugin {
#[structopt(flatten)]
common: Common,
/// The version tag to operate on
#[structopt(short, long)]
tag: String,
/// The location of the file on the disk
fname: PathBuf,
env: DroneEnv,
},
}

View File

@ -1,45 +0,0 @@
use crate::{gitea::*, *};
use anyhow::Result;
use std::path::PathBuf;
pub(crate) async fn run(
common: Common,
fname: PathBuf,
tag: Option<String>,
rm: ReleaseMeta,
) -> Result<()> {
let repo = git2::Repository::open(".")?;
let tag = tag.unwrap_or(version::read_version("VERSION".into())?);
let desc = changelog::read(fname.clone(), tag.clone())?;
if !git::has_tag(&repo, tag.clone())? {
git::tag_version(&repo, tag.clone(), desc.clone())?;
let _ = git::push_tags(&repo);
}
let desc = changelog::read(fname, tag.clone())?;
let cli = client(&common)?;
let cr = CreateRelease {
body: desc,
draft: rm.draft,
name: rm.name.or(Some(format!("Version {}", tag))).unwrap(),
prerelease: rm.pre_release,
tag_name: tag.clone(),
target_commitish: "HEAD".into(),
};
let resp = cli
.post(&format!(
"{}/api/v1/repos/{}/{}/releases",
common.server, common.owner, common.repo
))
.json(&cr)
.send()
.await?;
if !resp.status().is_success() {
return Err(anyhow!("{:?} -> {}", resp.status(), resp.text().await?));
}
Ok(())
}

View File

@ -1,32 +0,0 @@
use crate::{gitea::*, *};
use anyhow::Result;
use reqwest::multipart;
use std::fs::File;
use std::io::Read;
pub(crate) async fn run(common: Common, fname: PathBuf, tag: String) -> Result<()> {
let cli = client(&common)?;
let bytes = {
let mut fin = File::open(&fname)?;
let mut buffer = Vec::new();
fin.read_to_end(&mut buffer)?;
buffer
};
let form = multipart::Form::new().part("attachment", multipart::Part::bytes(bytes));
let release =
get_release_by_tag(&cli, &common.server, &common.owner, &common.repo, &tag).await?;
cli.post(
format!(
"{}/api/v1/repos/{}/{}/releases/{}",
&common.server, &common.owner, &common.repo, release.id,
)
.as_str(),
)
.query(&[("name", fname)])
.multipart(form)
.send()
.await?;
Ok(())
}

View File

@ -1,22 +1,67 @@
use anyhow::Result;
use git2::{Repository, Signature};
use git2::{
Cred, CredentialType, Direction, FetchOptions, PushOptions, RemoteCallbacks, Repository,
Signature,
};
use std::path::Path;
pub(crate) fn push_tags(repo: &Repository) -> Result<()> {
pub const TAGS: &'static [&'static str] = &["refs/tags/*:refs/tags/*"];
pub fn pull(repo: &Repository, token: String, what: &[&str]) -> Result<()> {
let mut remote = repo.find_remote("origin")?;
remote.connect(git2::Direction::Push)?;
remote.push(&["refs/tags/*:refs/tags/*"], None)?;
let mut fo = FetchOptions::new();
remote.connect_auth(Direction::Fetch, Some(callbacks(token.clone())), None)?;
fo.remote_callbacks(callbacks(token));
remote.fetch(what, Some(&mut fo), None)?;
Ok(())
}
pub(crate) fn tag_version(repo: &Repository, tag: String, desc: String) -> Result<()> {
let sig = &Signature::now("Gitea Release Tool", "gitea-release@tulpa.dev")?;
fn callbacks<'a>(token: String) -> RemoteCallbacks<'a> {
let mut callbacks = RemoteCallbacks::new();
callbacks.credentials(move |_u, _username, allowed_types| {
if allowed_types.contains(CredentialType::SSH_KEY) {
let user = "git";
Cred::ssh_key_from_agent(user)
} else {
Cred::userpass_plaintext(&token, "x-oauth-basic")
}
});
callbacks
}
pub fn push(repo: &Repository, token: String, what: &[&str]) -> Result<()> {
let mut remote = repo.find_remote("origin")?;
remote.connect_auth(Direction::Push, Some(callbacks(token.clone())), None)?;
let mut po = PushOptions::new();
po.remote_callbacks(callbacks(token));
remote.push(what, Some(&mut po))?;
Ok(())
}
pub fn commit(repo: &Repository, sig: &Signature, msg: &str, files: &[&str]) -> Result<()> {
let mut index = repo.index()?;
for file in files {
index.add_path(Path::new(file))?;
}
let oid = index.write_tree()?;
let tree = repo.find_tree(oid)?;
repo.commit(Some("HEAD"), &sig, &sig, &msg, &tree, &[])?;
Ok(())
}
pub fn tag_version(repo: &Repository, sig: &Signature, tag: String, desc: String) -> Result<()> {
let obj = repo.revparse_single("HEAD")?;
repo.tag(&tag, &obj, &sig, &desc, false)?;
Ok(())
}
pub(crate) fn has_tag(repo: &Repository, tag: String) -> Result<bool> {
pub fn has_tag(repo: &Repository, tag: String) -> Result<bool> {
let tags = repo.tag_names(Some(&tag))?;
for tag_obj in tags.iter() {
@ -25,6 +70,7 @@ pub(crate) fn has_tag(repo: &Repository, tag: String) -> Result<bool> {
}
let tag_name = tag_obj.unwrap();
log::debug!("tag: {}", tag_name.to_string());
if tag == tag_name.to_string() {
return Ok(true);
}
@ -37,7 +83,7 @@ pub(crate) fn has_tag(repo: &Repository, tag: String) -> Result<bool> {
mod tests {
use anyhow::Result;
use git2::*;
use std::{fs::File, io::Write, path::Path};
use std::{fs::File, io::Write};
use tempfile::tempdir;
#[test]
@ -48,22 +94,10 @@ mod tests {
let mut fout = File::create(&dir.path().join("VERSION"))?;
write!(fout, "{}", TAG)?;
drop(fout);
let mut index = repo.index()?;
index.add_path(Path::new("VERSION"))?;
let oid = index.write_tree()?;
let tree = repo.find_tree(oid)?;
let sig = &Signature::now("Gitea Release Tool", "gitea-release@tulpa.dev")?;
repo.commit(
Some("HEAD"),
&sig,
&sig,
"test commit please ignore",
&tree,
&[],
)?;
super::tag_version(&repo, TAG.into(), format!("version {}", TAG))?;
super::commit(&repo, &sig, "test commit please ignore", &["VERSION"])?;
super::tag_version(&repo, &sig, TAG.into(), format!("version {}", TAG))?;
assert!(super::has_tag(&repo, TAG.into())?);
Ok(())

View File

@ -1,137 +0,0 @@
use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Author {
pub id: i64,
pub login: String,
pub full_name: String,
pub email: String,
pub avatar_url: String,
pub language: String,
pub is_admin: bool,
pub last_login: String,
pub created: String,
pub username: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Release {
pub id: i64,
pub tag_name: String,
pub target_commitish: String,
pub name: String,
pub body: String,
pub url: String,
pub tarball_url: String,
pub zipball_url: String,
pub draft: bool,
pub prerelease: bool,
pub created_at: String,
pub published_at: String,
pub author: Author,
pub assets: Vec<Attachment>,
}
use cli_table::{Cell, Row};
impl Release {
pub fn row(&self) -> Row {
Row::new(vec![
Cell::new(&format!("{}", self.id), Default::default()),
Cell::new(&self.tag_name, Default::default()),
Cell::new(&self.created_at, Default::default()),
Cell::new(&self.target_commitish, Default::default()),
Cell::new(&self.author.username, Default::default()),
Cell::new(&self.name, Default::default()),
])
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CreateRelease {
pub body: String,
pub draft: bool,
pub name: String,
pub prerelease: bool,
pub tag_name: String,
pub target_commitish: String,
}
pub(crate) async fn get_release_by_tag(
cli: &reqwest::Client,
server: &String,
owner: &String,
repo: &String,
tag: &String,
) -> Result<Release> {
let releases: Vec<Release> = cli
.get(&format!(
"{}/api/v1/repos/{}/{}/releases",
server, owner, repo
))
.send()
.await?
.json()
.await?;
let mut release: Option<Release> = None;
for rls in releases {
if *tag == rls.tag_name {
release = Some(rls);
}
}
if release.is_none() {
return Err(anyhow!("tag {} not found", tag));
}
Ok(release.unwrap())
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Attachment {
pub id: i64,
pub name: String,
pub size: i64,
pub download_count: i64,
pub created_at: String,
pub uuid: String,
pub browser_download_url: String,
}
impl Attachment {
pub fn row(&self) -> Row {
let size = {
let bytes = byte_unit::Byte::from_bytes(self.size as u128);
let unit = bytes.get_appropriate_unit(false);
unit.to_string()
};
Row::new(vec![
Cell::new(&self.name, Default::default()),
Cell::new(&size, Default::default()),
Cell::new(&self.browser_download_url, Default::default()),
])
}
}
pub(crate) async fn get_attachments_for_release(
cli: &reqwest::Client,
server: &String,
owner: &String,
repo: &String,
id: &i64,
) -> Result<Vec<Attachment>> {
let attachments: Vec<Attachment> = cli
.get(&format!(
"{}/api/v1/repos/{}/{}/releases/{}/assets",
server, owner, repo, id
))
.send()
.await?
.json()
.await?;
Ok(attachments)
}

8
src/lib.rs Normal file
View File

@ -0,0 +1,8 @@
pub mod changelog;
pub mod cmd;
pub mod git;
pub mod version;
// Name your user agent after your app?
pub static APP_USER_AGENT: &str =
concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));

View File

@ -1,52 +1,21 @@
use anyhow::{anyhow, Result};
use reqwest::{header, Client};
use std::path::PathBuf;
use anyhow::Result;
use structopt::StructOpt;
mod changelog;
mod cmd;
mod git;
mod gitea;
mod version;
pub(crate) use cmd::*;
// Name your user agent after your app?
static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
pub(crate) fn client(c: &cmd::Common) -> Result<Client> {
let mut headers = header::HeaderMap::new();
let auth = format!("token {}", &c.token);
let auth = auth.as_str();
headers.insert(header::AUTHORIZATION, header::HeaderValue::from_str(auth)?);
Ok(Client::builder()
.user_agent(APP_USER_AGENT)
.default_headers(headers)
.build()?)
}
use ::gitea_release::{cmd::{self, Cmd}};
#[tokio::main]
async fn main() -> Result<()> {
let _ = kankyo::init();
let cmd = cmd::Cmd::from_args();
pretty_env_logger::init();
let cmd = Cmd::from_args();
match cmd {
Cmd::Delete { common, tag } => cmd::delete::run(common, tag).await,
Cmd::Download { common, fname, tag } => cmd::download::run(common, fname, tag).await,
Cmd::DronePlugin { env } => cmd::drone_plugin::run(env).await,
Cmd::Edit {
common,
description,
release_meta,
tag,
} => cmd::edit::run(common, description, release_meta, tag).await,
Cmd::Info { common, json, tag } => cmd::info::run(common, json, tag).await,
Cmd::Release {
Cmd::Cut {
common,
changelog,
tag,
release_meta,
} => cmd::release::run(common, changelog, tag, release_meta).await,
Cmd::Upload { common, fname, tag } => cmd::upload::run(common, fname, tag).await,
} => cmd::cut::run(common, changelog, tag, release_meta).await,
Cmd::DronePlugin { env } => cmd::drone_plugin::run(env).await,
}
}

View File

@ -1,16 +0,0 @@
use anyhow::Result;
use std::{fs, path::PathBuf};
pub(crate) fn read_version(fname: PathBuf) -> Result<String> {
let version = fs::read_to_string(fname)?;
Ok(version.trim().into())
}
#[cfg(test)]
mod tests {
#[test]
fn read_version() {
let version = super::read_version("./testdata/VERSION".into()).unwrap();
assert_eq!(version, "0.1.0");
}
}

53
src/version/cargo.rs Normal file
View File

@ -0,0 +1,53 @@
use anyhow::Result;
use cargo_toml::Manifest;
use std::fs::{self, File};
use std::io::Read;
fn get_file_as_byte_vec(filename: &str) -> Option<Vec<u8>> {
let f = File::open(&filename);
if f.is_err() {
log::debug!("can't read from Cargo.toml: {:?}", f.unwrap_err());
return None;
}
let mut f = f.unwrap();
let metadata = fs::metadata(&filename).expect("unable to read metadata");
let mut buffer = vec![0; metadata.len() as usize];
f.read(&mut buffer).expect("buffer overflow");
Some(buffer)
}
pub fn read() -> Result<Option<String>> {
log::debug!("reading version from Cargo.toml");
let bytes = get_file_as_byte_vec("Cargo.toml");
log::debug!("{:?}", bytes);
match bytes {
Some(bytes) => {
log::trace!("reading toml");
let pkg : Result<Manifest, _> = toml::from_slice(&bytes);
match pkg {
Err(why) => {
log::error!("error parsing Cargo.toml: {:?}", why);
Err(why.into())
}
Ok(pkg) => {
let version = pkg.package.unwrap().version;
log::trace!("got version {}", version);
Ok(Some(version))
}
}
}
None => Ok(None)
}
}
#[cfg(test)]
mod tests {
#[test]
fn read() {
use super::read;
let _ = pretty_env_logger::try_init();
read().unwrap().unwrap();
}
}

30
src/version/mod.rs Normal file
View File

@ -0,0 +1,30 @@
use anyhow::Result;
use std::{fs, path::PathBuf};
mod cargo;
pub(crate) fn read(fname: PathBuf) -> Result<String> {
let version = match read_fs(fname.clone()) {
Ok(version) => version,
Err(why) => {
log::debug!("can't read {:?}: {:?}", fname, why);
cargo::read().unwrap().unwrap()
}
};
Ok(version)
}
fn read_fs(fname: PathBuf) -> Result<String> {
log::debug!("reading version data from {:?}", fname);
Ok(fs::read_to_string(fname)?.trim().into())
}
#[cfg(test)]
mod tests {
#[test]
fn read_version() {
let _ = pretty_env_logger::try_init();
let version = super::read_fs("./testdata/VERSION".into()).unwrap();
assert_eq!(version, "0.1.0");
}
}

4
testdata/brackets.md vendored Normal file
View File

@ -0,0 +1,4 @@
## [0.1.0]
Hi there this is a test!
### ADDED
- something

96
tests/cut.rs Normal file
View File

@ -0,0 +1,96 @@
use ::gitea_release::{cmd, git, APP_USER_AGENT};
use anyhow::Result;
use git2::{Repository, Signature};
use log::debug;
use std::{fs::File, io::Write};
const TAG: &'static str = "0.1.0";
#[tokio::test]
async fn cut() -> Result<()> {
let _ = pretty_env_logger::try_init();
let name = elfs::next();
let token = std::env::var("DOMO_GITEA_TOKEN").expect("wanted envvar DOMO_GITEA_TOKEN");
let cli = gitea::Client::new("https://tulpa.dev".into(), token.clone(), APP_USER_AGENT)?;
debug!("created gitea client");
let gitea_repo = cli
.create_user_repo(gitea::CreateRepo {
auto_init: false,
description: format!("https://tulpa.dev/cadey/gitea-release test repo"),
gitignores: "".into(),
issue_labels: "".into(),
license: "".into(),
name: name.clone(),
private: true,
readme: "".into(),
})
.await?;
debug!("created repo domo/{}", name);
let dir = tempfile::tempdir()?;
let repo = Repository::init(&dir)?;
let sig = &Signature::now("Domo Arigato", "domo@tulpa.dev")?;
debug!("initialized repo in {:?}", dir.path());
let mut fout = File::create(&dir.path().join("VERSION"))?;
write!(fout, "{}", TAG)?;
drop(fout);
let mut fout = File::create(&dir.path().join("CHANGELOG.md"))?;
fout.write_all(include_bytes!("../testdata/basic.md"))?;
drop(fout);
git::commit(&repo, &sig, TAG.into(), &["VERSION", "CHANGELOG.md"])?;
debug!("committed files");
repo.remote("origin", &gitea_repo.clone_url)?;
debug!("set up origin remote to {}", gitea_repo.clone_url);
git::push(
&repo,
token.clone(),
&["refs/heads/master:refs/heads/master"],
)?;
debug!("pushed to {} with token {}", gitea_repo.clone_url, token);
std::env::set_current_dir(dir.path())?;
cmd::cut::run(
cmd::Common {
server: "https://tulpa.dev".into(),
token: token,
auth_user: gitea_repo.owner.login.clone(),
owner: gitea_repo.owner.login.clone(),
repo: gitea_repo.name,
email: "domo@tulpa.dev".into(),
username: "Domo Arigato".into(),
},
"CHANGELOG.md".into(),
None,
cmd::ReleaseMeta {
name: None,
draft: false,
pre_release: false,
},
)
.await?;
let rls = cli
.get_release_by_tag(
gitea_repo.owner.login.clone(),
name.clone(),
format!("v{}", TAG),
)
.await?;
assert_eq!(
rls.body,
"Hi there this is a test\\!\n### ADDED\n - something\n"
);
cli.delete_repo(gitea_repo.owner.login, name).await?;
debug!("deleted repo {}", gitea_repo.clone_url);
Ok(())
}