generated from PlexSheep/rs-base
Compare commits
1 Commits
master
...
v0.1.0-pre
Author | SHA1 | Date |
---|---|---|
Christoph J. Scherr | 64b8429e84 |
|
@ -1,30 +0,0 @@
|
||||||
version:
|
|
||||||
!cargo
|
|
||||||
changelog:
|
|
||||||
enable: true
|
|
||||||
git-log: true
|
|
||||||
|
|
||||||
uses:
|
|
||||||
cargo:
|
|
||||||
publish: true
|
|
||||||
# tokens are loaded from ~/.cargo/config.toml
|
|
||||||
registries:
|
|
||||||
- crates.io
|
|
||||||
- cscherr
|
|
||||||
|
|
||||||
api:
|
|
||||||
github:
|
|
||||||
type: github
|
|
||||||
repository: autocrate
|
|
||||||
auth:
|
|
||||||
user: PlexSheep
|
|
||||||
pass:
|
|
||||||
!env TOKEN_GH
|
|
||||||
cscherr:
|
|
||||||
type: forgejo
|
|
||||||
endpoint: https://git.cscherr.de
|
|
||||||
repository: autocrate
|
|
||||||
auth:
|
|
||||||
user: PlexSheep
|
|
||||||
pass:
|
|
||||||
!env TOKEN_CSCHERR
|
|
|
@ -6,7 +6,7 @@ on:
|
||||||
# - '!master'
|
# - '!master'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
format:
|
CI:
|
||||||
name: cargo CI
|
name: cargo CI
|
||||||
permissions:
|
permissions:
|
||||||
# Give the default GITHUB_TOKEN write permission to commit and push the
|
# Give the default GITHUB_TOKEN write permission to commit and push the
|
||||||
|
@ -16,11 +16,9 @@ jobs:
|
||||||
- name: get repo
|
- name: get repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: install rust
|
- name: install rust
|
||||||
uses: https://github.com/dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
- name: install additional rust things
|
- name: install additional rust things
|
||||||
run: |
|
run: rustup component add rustfmt
|
||||||
rustup component add rustfmt
|
|
||||||
rustup component add clippy
|
|
||||||
- name: config custom registry
|
- name: config custom registry
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.cargo/
|
mkdir -p ~/.cargo/
|
||||||
|
@ -30,16 +28,16 @@ jobs:
|
||||||
echo '[registries.cscherr]' >> ~/.cargo/config.toml
|
echo '[registries.cscherr]' >> ~/.cargo/config.toml
|
||||||
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
|
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
|
||||||
cat ~/.cargo/config.toml
|
cat ~/.cargo/config.toml
|
||||||
- name: cargo clippy check
|
- name: cargo check
|
||||||
run: cargo clippy --all-features --all-targets --workspace
|
run: cargo check --all-features --all-targets
|
||||||
- name: cargo clippy fix
|
- name: cargo fix
|
||||||
run: cargo clippy --fix --all-features --all-targets --workspace
|
run: cargo fix --all-features --all-targets
|
||||||
- name: cargo fmt
|
- name: cargo fmt
|
||||||
run: cargo fmt --all
|
run: cargo fmt --all
|
||||||
- name: cargo test
|
- name: cargo test
|
||||||
run: cargo test --all-features --all-targets --workspace
|
run: cargo test --all-features --all-targets
|
||||||
- name: commit back to repository
|
- name: commit back to repository
|
||||||
uses: https://github.com/stefanzweifel/git-auto-commit-action@v5
|
uses: stefanzweifel/git-auto-commit-action@v5
|
||||||
with:
|
with:
|
||||||
# Optional. Commit message for the created commit.
|
# Optional. Commit message for the created commit.
|
||||||
# Defaults to "Apply automatic changes"
|
# Defaults to "Apply automatic changes"
|
||||||
|
|
|
@ -19,9 +19,7 @@ jobs:
|
||||||
- name: install rust
|
- name: install rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
- name: install additional rust things
|
- name: install additional rust things
|
||||||
run: |
|
run: rustup component add rustfmt
|
||||||
rustup component add rustfmt
|
|
||||||
rustup component add clippy
|
|
||||||
- name: config custom registry
|
- name: config custom registry
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.cargo/
|
mkdir -p ~/.cargo/
|
||||||
|
@ -31,14 +29,14 @@ jobs:
|
||||||
echo '[registries.cscherr]' >> ~/.cargo/config.toml
|
echo '[registries.cscherr]' >> ~/.cargo/config.toml
|
||||||
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
|
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
|
||||||
cat ~/.cargo/config.toml
|
cat ~/.cargo/config.toml
|
||||||
- name: cargo clippy check
|
- name: cargo check
|
||||||
run: cargo clippy --all-features --all-targets --workspace
|
run: cargo check --all-features --all-targets
|
||||||
- name: cargo clippy fix
|
- name: cargo fix
|
||||||
run: cargo clippy --fix --all-features --all-targets --workspace
|
run: cargo fix --all-features --all-targets
|
||||||
- name: cargo fmt
|
- name: cargo fmt
|
||||||
run: cargo fmt --all
|
run: cargo fmt --all
|
||||||
- name: cargo test
|
- name: cargo test
|
||||||
run: cargo test --all-features --all-targets --workspace
|
run: cargo test --all-features --all-targets
|
||||||
- name: commit back to repository
|
- name: commit back to repository
|
||||||
uses: stefanzweifel/git-auto-commit-action@v5
|
uses: stefanzweifel/git-auto-commit-action@v5
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -19,4 +19,3 @@ Cargo.lock
|
||||||
# Added by cargo
|
# Added by cargo
|
||||||
|
|
||||||
/target
|
/target
|
||||||
.env
|
|
||||||
|
|
39
Cargo.toml
39
Cargo.toml
|
@ -1,48 +1,33 @@
|
||||||
[package]
|
[package]
|
||||||
name = "autocrate"
|
name = "autocrate"
|
||||||
version = "0.1.0-prealpha.5"
|
version = "0.1.0-prealpha.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = true
|
publish = false
|
||||||
authors = ["Christoph J. Scherr <software@cscherr.de>"]
|
authors = ["Christoph J. Scherr <software@cscherr.de>"]
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3"
|
||||||
description = "Release Manager for Your Projects on Gitea, GitHub, and GitLab"
|
description = "Release Manager for Your Projects on Gitea, GitHub, and GitLab"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
homepage = "https://git.cscherr.de/PlexSheep/autocrate"
|
homepage = "https://git.cscherr.de/PlexSheep/autocrate"
|
||||||
repository = "https://git.cscherr.de/PlexSheep/autocrate"
|
repository = "https://git.cscherr.de/PlexSheep/autocrate"
|
||||||
categories = ["command-line-utilities", "development-tools"]
|
categories = ["command-line-utilities", "development-tools"]
|
||||||
keywords = [
|
keywords = [
|
||||||
|
"ci",
|
||||||
|
"cd",
|
||||||
|
"continuous-integration",
|
||||||
"continuous-delivery",
|
"continuous-delivery",
|
||||||
"workflow-automation",
|
"workflow-automation",
|
||||||
|
"build-tool",
|
||||||
|
"version-control",
|
||||||
"gitea",
|
"gitea",
|
||||||
|
"github",
|
||||||
|
"gitlab",
|
||||||
"changelog",
|
"changelog",
|
||||||
|
"release-notes",
|
||||||
"automated-builds",
|
"automated-builds",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.79"
|
libpt = { version = "0.3.11", features = ["log"] }
|
||||||
async-trait = "0.1.77"
|
|
||||||
# cargo = "0.76.0"
|
|
||||||
clap = { version = "4.4.18", features = ["derive", "help"] }
|
|
||||||
clap-verbosity-flag = "2.1.2"
|
|
||||||
forgejo-api = "0.1.0"
|
|
||||||
futures = "0.3.30"
|
|
||||||
git2 = "0.18.1"
|
|
||||||
libpt = { version = "0.4.2", features = ["log"] }
|
|
||||||
octocrab = "0.38.0"
|
|
||||||
reqwest = "0.11.24"
|
|
||||||
serde = { version = "1.0.195", features = ["derive"] }
|
serde = { version = "1.0.195", features = ["derive"] }
|
||||||
serde_json = "1.0.116"
|
|
||||||
serde_yaml = "0.9.30"
|
serde_yaml = "0.9.30"
|
||||||
tempfile = "3.9.0"
|
|
||||||
thiserror = "1.0.56"
|
|
||||||
tokio = { version = "1.36.0", features = ["tokio-macros", "rt-multi-thread", "macros"] }
|
|
||||||
url = { version = "2.5.0", features = ["serde"] }
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "autocrate"
|
|
||||||
path = "src/main.rs"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "autocrate"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
150
README.md
150
README.md
|
@ -8,65 +8,46 @@
|
||||||
|
|
||||||
![logo](data/media/autocrate.jpeg)
|
![logo](data/media/autocrate.jpeg)
|
||||||
|
|
||||||
|
**Disclaimer**: I've generated the Readme and logo with the help of so called AI
|
||||||
|
tools and modified them afterwards.
|
||||||
|
|
||||||
Autocrate simplifies the creation and maintenance of releases for your Rust
|
Autocrate simplifies the creation and maintenance of releases for your Rust
|
||||||
projects hosted on fancy git servers. By providing functionalities
|
projects hosted on Gitea servers. By providing essential functionalities
|
||||||
like creating releases uploading artifacts, publishing crates, and managing changelogs,
|
like uploading artifacts, publishing crates, and managing changelogs,
|
||||||
Autocrate tries to streamline the release process. Although initially built for Forgejo,
|
Autocrate streamlines the release process, allowing developers to focus on
|
||||||
I plan to extend support to other platforms such as GitHub and GitLab.
|
their work. Although initially built for Gitea, we plan to extend support
|
||||||
|
for additional platforms such as GitHub and GitLab.
|
||||||
|
|
||||||
Autocrate can then be used in CI/CD, or in projects without
|
|
||||||
continuous integration to release software.
|
|
||||||
|
|
||||||
The software is built in Rust, and offers integration for Rust Projects with Cargo.
|
|
||||||
In the future, using other tools and specifying custom scripts will become possible.
|
|
||||||
|
|
||||||
* [Original Repository](https://git.cscherr.de/PlexSheep/Autocrate)
|
* [Original Repository](https://git.cscherr.de/PlexSheep/Autocrate)
|
||||||
* [Codeberg Mirror](https://codeberg.org/PlexSheep/autocrate)
|
|
||||||
* [GitHub Mirror](https://github.com/PlexSheep/Autocrate)
|
* [GitHub Mirror](https://github.com/PlexSheep/Autocrate)
|
||||||
* [crates.io](https://crates.io/crates/autocrate)
|
|
||||||
* [docs.rs](https://docs.rs/crate/autocrate/)
|
|
||||||
|
|
||||||
Take a look at the [scripts](./scripts) directory! [publish.sh](scripts/publish.sh)
|
Take a look at the [scripts](./scripts) directory! [publish.sh](scripts/publish.sh)
|
||||||
and [release.sh](scripts/release.sh) are what I'm trying to get rid of.
|
and [release.sh](scripts/release.sh) are exactly what I'm trying to get rid of.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Create and update releases on your Git platform
|
* Create and update releases on your Gitea server
|
||||||
* Publish crates to crates.io or other repositories
|
* Publish crates to Cargo.rs
|
||||||
* Upload artifacts, including binaries and signatures alongside your releases
|
or other repositories directly from your Rust projects
|
||||||
* Generate changelogs and release notes
|
* Upload artifacts, including documentation and binaries, alongside your releases
|
||||||
* Configure with a simple yaml file
|
* Generate and maintain changelogs and release notes.
|
||||||
|
|
||||||
### Upcoming Features
|
### Upcoming Features
|
||||||
|
|
||||||
Autocrate is still in pre-alpha, so the features listed above are still being
|
My goal is to continuously enhance Autocrate to better serve the developer
|
||||||
worked on. For the future, the following Features are planned:
|
community. Some planned improvements include supporting other popular hosting
|
||||||
|
platforms, enabling even greater flexibility and convenience.
|
||||||
* Support for platforms other than Forgejo
|
|
||||||
* Custom artifact build scripts
|
|
||||||
* Version bumping
|
|
||||||
* Interactive and scriptable CLI interface
|
|
||||||
* Publish a cargo workspace (that depends on it's own crates)
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
Before getting started with Autocrate, make sure you have the necessary
|
Before getting started with Autocrate, make sure you have the necessary
|
||||||
prerequisites covered:
|
prerequisites covered:
|
||||||
|
|
||||||
You can use `autocrate init` to set your workspace up with a basic
|
* **A Rust Environment**: Install the latest stable Rust compiler and
|
||||||
`.autocrate.yaml`.
|
associated tools via the official website: <https://www.rust-lang.org/>
|
||||||
|
* **Access to a Gitea Server** (such as [git.cscherr.de](https://git.cscherr.de)
|
||||||
* **Access to a supported Git Server** (such as [git.cscherr.de](https://git.cscherr.de)
|
and [codeberg.org](https://codeberg.org) (strictly speaking, uses a Gitea fork)
|
||||||
and [codeberg.org](https://codeberg.org))
|
|
||||||
|
|
||||||
### Pre-requisites
|
|
||||||
|
|
||||||
* Git
|
|
||||||
|
|
||||||
#### If you want to compile it yourself
|
|
||||||
|
|
||||||
Install Rust, the officially recommended way is through [rustup.rs](https://rustup.rs/).
|
|
||||||
Your distribution may offer a Rust distribution in your package manager as an alternative
|
|
||||||
|
|
||||||
### Installing
|
### Installing
|
||||||
|
|
||||||
|
@ -83,68 +64,45 @@ locally, making it readily accessible through your command line interfaces.
|
||||||
Create a YAML file named `.autocrate.yml` (or `.yaml`) in the root of your Git
|
Create a YAML file named `.autocrate.yml` (or `.yaml`) in the root of your Git
|
||||||
repository. It should contain the following parameters (replace the placeholders):
|
repository. It should contain the following parameters (replace the placeholders):
|
||||||
|
|
||||||
| Parent | Key | Value | Explanation |
|
| Parent | Key | Value | Explanation |
|
||||||
|----------------------|--------------|----------------------------------------------------------------------------------|------------------------------------------------------------------------------|
|
|-----------------|--------------|----------------------------------------------------------------------------------|------------------------------------------------------------------------------|
|
||||||
| (root) | `changelog` | list of keys with this as parent (`git-log` etc) | information on how a changelog is generated |
|
| (root) | `changelog` | list of keys with this as parent (`git-log` etc) | information on how a changelog is generated |
|
||||||
| `changelog` | `enable` | `true`/`false` | If false, no changelog will be generated |
|
| `changelog` | `enable` | `true`/`false` | If false, no changelog will be generated |
|
||||||
| `changelog` | `git-log` | `true`/`false` | should a changelog be generated with `git log`? |
|
| `changelog` | `git-log` | `true`/`false` | should a changelog be generated with `git log`? |
|
||||||
| (root) | `uses` | list of keys with this as parent (`cargo` etc) | Marks features to be used by Autocrate |
|
| (root) | `uses` | list of keys with this as parent (`cargo` etc) | Marks features to be used by Autocrate |
|
||||||
| `uses` | `cargo` | list of keys with this as parent (`publish` etc) | tells us that your project uses cargo |
|
| `uses` | `cargo` | list of keys with this as parent (`publish` etc) | tells us that your project uses cargo |
|
||||||
| `cargo` | `publish` | `true`/`false` | should we publish crates? |
|
| `cargo` | `publish` | `true`/`false` | should we publish crates? |
|
||||||
| `cargo` | `registries` | registries see [this](https://doc.rust-lang.org/cargo/reference/registries.html) | A list of registries we should publish to. If empty defaults to `crates.io`. |
|
| `cargo` | `registries` | registries see [this](https://doc.rust-lang.org/cargo/reference/registries.html) | A list of registries we should publish to. If empty defaults to `crates.io`. |
|
||||||
| (root) | `api` | list of names, which each have the same keys | defines the api we talk to |
|
| (root) | `api` | list of names, which each have the same keys | defines the api we talk to |
|
||||||
| `api.NAME` | `type` | one of `gitea`,`github`,`gitlab` (currently only support for `gitea` | Let's us know which api type we are talking to |
|
| `api.NAME` | `type` | one of `gitea`,`github`,`gitlab` (currently only support for `gitea` | Let's us know which api type we are talking to |
|
||||||
| `api.NAME` | `endpoint` | Base URL of the target server | Let's us know which api type we are talking to |
|
| `api.NAME` | `endpoint` | Base URL of the target server | Let's us know which api type we are talking to |
|
||||||
| `api.NAME` | `auth` | list of keys with this as parent (`user` and `pass`) | We probably need authentication on the target server |
|
| `api.NAME` | `auth` | list of keys with this as parent (`user` and `pass`) | We probably need authentication on the target server |
|
||||||
| `api.NAME.auth` | `user` | a string | Which user should we try to authenticate as |
|
| `api.NAME.auth` | `user` | a string | Which user should we try to authenticate as |
|
||||||
| `api.NAME.auth` | `pass` | contains either of `text`, `env` or `file` | sets the secret for authentication with this server |
|
| `api.NAME.auth` | `pass` | a string | A secret for authentication o the server, probably a token |
|
||||||
| `api.NAME.auth.pass` | `text` | a authentication pass as clear text | A secret for authentication of the server, probably a token |
|
|
||||||
| `api.NAME.auth.pass` | `env` | env var which contains the token | A secret for authentication of the server, probably a token |
|
|
||||||
| `api.NAME.auth.pass` | `file` | file var which contains the token | A secret for authentication of the server, probably a token |
|
|
||||||
|
|
||||||
An example `.autocrate.yaml` could look like this:
|
An example `.autocrate.yaml` could look like this:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
changelog:
|
changelog:
|
||||||
enable: true
|
- enable: true
|
||||||
git-log: true
|
- git-log: true
|
||||||
|
|
||||||
uses:
|
uses:
|
||||||
cargo:
|
- cargo:
|
||||||
publish: true
|
- publish = true
|
||||||
# tokens are loaded from ~/.cargo/config.toml
|
# tokens are loaded from ~/.cargo/config.toml
|
||||||
registries:
|
- registries:
|
||||||
- default
|
- crates.io
|
||||||
- cscherr
|
- example.com
|
||||||
|
|
||||||
api:
|
api:
|
||||||
github:
|
github:
|
||||||
type: github
|
user: myUserName
|
||||||
endpoint: https://github.com
|
pass: token_superimportantsecret
|
||||||
auth:
|
|
||||||
user: PlexSheep
|
|
||||||
pass:
|
|
||||||
text: token_superimportantsecret
|
|
||||||
cscherr:
|
|
||||||
type: gitea
|
|
||||||
endpoint: https://git.cscherr.de
|
|
||||||
auth:
|
|
||||||
user: PlexSheep
|
|
||||||
pass:
|
|
||||||
file: secrettoken.txt
|
|
||||||
```
|
```
|
||||||
|
|
||||||
After Autocrate has been bootstrapped, it will be released and published
|
|
||||||
with itself, so you can take a look at this repositories
|
|
||||||
[`.autocrate.yaml`](./.autocrate.yaml).
|
|
||||||
|
|
||||||
## Using Autocrate
|
## Using Autocrate
|
||||||
|
|
||||||
After you have your workspace with a `.autocrate.yaml` file, you can:
|
TBD
|
||||||
|
|
||||||
* `autocrapte release` to create a release on your git server(s), optionally publishing too
|
|
||||||
* `autocrate publish` to publish your crate to the specified registries(s) (default is crates.io)
|
|
||||||
* `autocrate changelog` to generate a changelog since the last tag
|
|
||||||
|
|
||||||
## Licensing
|
## Licensing
|
||||||
|
|
||||||
|
@ -155,23 +113,13 @@ License. Please refer to [`LICENSE`](./LICENSE) for complete licensing details.
|
||||||
|
|
||||||
## Project status
|
## Project status
|
||||||
|
|
||||||
The project has started recently and is currently in pre-alpha. Many features
|
The project has started recently and is currently in pre-alpha.
|
||||||
are still missing or experimental
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
I'd be very happy to get contributions! Although the master repository is on
|
I'd be very happy to get contributions! Although the master repository is on
|
||||||
my self hosted git server, you're free to create issues, PRs and so on
|
my self hosted git server, you're free to create issues, PRs and so on on
|
||||||
GitHub. If enough activity comes around, moving to GitHub Codeberg might be a
|
GitHub. If enough activity comes around, moving to GitHub might be a good idea.
|
||||||
good idea.
|
|
||||||
|
|
||||||
If you have any questions, use issues and discussions tabs or write me an email
|
If you have any questions, use issues and discussions tabs or write me an email
|
||||||
to [software@cscherr.de](mailto:software@cscherr.de)
|
to [software@cscherr.de](mailto:software@cscherr.de)
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
If you find a security issue with this repository, it would be best if you sent
|
|
||||||
me a mail to [software@cscherr.de](mailto:software@cscherr.de) or reported it on
|
|
||||||
GitHub.
|
|
||||||
|
|
||||||
See [`SECURITY`](SECURITY.md).
|
|
||||||
|
|
14
SECURITY.md
14
SECURITY.md
|
@ -1,14 +0,0 @@
|
||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
Only the latest release is currently supported.
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
It would be best if you reported any found security vulnerabilities on the GitHub mirror.
|
|
||||||
|
|
||||||
If you want to send something encrypted, use [this](https://static.cscherr.de/keys/software@cscherr.de.asc) key.
|
|
||||||
You can always reach me on [software@cscherr.de](mailto:software@cscherr.de), but sadly, I'm struggeling with PGP
|
|
||||||
encryption in my mail client. I use protonmail to host my EMail services, so if you send me something from protonmail,
|
|
||||||
it will be end to end encrypted.
|
|
|
@ -1,11 +1,19 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
cargo check --all-features
|
cargo check --all-features
|
||||||
|
echo ">>>>>>>> SELECT A NEW VERSION"
|
||||||
|
cargo ws version --no-git-commit -- -p autocrate
|
||||||
|
NEW_VERSION=$(cat Cargo.toml | rg '^\s*version\s*=\s*"([^"]*)"\s*$' -or '$1')
|
||||||
|
bash scripts/set_all_versions.sh $NEW_VERSION
|
||||||
|
git add -A
|
||||||
|
git commit -m "Release v$NEW_VERSION" || (echo ">>>>>>>> COMMIT FAILED OR THERE WAS NOTHING TO COMMIT"; sleep 5)
|
||||||
|
echo ">>>>>>>> SKIP!!!!!"
|
||||||
|
cargo ws version --amend -- -p autocrate
|
||||||
echo ">>>>>>>> PUBLISHING RELEASE FOR REPO"
|
echo ">>>>>>>> PUBLISHING RELEASE FOR REPO"
|
||||||
bash scripts/release.sh
|
bash scripts/release.sh
|
||||||
echo ">>>>>>>> PUBLISHING TO CRATES.IO NEXT"
|
echo ">>>>>>>> PUBLISHING TO CRATES.IO NEXT"
|
||||||
sleep 2
|
sleep 10
|
||||||
cargo publish
|
cargo publish
|
||||||
echo ">>>>>>>> PUBLISHING TO CSCHERR.DE NEXT"
|
echo ">>>>>>>> PUBLISHING TO CSCHERR.DE NEXT"
|
||||||
sleep 2
|
sleep 3
|
||||||
cargo publish --registry cscherr
|
cargo publish --registry cscherr
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
TOKEN=$(cat ~/.git-credentials | grep 'git.cscherr.de' | grep -P '(?:)[^:]*(?=@)' -o)
|
TOKEN=$(cat ~/.git-credentials | grep 'git.cscherr.de' | grep -P '(?:)[^:]*(?=@)' -o)
|
||||||
NEW_VERSION=$(cat Cargo.toml | rg '^\s*version\s*=\s*"([^"]*)"\s*$' -or '$1')
|
NEW_VERSION=$(cat Cargo.toml | rg '^\s*version\s*=\s*"([^"]*)"\s*$' -or '$1')
|
||||||
|
VERSION=$(git rev-parse HEAD)
|
||||||
GIT_COMMIT_SHA=$(git rev-parse HEAD)
|
GIT_COMMIT_SHA=$(git rev-parse HEAD)
|
||||||
REPO=${PWD##*/} # name of cwd
|
|
||||||
BODY="
|
BODY="
|
||||||
$(git log $(git describe --tags --abbrev=0)..HEAD --pretty="- %s" --oneline --decorate)
|
$(git log $(git describe --tags --abbrev=0)..HEAD --pretty="- %s" --oneline --decorate)
|
||||||
"
|
"
|
||||||
USER=PlexSheep
|
USER=PlexSheep
|
||||||
git tag "v$NEW_VERSION" || echo "could not tag"
|
git tag "v$NEW_VERSION-test" || echo "could not tag"
|
||||||
curl -X 'POST' \
|
curl -X 'POST' \
|
||||||
'https://git.cscherr.de/api/v1/repos/PlexSheep/'$REPO'/releases' \
|
'https://git.cscherr.de/api/v1/repos/PlexSheep/pt/releases' \
|
||||||
-H 'accept: application/json' \
|
-H 'accept: application/json' \
|
||||||
-H "Authorization: token $TOKEN" \
|
-H "Authorization: token $TOKEN" \
|
||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
export NEW_VER=$1
|
||||||
|
pwd
|
||||||
|
sed -i 's/\(^\s*version\)\s*=\s*"\([^"]*\)"$/\1 = "'$NEW_VER'"/g' Cargo.toml
|
||||||
|
find * -name 'Cargo.toml' -type f \
|
||||||
|
-exec sed -i 's/\(libpt.*version\s*=\s*\)"[^"]*"/\1"'$NEW_VER'"/g' Cargo.toml {} +
|
|
@ -1,66 +0,0 @@
|
||||||
use std::{fmt::Display, process::Command};
|
|
||||||
|
|
||||||
use crate::{config::Config, error::*};
|
|
||||||
|
|
||||||
/// Represents a changelog that is currently under construction.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Changelog {
|
|
||||||
git_log: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Changelog {
|
|
||||||
pub fn build(cfg: &Config) -> Result<Self> {
|
|
||||||
if !cfg.yaml.changelog.enable {
|
|
||||||
return Err(ChangelogError::IsDisabledButUsed.into());
|
|
||||||
}
|
|
||||||
let git_log = Self::make_git_log(cfg)?;
|
|
||||||
Ok(Changelog { git_log })
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: use libgit2 instead of the cli interface
|
|
||||||
fn make_git_log(cfg: &Config) -> Result<Option<String>> {
|
|
||||||
if !cfg.yaml.changelog.enable {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
let mut cmd = Command::new("git");
|
|
||||||
cmd.arg("log")
|
|
||||||
.arg(format!("{}..HEAD", Self::get_last_tag()?,))
|
|
||||||
.arg("--oneline");
|
|
||||||
let out = cmd.output()?;
|
|
||||||
// FIXME: this does not catch fancy colors, those are from the shell as it seems? I don't
|
|
||||||
// get it.
|
|
||||||
let buf = String::from_utf8(out.stdout).map_err(ChangelogError::GitUTF8Error)?;
|
|
||||||
if !out.status.success() {
|
|
||||||
// TODO: get the stderr for error reporting
|
|
||||||
// TODO: Make the error more understandable for the user
|
|
||||||
return Err(ChangelogError::GitBadStatus(out.status, buf).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Some(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_last_tag() -> Result<String> {
|
|
||||||
let mut cmd = Command::new("git");
|
|
||||||
cmd.arg("describe").arg("--tags").arg("--abbrev=0");
|
|
||||||
let out = cmd.output()?;
|
|
||||||
let buf = String::from_utf8(out.stdout).map_err(ChangelogError::GitUTF8Error)?;
|
|
||||||
if !out.status.success() {
|
|
||||||
// TODO: get the stderr for error reporting
|
|
||||||
// TODO: Make the error more understandable for the user
|
|
||||||
return Err(ChangelogError::GitBadStatus(out.status, buf).into());
|
|
||||||
}
|
|
||||||
let buf = buf.replace('\n', "");
|
|
||||||
Ok(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Changelog {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let mut full: String = String::new();
|
|
||||||
full += "Changelog";
|
|
||||||
if self.git_log.is_some() {
|
|
||||||
full += format!("\n\n{}", self.git_log.clone().unwrap()).as_str();
|
|
||||||
}
|
|
||||||
write!(f, "{full}")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use libpt::log::{Level, Logger};
|
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
|
||||||
use clap_verbosity_flag::{InfoLevel, Verbosity};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Parser)]
|
|
||||||
#[command(
|
|
||||||
author,
|
|
||||||
version,
|
|
||||||
about,
|
|
||||||
long_about,
|
|
||||||
help_template = r#"{about-section}
|
|
||||||
{usage-heading} {usage}
|
|
||||||
{all-args}{tab}
|
|
||||||
|
|
||||||
autocrate: {version}
|
|
||||||
Author: {author-with-newline}
|
|
||||||
"#
|
|
||||||
)]
|
|
||||||
/// Release Manager for Your Projects on Gitea, GitHub, and GitLab.
|
|
||||||
pub struct Cli {
|
|
||||||
// clap_verbosity_flag seems to make this a global option implicitly
|
|
||||||
/// set a verbosity, multiple allowed (f.e. -vvv)
|
|
||||||
#[command(flatten)]
|
|
||||||
pub verbose: Verbosity<InfoLevel>,
|
|
||||||
|
|
||||||
/// show additional logging meta data
|
|
||||||
#[arg(long)]
|
|
||||||
pub meta: bool,
|
|
||||||
|
|
||||||
/// the subcommands are part of this enum
|
|
||||||
#[command(subcommand)]
|
|
||||||
pub command: Commands,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Subcommand)]
|
|
||||||
pub enum Commands {
|
|
||||||
Changelog {},
|
|
||||||
/// Create a new release on the server
|
|
||||||
Release {
|
|
||||||
// FIXME: allow taking a message like this:
|
|
||||||
// `autocrate changelog -m arg1 arg2 arg3`
|
|
||||||
// -> msg="arg1 arg2 arg3"
|
|
||||||
// Instead of only
|
|
||||||
// `autocrate changelog -m "arg1 arg2 arg3"`
|
|
||||||
// -> msg="arg1 arg2 arg3"
|
|
||||||
//
|
|
||||||
// TODO:
|
|
||||||
// Perhaps open the $EDITOR of the user if
|
|
||||||
// no message is provided, like git does
|
|
||||||
//
|
|
||||||
// TODO:
|
|
||||||
// find a way to make this a global option but only usable with specific subcommands
|
|
||||||
//
|
|
||||||
// TODO:
|
|
||||||
// integrate a CHANGELOG.md file
|
|
||||||
//
|
|
||||||
/// Message body of the release
|
|
||||||
#[arg(short, long)]
|
|
||||||
message: Option<Vec<String>>,
|
|
||||||
|
|
||||||
/// generate and add a changelog
|
|
||||||
#[arg(short, long)]
|
|
||||||
changelog: bool,
|
|
||||||
|
|
||||||
/// publish after releasing
|
|
||||||
#[arg(short, long)]
|
|
||||||
publish: bool,
|
|
||||||
},
|
|
||||||
/// Publish to a package registry
|
|
||||||
Publish {
|
|
||||||
// see Commands::Release { message }
|
|
||||||
#[arg(short, long)]
|
|
||||||
message: Option<Vec<String>>,
|
|
||||||
},
|
|
||||||
///
|
|
||||||
Version {},
|
|
||||||
Init {},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Commands {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}",
|
|
||||||
match self {
|
|
||||||
Self::Changelog { .. } => "Changelog",
|
|
||||||
Self::Release { .. } => "Release",
|
|
||||||
Self::Publish { .. } => "Publish",
|
|
||||||
Self::Version { .. } => "Version",
|
|
||||||
Self::Init { .. } => "Init",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Cli {
|
|
||||||
pub fn cli_parse() -> Self {
|
|
||||||
let cli = Self::parse();
|
|
||||||
let ll: Level = match cli.verbose.log_level().unwrap().as_str() {
|
|
||||||
"TRACE" => Level::TRACE,
|
|
||||||
"DEBUG" => Level::DEBUG,
|
|
||||||
"INFO" => Level::INFO,
|
|
||||||
"WARN" => Level::WARN,
|
|
||||||
"ERROR" => Level::ERROR,
|
|
||||||
_ => {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if cli.meta {
|
|
||||||
Logger::build(None, Some(ll), true).expect("could not initialize Logger");
|
|
||||||
} else {
|
|
||||||
// less verbose version
|
|
||||||
Logger::build_mini(Some(ll)).expect("could not initialize Logger");
|
|
||||||
}
|
|
||||||
cli
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,340 +0,0 @@
|
||||||
use std::str::FromStr;
|
|
||||||
use std::{
|
|
||||||
collections::HashMap, fmt::Debug, fs::File, io::BufReader, path::PathBuf, process::Command,
|
|
||||||
};
|
|
||||||
|
|
||||||
use git2;
|
|
||||||
use libpt::log::{debug, error, trace};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use crate::error::*;
|
|
||||||
|
|
||||||
pub mod cli;
|
|
||||||
pub mod packages;
|
|
||||||
use cli::Cli;
|
|
||||||
|
|
||||||
pub trait YamlConfigSection: Debug + Clone + for<'a> Deserialize<'a> {
|
|
||||||
fn check(&self) -> Result<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct Changelog {
|
|
||||||
pub enable: bool,
|
|
||||||
#[serde(alias = "git-log")]
|
|
||||||
pub git_log: bool,
|
|
||||||
}
|
|
||||||
impl YamlConfigSection for Changelog {
|
|
||||||
fn check(&self) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct UseCargo {
|
|
||||||
pub publish: bool,
|
|
||||||
pub registries: Vec<String>,
|
|
||||||
}
|
|
||||||
impl YamlConfigSection for UseCargo {
|
|
||||||
fn check(&self) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct Uses {
|
|
||||||
cargo: UseCargo,
|
|
||||||
}
|
|
||||||
impl YamlConfigSection for Uses {
|
|
||||||
fn check(&self) -> Result<()> {
|
|
||||||
self.cargo.check()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum Pass {
|
|
||||||
/// pass specified as plainext
|
|
||||||
#[serde(alias = "pass_text")]
|
|
||||||
Text(String),
|
|
||||||
/// pass to be loaded from an env var
|
|
||||||
#[serde(alias = "pass_env")]
|
|
||||||
Env(String),
|
|
||||||
/// pass to be loaded from a file
|
|
||||||
#[serde(alias = "pass_file")]
|
|
||||||
File(PathBuf),
|
|
||||||
}
|
|
||||||
impl Pass {
|
|
||||||
/// Get the pass, extracting from the underlying source
|
|
||||||
pub fn get_pass(&self) -> Result<String> {
|
|
||||||
self.check()?;
|
|
||||||
Ok(match self {
|
|
||||||
Self::Text(pass) => pass.clone(),
|
|
||||||
Self::Env(key) => std::env::var(key).map_err(ConfigError::from)?,
|
|
||||||
Self::File(file) => std::fs::read_to_string(file)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl YamlConfigSection for Pass {
|
|
||||||
fn check(&self) -> Result<()> {
|
|
||||||
match self {
|
|
||||||
Self::Text(_) => (),
|
|
||||||
Self::Env(envvar) => {
|
|
||||||
if !std::env::var(envvar).map_err(ConfigError::from)?.is_empty() {
|
|
||||||
} else {
|
|
||||||
return Err(ConfigError::EnvNotSet(envvar.clone()).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::File(file) => {
|
|
||||||
if !file.exists() {
|
|
||||||
return Err(ConfigError::PassFileDoesNotExist(file.clone()).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct ApiAuth {
|
|
||||||
pub user: String,
|
|
||||||
pub pass: Pass,
|
|
||||||
}
|
|
||||||
impl YamlConfigSection for ApiAuth {
|
|
||||||
fn check(&self) -> Result<()> {
|
|
||||||
self.pass.check()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct Api {
|
|
||||||
#[serde(alias = "type")]
|
|
||||||
pub server_type: ApiType,
|
|
||||||
/// May be left empty if the [ApiType] is [Github](ApiType::Github).
|
|
||||||
pub endpoint: Option<Url>,
|
|
||||||
/// May be left empty if the Api does not need auth or the auth is part of the
|
|
||||||
/// [endpoint](Api::endpoint) [Url].
|
|
||||||
pub auth: Option<ApiAuth>,
|
|
||||||
/// Name of the repository on the Git server, as git itself has no concept of repository name
|
|
||||||
pub repository: String,
|
|
||||||
}
|
|
||||||
impl std::fmt::Display for Api {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self.server_type {
|
|
||||||
ApiType::Github => write!(
|
|
||||||
f,
|
|
||||||
"{}",
|
|
||||||
self.server_type
|
|
||||||
.default_endpoint()
|
|
||||||
.expect("no default endpoint set for github")
|
|
||||||
),
|
|
||||||
_ => write!(f, "{}", self.endpoint.clone().expect("no endpoint set")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl YamlConfigSection for Api {
|
|
||||||
fn check(&self) -> Result<()> {
|
|
||||||
self.server_type.check()?;
|
|
||||||
if self.server_type != ApiType::Github {
|
|
||||||
if self.auth.is_none() {
|
|
||||||
return Err(ConfigError::NoEndpointSet.into());
|
|
||||||
}
|
|
||||||
match self.endpoint.clone().unwrap().socket_addrs(|| None) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(err) => return Err(err.into()),
|
|
||||||
}
|
|
||||||
} else if let Some(_url) = &self.endpoint {
|
|
||||||
return Err(ConfigError::EndpointSetButNotNeeded.into());
|
|
||||||
}
|
|
||||||
if self.auth.is_some() {
|
|
||||||
self.auth.clone().unwrap().check()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub enum ApiType {
|
|
||||||
#[serde(alias = "gitea")]
|
|
||||||
Gitea,
|
|
||||||
#[serde(alias = "gitlab")]
|
|
||||||
Gitlab,
|
|
||||||
#[serde(alias = "github", alias = "GitHub")]
|
|
||||||
Github,
|
|
||||||
#[serde(alias = "forgejo")]
|
|
||||||
Forgejo,
|
|
||||||
}
|
|
||||||
impl ApiType {
|
|
||||||
pub fn default_endpoint(&self) -> Option<Url> {
|
|
||||||
match self {
|
|
||||||
Self::Github => Some(Url::from_str("https://github.com").unwrap()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl YamlConfigSection for ApiType {
|
|
||||||
fn check(&self) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum Version {
|
|
||||||
Text(String),
|
|
||||||
Cmd(String),
|
|
||||||
Cargo,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Version {
|
|
||||||
pub fn get_version(&self) -> String {
|
|
||||||
// TODO: Error handling
|
|
||||||
match self {
|
|
||||||
Self::Text(ver) => ver.clone(),
|
|
||||||
Self::Cmd(shell_command) => {
|
|
||||||
match Command::new("/bin/bash")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(shell_command)
|
|
||||||
.output()
|
|
||||||
{
|
|
||||||
Ok(output) => {
|
|
||||||
// TODO: check status
|
|
||||||
String::from_utf8(output.stdout).unwrap()
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
panic!("{err:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Cargo => {
|
|
||||||
match Command::new("/bin/bash")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(r#"cat Cargo.toml | rg '^\s*version\s*=\s*"([^"]*)"\s*$' -or '$1'"#)
|
|
||||||
.output()
|
|
||||||
{
|
|
||||||
Ok(output) => {
|
|
||||||
// TODO: check status
|
|
||||||
String::from_utf8(output.stdout).unwrap().trim().to_owned()
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
panic!("{err:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl YamlConfigSection for Version {
|
|
||||||
fn check(&self) -> Result<()> {
|
|
||||||
match self {
|
|
||||||
Self::Text(_) => (),
|
|
||||||
Self::Cmd(_cmd) => {
|
|
||||||
// TODO: get the version with a command
|
|
||||||
todo!("verion from cmd not implemented")
|
|
||||||
}
|
|
||||||
Self::Cargo => {
|
|
||||||
// TODO: get the version as specified in a Cargo.toml
|
|
||||||
todo!("verion from cargo not implemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct YamlConfig {
|
|
||||||
pub changelog: Changelog,
|
|
||||||
pub uses: Uses,
|
|
||||||
pub api: HashMap<String, Api>,
|
|
||||||
pub version: Version,
|
|
||||||
}
|
|
||||||
impl YamlConfigSection for YamlConfig {
|
|
||||||
fn check(&self) -> Result<()> {
|
|
||||||
self.changelog.check()?;
|
|
||||||
self.uses.check()?;
|
|
||||||
self.version.check()?;
|
|
||||||
for api in self.api.values() {
|
|
||||||
api.check()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl YamlConfig {
|
|
||||||
/// check if the built configuration is valid
|
|
||||||
pub fn check(&self) -> Result<()> {
|
|
||||||
for api in &self.api {
|
|
||||||
api.1.check()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Config {
|
|
||||||
pub yaml: YamlConfig,
|
|
||||||
pub cli: Cli,
|
|
||||||
pub path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Config {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}",
|
|
||||||
format_args!(
|
|
||||||
"Config {{yaml: {:?}, repo_path: {:?}}}",
|
|
||||||
self.yaml, self.path
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
pub fn load(cli: &Cli) -> Result<Self> {
|
|
||||||
let repo = match git2::Repository::open_from_env() {
|
|
||||||
Ok(repo) => repo,
|
|
||||||
Err(_err) => {
|
|
||||||
let err = ConfigError::GitRepoNotFound.into();
|
|
||||||
error!("{err}");
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut path = repo.path().to_path_buf();
|
|
||||||
path.pop(); // we want the real root, not the `.git` dir
|
|
||||||
|
|
||||||
let yaml_file_name = if path.join(".autocrate.yaml").exists() {
|
|
||||||
".autocrate.yaml"
|
|
||||||
} else if path.join(".autocrate.yml").exists() {
|
|
||||||
".autocrate.yml"
|
|
||||||
} else {
|
|
||||||
let err = ConfigError::NoYamlFile.into();
|
|
||||||
error!("{err}");
|
|
||||||
return Err(err);
|
|
||||||
};
|
|
||||||
let yaml_file_path = path.join(yaml_file_name);
|
|
||||||
// we can be sure it exists from the checks above
|
|
||||||
assert!(yaml_file_path.exists());
|
|
||||||
if !yaml_file_path.is_file() {
|
|
||||||
let err = ConfigError::YamlFileIsNotFile.into();
|
|
||||||
error!("{err}");
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
let yaml_rd = BufReader::new(File::open(yaml_file_path)?);
|
|
||||||
debug!("reading yaml config and building data structure");
|
|
||||||
let yaml: YamlConfig = serde_yaml::from_reader(yaml_rd)?;
|
|
||||||
trace!("load config:\n{:#?}", yaml);
|
|
||||||
yaml.check()?;
|
|
||||||
debug!("built and checked yaml config");
|
|
||||||
|
|
||||||
Ok(Config {
|
|
||||||
yaml,
|
|
||||||
path,
|
|
||||||
cli: cli.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
pub enum PackageType {
|
|
||||||
Cargo,
|
|
||||||
}
|
|
72
src/error.rs
72
src/error.rs
|
@ -1,72 +0,0 @@
|
||||||
use std::{env::VarError, path::PathBuf, process::ExitStatus, string::FromUtf8Error};
|
|
||||||
|
|
||||||
use anyhow;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use crate::config::ApiAuth;
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
/// Bad IO operation
|
|
||||||
#[error("Bad IO operation")]
|
|
||||||
IO(#[from] std::io::Error),
|
|
||||||
#[error("Bad configuration file")]
|
|
||||||
Config(#[from] ConfigError),
|
|
||||||
#[error(transparent)]
|
|
||||||
Other(#[from] anyhow::Error),
|
|
||||||
#[error("Yaml error")]
|
|
||||||
SerdeYaml(#[from] serde_yaml::Error),
|
|
||||||
#[error("Could not generate the changelog")]
|
|
||||||
ChangelogError(#[from] ChangelogError),
|
|
||||||
#[error("Server Api error")]
|
|
||||||
ServerApiError(#[from] ServerApiError),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum ServerApiError {
|
|
||||||
#[error(transparent)]
|
|
||||||
ParseUrl(#[from] url::ParseError),
|
|
||||||
#[error(transparent)]
|
|
||||||
InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
|
|
||||||
#[error(transparent)]
|
|
||||||
ReqwestErr(#[from] reqwest::Error),
|
|
||||||
#[error(transparent)]
|
|
||||||
ForgejoApiError(#[from] forgejo_api::ForgejoError),
|
|
||||||
#[error(transparent)]
|
|
||||||
GithubApiError(#[from] octocrab::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum ChangelogError {
|
|
||||||
#[error("changelog has 'enabled = false' in the yaml config")]
|
|
||||||
IsDisabledButUsed,
|
|
||||||
#[error("error while using `git log`, is git installed?")]
|
|
||||||
GitCommandError,
|
|
||||||
#[error("error while using `git log`, could not format stdout with utf8")]
|
|
||||||
GitUTF8Error(#[from] FromUtf8Error),
|
|
||||||
#[error("git exited with status {0}: {1}")]
|
|
||||||
GitBadStatus(ExitStatus, String),
|
|
||||||
}
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum ConfigError {
|
|
||||||
#[error("could not find git repository")]
|
|
||||||
GitRepoNotFound,
|
|
||||||
#[error("no \".autocrate.yaml\" or \".autocrate.yml\" found in repository root")]
|
|
||||||
NoYamlFile,
|
|
||||||
#[error("the autocrate config file is not a regular file (is it a directory?)")]
|
|
||||||
YamlFileIsNotFile,
|
|
||||||
#[error("api {0:?} provides both a `pass` and a `pass_file`")]
|
|
||||||
YamlApiAuthBothPass(ApiAuth),
|
|
||||||
#[error("password provided as file, but does not exist: {0}")]
|
|
||||||
PassFileDoesNotExist(PathBuf),
|
|
||||||
#[error("config requires environment variable {0}, but {0} is not set")]
|
|
||||||
EnvNotSet(String),
|
|
||||||
#[error("Bad value for environment variable: {0}")]
|
|
||||||
BadEnv(#[from] VarError),
|
|
||||||
#[error("An endpoint was set for an ApiType that does not require one")]
|
|
||||||
EndpointSetButNotNeeded,
|
|
||||||
#[error("No endpoint was set for an ApiType that requires one")]
|
|
||||||
NoEndpointSet,
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
use git2;
|
|
||||||
use libpt::log::error;
|
|
||||||
|
|
||||||
use crate::error::ConfigError;
|
|
||||||
use crate::{config::Config, error::Result};
|
|
||||||
|
|
||||||
pub(crate) fn get_repo() -> Result<git2::Repository> {
|
|
||||||
let repo = match git2::Repository::open_from_env() {
|
|
||||||
Ok(repo) => repo,
|
|
||||||
Err(_err) => {
|
|
||||||
let err = ConfigError::GitRepoNotFound.into();
|
|
||||||
error!("{err}");
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(repo)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn tag<'repo>(
|
|
||||||
repo: &'repo mut git2::Repository,
|
|
||||||
cfg: &Config,
|
|
||||||
) -> Result<git2::Tag<'repo>> {
|
|
||||||
// TODO: error handling
|
|
||||||
// TODO: allow force
|
|
||||||
// TODO: allow setting a message
|
|
||||||
// TODO: maybe using git as cmd is fancier?
|
|
||||||
let target = repo
|
|
||||||
.find_object(
|
|
||||||
repo.head().unwrap().target().unwrap(),
|
|
||||||
Some(git2::ObjectType::Commit),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let tagger = repo.signature().expect("could not get signature");
|
|
||||||
let message = String::new();
|
|
||||||
let force = true;
|
|
||||||
let tag = repo
|
|
||||||
.tag(
|
|
||||||
&cfg.yaml.version.get_version(),
|
|
||||||
// "importantversion",
|
|
||||||
&target,
|
|
||||||
&tagger,
|
|
||||||
&message,
|
|
||||||
force,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let tag: git2::Tag = repo.find_tag(tag).unwrap();
|
|
||||||
Ok(tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn push(_cfg: &Config) -> Result<()> {
|
|
||||||
// TODO: error handling
|
|
||||||
// TODO: maybe using git as lib is fancier?
|
|
||||||
Command::new("git").arg("push").status().unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_commit_sig<'repo>(repo: &'repo git2::Repository) -> Result<String> {
|
|
||||||
// TODO: error handling
|
|
||||||
// TODO: maybe using git as cmd is fancier?
|
|
||||||
let target = repo
|
|
||||||
.find_commit(repo.head().unwrap().target().unwrap())
|
|
||||||
.unwrap();
|
|
||||||
Ok(target.id().to_string())
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
pub mod changelog;
|
|
||||||
pub mod config;
|
|
||||||
pub mod error;
|
|
||||||
pub mod git;
|
|
||||||
pub mod publish;
|
|
||||||
pub mod release;
|
|
||||||
pub mod serverapi;
|
|
63
src/main.rs
63
src/main.rs
|
@ -1,62 +1,3 @@
|
||||||
use std::error::Error as _;
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
use autocrate::{
|
|
||||||
changelog::*,
|
|
||||||
config::{
|
|
||||||
cli::{Cli, Commands},
|
|
||||||
Config,
|
|
||||||
},
|
|
||||||
error::*,
|
|
||||||
publish::publish,
|
|
||||||
release::release,
|
|
||||||
serverapi::ApiCollection,
|
|
||||||
};
|
|
||||||
use libpt::log::{debug, error};
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> Result<()> {
|
|
||||||
let cli = Cli::cli_parse();
|
|
||||||
let cfg = Config::load(&cli)?;
|
|
||||||
|
|
||||||
let status: Option<Error> = match cli.command {
|
|
||||||
Commands::Changelog { .. } => {
|
|
||||||
let chlog = Changelog::build(&cfg);
|
|
||||||
if chlog.is_ok() {
|
|
||||||
println!("{}", chlog.unwrap());
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(chlog.unwrap_err())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Commands::Release { .. } => {
|
|
||||||
// TODO: check if repo is dirty and create a commit with a given option
|
|
||||||
let mut apis = ApiCollection::build(&cfg).await?;
|
|
||||||
match release(&cfg, &mut apis).await {
|
|
||||||
Ok(_) => None,
|
|
||||||
Err(err) => Some(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Commands::Publish { .. } => {
|
|
||||||
// TODO: check if repo is dirty and create a commit with a given option
|
|
||||||
match publish(&cfg).await {
|
|
||||||
Ok(_) => None,
|
|
||||||
Err(err) => Some(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Commands::Version {} => {
|
|
||||||
// TODO: version bump
|
|
||||||
// TODO: version select interactive
|
|
||||||
// TODO: version select automated
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
Commands::Init { .. } => {
|
|
||||||
// TODO: create a basic autocrate yaml
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some(err) = status {
|
|
||||||
error!("{err}");
|
|
||||||
debug!("{:#?}", err.source());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
use crate::{config::Config, error::*};
|
|
||||||
|
|
||||||
pub struct PublishContext;
|
|
||||||
|
|
||||||
pub async fn publish(_cfg: &Config) -> Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
use crate::{
|
|
||||||
config::Config,
|
|
||||||
error::*,
|
|
||||||
git::{self, get_commit_sig, push, tag},
|
|
||||||
serverapi::ApiCollection,
|
|
||||||
};
|
|
||||||
|
|
||||||
use futures::{self, stream::FuturesUnordered, StreamExt};
|
|
||||||
use libpt::log::info;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
|
||||||
pub struct ReleaseContext {
|
|
||||||
pub draft: bool,
|
|
||||||
pub prerelease: bool,
|
|
||||||
pub username: String,
|
|
||||||
pub repository: String,
|
|
||||||
pub text: String,
|
|
||||||
pub tag: String,
|
|
||||||
pub commit_sig: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn release(cfg: &Config, apis: &mut ApiCollection) -> Result<()> {
|
|
||||||
// TODO: Error handling
|
|
||||||
let _changelog = crate::changelog::Changelog::build(cfg)?.to_string();
|
|
||||||
let mut repo = git::get_repo()?;
|
|
||||||
let tag = tag(&mut repo, cfg).await?.name().unwrap().to_string();
|
|
||||||
let commit_sig = get_commit_sig(&repo).await?;
|
|
||||||
push(cfg).await?; // we assume that we only need to push the current branch to the singular
|
|
||||||
// remote, expecting that the repositories are somehow mirrored
|
|
||||||
// TODO: push to multiple remotes?
|
|
||||||
|
|
||||||
let mut results = FuturesUnordered::new();
|
|
||||||
for api in apis.iter_mut() {
|
|
||||||
// TODO: check that auth exists
|
|
||||||
let specific_rc = ReleaseContext {
|
|
||||||
draft: true,
|
|
||||||
prerelease: true,
|
|
||||||
username: api
|
|
||||||
.get_inner()
|
|
||||||
.clone()
|
|
||||||
.auth
|
|
||||||
.expect("no auth but trying to publish")
|
|
||||||
.user,
|
|
||||||
repository: api.get_inner().repository.clone(),
|
|
||||||
text: crate::changelog::Changelog::build(cfg)?.to_string(),
|
|
||||||
tag: tag.clone(),
|
|
||||||
commit_sig: commit_sig.clone(),
|
|
||||||
};
|
|
||||||
info!("pushing release for {}", api.get_inner());
|
|
||||||
results.push(api.push_release(specific_rc));
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for the release requests to finish
|
|
||||||
while let Some(result) = results.next().await {
|
|
||||||
if result.is_err() {
|
|
||||||
return Err(result.unwrap_err());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: check that the release is made
|
|
||||||
// TODO: generate artifacts
|
|
||||||
// TODO: upload artifacts
|
|
||||||
// TODO: upload artifact signatures
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
use crate::{
|
|
||||||
config::{Api, Config},
|
|
||||||
error::*,
|
|
||||||
serverapi::{PublishContext, ReleaseContext, ServerApi},
|
|
||||||
};
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use forgejo_api;
|
|
||||||
|
|
||||||
pub struct Forgejo {
|
|
||||||
api: Api,
|
|
||||||
cfg: Config,
|
|
||||||
api_wrapper: forgejo_api::Forgejo,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Forgejo {
|
|
||||||
pub async fn build(api: &Api, cfg: &Config) -> Result<Self> {
|
|
||||||
let api_wrapper: forgejo_api::Forgejo = forgejo_api::Forgejo::new(
|
|
||||||
forgejo_api::Auth::Token(&api.auth.clone().unwrap().pass.get_pass()?),
|
|
||||||
api.endpoint.clone().unwrap(),
|
|
||||||
)
|
|
||||||
.map_err(ServerApiError::from)?;
|
|
||||||
Ok(Self {
|
|
||||||
api: api.clone(),
|
|
||||||
cfg: cfg.clone(),
|
|
||||||
api_wrapper,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ServerApi for Forgejo {
|
|
||||||
async fn push_release(&mut self, rc: ReleaseContext) -> Result<()> {
|
|
||||||
let body: forgejo_api::structs::CreateReleaseOption =
|
|
||||||
forgejo_api::structs::CreateReleaseOption {
|
|
||||||
body: Some(rc.text),
|
|
||||||
draft: Some(rc.draft),
|
|
||||||
name: Some(rc.tag.clone()),
|
|
||||||
prerelease: Some(rc.prerelease),
|
|
||||||
tag_name: rc.tag,
|
|
||||||
target_commitish: Some(rc.commit_sig),
|
|
||||||
};
|
|
||||||
self.api_wrapper
|
|
||||||
.repo_create_release(&rc.username, &rc.repository, body)
|
|
||||||
.await
|
|
||||||
.map_err(ServerApiError::from)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn get_inner(&self) -> &Api {
|
|
||||||
&self.api
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
use super::{PublishContext, ReleaseContext, ServerApi};
|
|
||||||
use crate::{
|
|
||||||
config::{Api, Config},
|
|
||||||
error::*,
|
|
||||||
};
|
|
||||||
pub struct Gitea {
|
|
||||||
api: Api,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ServerApi for Gitea {
|
|
||||||
async fn push_release(&mut self, _rc: ReleaseContext) -> Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn get_inner(&self) -> &Api {
|
|
||||||
&self.api
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gitea {
|
|
||||||
pub async fn build(api: &Api, _cfg: &Config) -> Result<Self> {
|
|
||||||
Ok(Self { api: api.clone() })
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
use async_trait::async_trait;
|
|
||||||
use octocrab;
|
|
||||||
|
|
||||||
use super::{PublishContext, ReleaseContext, ServerApi};
|
|
||||||
use crate::{
|
|
||||||
config::{Api, Config},
|
|
||||||
error::*,
|
|
||||||
};
|
|
||||||
pub struct Github {
|
|
||||||
api: Api,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Github {
|
|
||||||
pub async fn build(api: &Api, _cfg: &Config) -> Result<Self> {
|
|
||||||
Ok(Self {
|
|
||||||
api: api.to_owned(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ServerApi for Github {
|
|
||||||
async fn push_release(&mut self, rc: ReleaseContext) -> Result<()> {
|
|
||||||
let _response = octocrab::instance()
|
|
||||||
.repos(rc.username, rc.repository)
|
|
||||||
.releases()
|
|
||||||
.create(&rc.tag)
|
|
||||||
.target_commitish(&rc.commit_sig)
|
|
||||||
.name(&rc.tag)
|
|
||||||
.body(&rc.text)
|
|
||||||
.draft(rc.draft)
|
|
||||||
.prerelease(rc.prerelease)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map_err(ServerApiError::from)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn get_inner(&self) -> &Api {
|
|
||||||
&self.api
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
use super::{PublishContext, ReleaseContext, ServerApi};
|
|
||||||
use crate::{
|
|
||||||
config::{Api, Config},
|
|
||||||
error::*,
|
|
||||||
};
|
|
||||||
pub struct Gitlab {
|
|
||||||
api: Api,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl ServerApi for Gitlab {
|
|
||||||
async fn push_release(&mut self, _rc: ReleaseContext) -> Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn get_inner(&self) -> &Api {
|
|
||||||
&self.api
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gitlab {
|
|
||||||
pub async fn build(api: &Api, _cfg: &Config) -> Result<Self> {
|
|
||||||
Ok(Self { api: api.clone() })
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use reqwest::ClientBuilder;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
config::{self, ApiType, Config},
|
|
||||||
error::*,
|
|
||||||
publish::PublishContext,
|
|
||||||
release::ReleaseContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod forgejo;
|
|
||||||
pub mod gitea;
|
|
||||||
pub mod github;
|
|
||||||
pub mod gitlab;
|
|
||||||
use forgejo::*;
|
|
||||||
use gitea::*;
|
|
||||||
use github::*;
|
|
||||||
use gitlab::*;
|
|
||||||
|
|
||||||
pub static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
|
|
||||||
|
|
||||||
// NOTE: in stable rust, traits can normally not contain async methods,
|
|
||||||
// see [here](https://stackoverflow.com/questions/65921581/how-can-i-define-an-async-method-in-a-trait).
|
|
||||||
// The `async_trait` crate can be used to work around this limitation.
|
|
||||||
#[async_trait]
|
|
||||||
pub trait ServerApi {
|
|
||||||
async fn push_release(&mut self, rc: ReleaseContext) -> Result<()>;
|
|
||||||
async fn push_release_artifact(&mut self, rc: ReleaseContext) -> Result<()>;
|
|
||||||
async fn push_pkg(&mut self, pc: PublishContext) -> Result<()>;
|
|
||||||
fn get_inner(&self) -> &config::Api;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) type ApiCollectionInner = Vec<Box<dyn ServerApi>>;
|
|
||||||
|
|
||||||
pub struct ApiCollection {
|
|
||||||
collection: ApiCollectionInner,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApiCollection {
|
|
||||||
pub async fn build(cfg: &Config) -> Result<Self> {
|
|
||||||
let mut collection: ApiCollectionInner = ApiCollectionInner::new();
|
|
||||||
for api in &cfg.yaml.api {
|
|
||||||
match api.1.server_type {
|
|
||||||
ApiType::Gitea => {
|
|
||||||
collection.push(Box::new(Gitea::build(api.1, cfg).await?));
|
|
||||||
}
|
|
||||||
ApiType::Gitlab => {
|
|
||||||
collection.push(Box::new(Gitlab::build(api.1, cfg).await?));
|
|
||||||
}
|
|
||||||
ApiType::Github => {
|
|
||||||
collection.push(Box::new(Github::build(api.1, cfg).await?));
|
|
||||||
}
|
|
||||||
ApiType::Forgejo => {
|
|
||||||
collection.push(Box::new(Forgejo::build(api.1, cfg).await?));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(ApiCollection { collection })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn collection(&self) -> &ApiCollectionInner {
|
|
||||||
self.collection.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn collection_mut(&mut self) -> &mut ApiCollectionInner {
|
|
||||||
self.collection.as_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn client_builder() -> ClientBuilder {
|
|
||||||
ClientBuilder::new().user_agent(USER_AGENT)
|
|
||||||
}
|
|
||||||
|
|
||||||
// trait iimplementations for easy use of ApiCollection follow
|
|
||||||
impl IntoIterator for ApiCollection {
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.collection.into_iter()
|
|
||||||
}
|
|
||||||
type Item = Box<dyn ServerApi>;
|
|
||||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for ApiCollection {
|
|
||||||
type Target = [Box<dyn ServerApi>];
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.collection[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for ApiCollection {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.collection[..]
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue