59 Commits

Author SHA1 Message Date
cuddle-please
3ec1f98271 chore(release): 0.6.0
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-01-09 22:46:23 +00:00
5fd902f87c feat: enable commit bodies in changelog and fixes general warnings and updates (#49)
All checks were successful
continuous-integration/drone/push Build is passing
Allows commit bodies to show up in release notes, this is something I'd prefer as my releases are usually short, and I'd like to see these as I don't use pull requests as often, and often miss the context, as I don't link to commits currently.

Also fixes a lot of warnings and reintroduces failing tests, still not perfect, but better than before.
Co-authored-by: kjuulh <contact@kjuulh.io>
Co-committed-by: kjuulh <contact@kjuulh.io>
2025-01-09 23:46:07 +01:00
600d8c184c chore(deps): update rust crate serde to v1.0.217
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-12-28 01:28:59 +00:00
be27da1712 chore(deps): update rust crate serde to v1.0.216
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-12-11 05:24:41 +00:00
87e789d657 chore(deps): update rust crate tracing-subscriber to v0.3.19
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-11-30 01:43:37 +00:00
16710ed41b chore(deps): update rust crate tracing to v0.1.41
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-11-28 01:28:01 +00:00
18bd9e900a chore(deps): update rust crate serde to v1.0.215
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-11-12 01:22:22 +00:00
545e8c5476 chore(deps): update rust crate serde to v1.0.214
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-10-29 01:27:46 +00:00
42f23fdfac chore(deps): update rust crate serde to v1.0.213
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-10-23 00:28:22 +00:00
249a40f1aa chore(deps): update rust crate serde to v1.0.210
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-10-17 00:26:58 +00:00
c1187022f2 feat: removed tests for now
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2024-10-16 21:40:02 +02:00
0fc1438a4a feat: update deps
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2024-10-16 21:30:38 +02:00
166be0c289 feat: update
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2024-04-10 15:48:57 +02:00
cuddle-please
ff9f5e25d2 chore(release): 0.5.0
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
chore: stuff

feat: something

Signed-off-by: kjuulh <contact@kjuulh.io>
2024-04-10 15:47:28 +02:00
9ac744b39d feat: stuff
All checks were successful
continuous-integration/drone/push Build is passing
2024-04-09 23:31:19 +02:00
9c967a0f31 feat: with rust version
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2024-04-09 23:10:12 +02:00
3878e6bc0a feat: add rust actions
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2024-04-09 22:57:01 +02:00
cuddle-please
9999fca9b0 chore(release): 0.4.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2024-04-08 21:08:18 +00:00
dad8fc472e feat: remove comment
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2024-04-08 23:07:51 +02:00
05ecdf5251 feat: add jq
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2024-04-08 22:52:06 +02:00
12063f7c23 feat: fix tests
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2024-04-08 22:48:30 +02:00
490130126b feat: update deps
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2024-04-08 22:44:14 +02:00
9201ff9294 chore(deps): update all dependencies
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2024-04-05 14:34:44 +00:00
477d82af55 feat: update chrono
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2024-03-30 21:32:40 +01:00
d94b9cbe86 feat: remove deps
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2024-03-30 21:19:39 +01:00
2b277ec61f something
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-30 21:17:48 +01:00
3f2642aed0 feat: without dagger
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2024-03-30 20:53:08 +01:00
44bc26de93 fix(deps): update rust crate futures to 0.3.30
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2023-12-24 14:58:32 +00:00
013bf3b3dc fix(deps): update rust crate futures to 0.3.29
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2023-10-26 15:01:40 +00:00
497ae0f19d chore(deps): update rust crate chrono to 0.4.28
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-30 20:09:36 +00:00
2dcd9d0cd1 chore(deps): update rust crate chrono to 0.4.27
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-29 14:44:14 +00:00
a0634a542b chore(deps): update rust crate clap to 4.4.1
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-28 17:27:34 +00:00
036998b0b9 chore(deps): update rust crate url to 2.4.1
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-28 10:09:28 +00:00
e92284002a chore(deps): update rust crate regex to 1.9.4
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-26 14:37:53 +00:00
69f64bdab2 chore(deps): update rust crate clap to 4.4.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-24 16:37:13 +00:00
f624109643 chore(deps): update rust crate reqwest to 0.11.20
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-23 21:11:15 +00:00
2404a14a32 chore(deps): update rust crate clap to 4.3.24
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-23 14:54:25 +00:00
9a5396a81e chore(deps): update rust crate reqwest to 0.11.19
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-21 19:23:22 +00:00
ae0a54db69 chore(deps): update rust crate clap to 4.3.23
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-18 21:35:58 +00:00
8e36f2a3ca chore(deps): update all dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-17 14:35:51 +00:00
cuddle-please
384e575758 chore(release): 0.3.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-08-13 12:23:58 +00:00
850ada11c2 chore: remove unnused arguments
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-13 14:17:30 +02:00
56d33e2ca5 chore(ci): fix release step
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-13 14:12:52 +02:00
d64a1d15dc feat(ci): with dagger-rust components
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-13 14:10:40 +02:00
c2e0b548f6 chore: add dagger-rust and dagger-cuddle-please
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-13 13:39:59 +02:00
51ca73a53b fix(git): make sure we always fail on exit code != 0
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-12 21:48:18 +02:00
675947ed1e feat: allow v in start of versions
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-08 16:15:49 +02:00
bf3593eee4 chore(deps): update rust crate clap to 4.3.21
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-08 02:23:16 +00:00
19d748702a chore(deps): update rust crate clap to 4.3.20
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-08 01:34:57 +00:00
4276f4529c chore(deps): update rust crate parse-changelog to 0.6.2
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-06 10:08:44 +00:00
d287a54cdf chore(deps): update rust crate regex to 1.9.3
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-05 22:31:28 +00:00
7baf51c1f2 chore(deps): update rust crate regex to 1.9.2
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-05 19:04:56 +00:00
56b44cf2e2 fix(json-edit): with actual arg instead of stupid str replace
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 18:56:43 +02:00
2919ca9a04 chore: remove cr
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 18:38:11 +02:00
ff2b59dd02 chore(json-edit): clarify errors
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 18:37:32 +02:00
19dd0ff636 fix(ci): without token
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 18:36:03 +02:00
c08918ad6f feat(json-edit): added json-edit to update some json content with next global version
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 18:32:09 +02:00
19e7adfedb fix(docs): check fix version
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 15:20:05 +02:00
27cb31f433 chore(docs): remove 0.2 checklist
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 13:05:03 +00:00
35 changed files with 2570 additions and 3954 deletions

View File

@@ -1,170 +1,2 @@
kind: pipeline
name: default
type: docker
steps:
- name: build ci
image: rustlang/rust:nightly
volumes:
- name: ci
path: /mnt/ci
environment:
PKG_CONFIG_SYSROOT_DIR: "/"
CI_PREFIX: "/mnt/ci"
commands:
- set -e
- apt update
- apt install musl-tools pkg-config libssl-dev openssl build-essential musl-dev -y
- rustup target add x86_64-unknown-linux-musl
- cargo build --target=x86_64-unknown-linux-musl -p ci
#- cargo build -p ci
- mv target/x86_64-unknown-linux-musl/debug/ci "$CI_PREFIX/ci"
#- mv target/debug/ci $CI_PREFIX/ci
- name: load_secret
image: debian:buster-slim
volumes:
- name: ssh
path: /root/.ssh/
environment:
SSH_KEY:
from_secret: gitea_id_ed25519
commands:
- mkdir -p $HOME/.ssh/
- echo "$SSH_KEY" | base64 -d > $HOME/.ssh/id_ed25519
- name: build pr
image: kasperhermansen/cuddle:latest
pull: always
volumes:
- name: ssh
path: /root/.ssh/
- name: dockersock
path: /var/run
- name: ci
path: /mnt/ci
commands:
- eval `ssh-agent`
- chmod -R 600 ~/.ssh
- ssh-add
- echo "$DOCKER_PASSWORD" | docker login --password-stdin --username="$DOCKER_USERNAME" docker.io
- ldd $CI_PREFIX
- apk add git
- cuddle x ci:pr
environment:
DOCKER_BUILDKIT: 1
DOCKER_PASSWORD:
from_secret: docker_password
DOCKER_USERNAME:
from_secret: docker_username
CUDDLE_SECRETS_PROVIDER: 1password
CUDDLE_ONE_PASSWORD_DOT_ENV: ".env.ci"
CUDDLE_SSH_AGENT: "true"
CI_PREFIX: "/mnt/ci/ci"
CUDDLE_PLEASE_TOKEN:
from_secret: cuddle_please_token
OP_SERVICE_ACCOUNT_TOKEN:
from_secret: op_service_account_token
when:
event:
- pull_request
exclude:
- main
- master
depends_on:
- "load_secret"
- "build ci"
- name: build main
image: kasperhermansen/cuddle:latest
pull: always
volumes:
- name: ssh
path: /root/.ssh/
- name: dockersock
path: /var/run
- name: ci
path: /mnt/ci
commands:
- eval `ssh-agent`
- chmod -R 600 ~/.ssh
- ssh-add
- echo "$DOCKER_PASSWORD" | docker login --password-stdin --username="$DOCKER_USERNAME" docker.io
- ldd $CI_PREFIX
- apk add git
- cuddle x ci:main
environment:
DOCKER_BUILDKIT: 1
DOCKER_PASSWORD:
from_secret: docker_password
DOCKER_USERNAME:
from_secret: docker_username
CUDDLE_SECRETS_PROVIDER: 1password
CUDDLE_ONE_PASSWORD_DOT_ENV: ".env.ci"
CUDDLE_SSH_AGENT: "true"
CI_PREFIX: "/mnt/ci/ci"
CUDDLE_PLEASE_TOKEN:
from_secret: cuddle_please_token
OP_SERVICE_ACCOUNT_TOKEN:
from_secret: op_service_account_token
when:
event:
- push
branch:
- main
- master
exclude:
- pull_request
depends_on:
- "load_secret"
- "build ci"
- name: deploy release
image: kasperhermansen/cuddle:latest
pull: always
volumes:
- name: ssh
path: /root/.ssh/
- name: dockersock
path: /var/run
commands:
- eval `ssh-agent`
- chmod -R 600 ~/.ssh
- ssh-add
- cuddle x build:release:all
- cuddle x deploy:docs:preview
environment:
DOCKER_BUILDKIT: 1
CUDDLE_SECRETS_PROVIDER: 1password
CUDDLE_ONE_PASSWORD_DOT_ENV: ".env.ci"
CUDDLE_SSH_AGENT: "true"
CUDDLE_CI: "true"
CUDDLE_PLEASE_TOKEN:
from_secret: cuddle_please_token
OP_SERVICE_ACCOUNT_TOKEN:
from_secret: op_service_account_token
when:
event:
- tag
ref:
include:
- refs/tags/v*
depends_on:
- "load_secret"
- "build ci"
services:
- name: docker
image: docker:dind
privileged: true
volumes:
- name: dockersock
path: /var/run
volumes:
- name: ssh
temp: {}
- name: dockersock
temp: {}
- name: ci
temp: {}
kind: template
load: cuddle-rust-cli-plan.yaml

View File

@@ -6,6 +6,90 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.6.0] - 2025-01-09
### Added
- enable commit bodies in changelog and fixes general warnings and updates (#49)
- removed tests for now
- update deps
- update
### Other
- *(deps)* update rust crate serde to v1.0.217
- *(deps)* update rust crate serde to v1.0.216
- *(deps)* update rust crate tracing-subscriber to v0.3.19
- *(deps)* update rust crate tracing to v0.1.41
- *(deps)* update rust crate serde to v1.0.215
- *(deps)* update rust crate serde to v1.0.214
- *(deps)* update rust crate serde to v1.0.213
- *(deps)* update rust crate serde to v1.0.210
## [0.5.0] - 2024-04-09
### Added
- stuff
- with rust version
- add rust actions
## [0.4.0] - 2024-04-08
### Added
- remove comment
- add jq
- fix tests
- update deps
- update chrono
- remove deps
- without dagger
### Fixed
- *(deps)* update rust crate futures to 0.3.30
- *(deps)* update rust crate futures to 0.3.29
### Other
- *(deps)* update all dependencies
- something
- *(deps)* update rust crate chrono to 0.4.28
- *(deps)* update rust crate chrono to 0.4.27
- *(deps)* update rust crate clap to 4.4.1
- *(deps)* update rust crate url to 2.4.1
- *(deps)* update rust crate regex to 1.9.4
- *(deps)* update rust crate clap to 4.4.0
- *(deps)* update rust crate reqwest to 0.11.20
- *(deps)* update rust crate clap to 4.3.24
- *(deps)* update rust crate reqwest to 0.11.19
- *(deps)* update rust crate clap to 4.3.23
- *(deps)* update all dependencies
## [0.3.0] - 2023-08-13
### Added
- *(ci)* with dagger-rust components
- allow v in start of versions
- *(json-edit)* added json-edit to update some json content with next global version
### Fixed
- *(git)* make sure we always fail on exit code != 0
- *(json-edit)* with actual arg instead of stupid str replace
- *(ci)* without token
- *(docs)* check fix version
- *(crate)* initial pr always included the entire changelog
- *(crate)* always prefix with 'v' when creating prs, or releases (#9)
### Other
- remove unnused arguments
- *(ci)* fix release step
- add dagger-rust and dagger-cuddle-please
- *(deps)* update rust crate clap to 4.3.21
- *(deps)* update rust crate clap to 4.3.20
- *(deps)* update rust crate parse-changelog to 0.6.2
- *(deps)* update rust crate regex to 1.9.3
- *(deps)* update rust crate regex to 1.9.2
- remove cr
- *(json-edit)* clarify errors
- *(docs)* remove 0.2 checklist
## [0.2.1] - 2023-08-04
### Docs
@@ -44,7 +128,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- add mkdocs build
- add basic version
- update with repository
- add publishable to rest
- add publish to rest
- hack get in control of log level
### Docs

2818
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,5 @@
[workspace]
members = [
"crates/cuddle-please",
"crates/cuddle-please-frontend",
"crates/cuddle-please-commands",
"crates/cuddle-please-misc",
"ci"
]
members = ["crates/*"]
resolver = "2"
[workspace.dependencies]
@@ -13,24 +7,31 @@ cuddle-please = { path = "crates/cuddle-please", version = "0.1.0" }
cuddle-please-frontend = { path = "crates/cuddle-please-frontend", version = "0.1.0" }
cuddle-please-commands = { path = "crates/cuddle-please-commands", version = "0.1.0" }
cuddle-please-misc = { path = "crates/cuddle-please-misc", version = "0.1.0" }
cuddle-please-release-strategy = { path = "crates/cuddle-please-release-strategy", version = "0.1.0" }
cuddle-please-actions = { path = "crates/cuddle-please-actions", version = "0.1.0" }
anyhow = { version = "1.0.72" }
anyhow = { version = "1.0.81" }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
clap = { version = "4.3.19", features = ["derive", "env"] }
dotenv = { version = "0.15.0" }
url = { version = "2.4.0" }
serde_yaml = { version = "0.9.25" }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
clap = { version = "4.5.23", features = ["derive", "env"] }
dotenvy = { version = "0.15.7" }
url = { version = "2.5.0" }
serde_yaml = { version = "0.9.34+deprecated" }
yaml-rust2 = { version = "0.8.0" }
serde = { version = "1", features = ["derive"] }
semver = "1.0.18"
semver = "1.0.22"
conventional_commit_parser = "0.9.4"
tempdir = "0.3.7"
reqwest = { version = "0.11.18" }
git-cliff-core = "1.2.0"
regex = "1.9.1"
chrono = "0.4.26"
reqwest = { version = "0.12.3" }
git-cliff-core = "2.7.0"
regex = "1.10.4"
chrono = "0.4.37"
lazy_static = "1.4.0"
parse-changelog = "0.6.1"
parse-changelog = "0.6.6"
toml_edit = "0.22.9"
tracing-test = "0.2"
pretty_assertions = "1.4"
[workspace.package]
version = "0.6.0"

View File

@@ -36,20 +36,9 @@ See docs for more information about installation and some such
## Checklist
### 0.2 Milestone
- [x] Add docs
- [ ] Add asciinema
- [x] Create docker image
- [x] Add examples
- [x] Fx drone config
- [x] Releaser
- [x] On main/master
- [ ] tbd...
### 0.3 Milestone
- [ ] Fix: 0.0.0 -> **v**0.0.0
- [x] Fix: 0.0.0 -> **v**0.0.0
- [ ] Add release strategies
- [ ] Add reporter for PR and Repositories
- [ ] Add inquire for missing values when needed (when not running in ci or have a proper tty)

1861
ci/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +0,0 @@
[package]
name = "ci"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dagger-sdk = "*"
eyre = "*"
color-eyre = "*"
tokio = "1"
clap = {version = "4", features = ["derive"]}
futures = "0.3.28"
async-scoped = { version = "0.7.1", features = ["tokio", "use-tokio"] }
dotenv.workspace = true

View File

@@ -1,770 +0,0 @@
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use clap::Args;
use clap::Parser;
use clap::Subcommand;
use clap::ValueEnum;
use dagger_sdk::Platform;
use dagger_sdk::QueryContainerOpts;
use futures::StreamExt;
use crate::please_release::run_release_please;
#[derive(Parser, Clone)]
#[command(author, version, about, long_about = None, subcommand_required = true)]
pub struct Command {
#[command(subcommand)]
commands: Commands,
#[command(flatten)]
global: GlobalArgs,
}
#[derive(Subcommand, Clone)]
pub enum Commands {
#[command(subcommand_required = true)]
Local {
#[command(subcommand)]
command: LocalCommands,
},
PullRequest {
#[arg(long)]
image: String,
#[arg(long)]
tag: String,
#[arg(long)]
bin_name: String,
},
Main {
#[arg(long)]
image: String,
#[arg(long)]
tag: String,
#[arg(long)]
bin_name: String,
},
Release,
}
#[derive(Subcommand, Clone)]
pub enum LocalCommands {
Build {
#[arg(long, default_value = "debug")]
profile: BuildProfile,
#[arg(long)]
bin_name: String,
},
Test,
DockerImage {
#[arg(long)]
image: String,
#[arg(long)]
tag: String,
#[arg(long)]
bin_name: String,
},
PleaseRelease,
BuildDocs {},
}
#[derive(Debug, Clone, ValueEnum)]
pub enum BuildProfile {
Debug,
Release,
}
#[derive(Debug, Clone, Args)]
pub struct GlobalArgs {
#[arg(long, global = true, help_heading = "Global")]
dry_run: bool,
#[arg(long, global = true, help_heading = "Global")]
rust_builder_image: Option<String>,
#[arg(long, global = true, help_heading = "Global")]
production_image: Option<String>,
#[arg(long, global = true, help_heading = "Global")]
mkdocs_image: Option<String>,
#[arg(long, global = true, help_heading = "Global")]
caddy_image: Option<String>,
#[arg(long, global = true, help_heading = "Global")]
source: Option<PathBuf>,
#[arg(long, global = true, help_heading = "Global")]
docs_image: Option<String>,
#[arg(long, global = true, help_heading = "Global")]
docs_image_tag: Option<String>,
}
#[tokio::main]
async fn main() -> eyre::Result<()> {
let _ = dotenv::dotenv();
let _ = color_eyre::install();
let client = dagger_sdk::connect().await?;
let cli = Command::parse();
match &cli.commands {
Commands::Local { command } => match command {
LocalCommands::Build {
profile: _,
bin_name,
} => {
let base_image = base_rust_image(
client.clone(),
&cli.global,
&None,
&bin_name.clone(),
&"release".into(),
)
.await?;
let prod_image = get_base_debian_image(client.clone(), &cli.global, None).await?;
build::execute(
client,
&cli.global,
&base_image,
&prod_image,
bin_name,
&None,
)
.await?;
}
LocalCommands::Test => {
let base_image = base_rust_image(
client.clone(),
&cli.global,
&None,
&"cuddle-please".into(),
&"debug".into(),
)
.await?;
test::execute(client, &cli.global, base_image).await?;
}
LocalCommands::DockerImage {
tag,
image,
bin_name,
} => {
build::build_and_deploy(client, &cli.global, bin_name, image, tag).await?;
}
LocalCommands::PleaseRelease => todo!(),
LocalCommands::BuildDocs {} => {
let _image = docs::execute(
client.clone(),
&cli.global,
&Some("linux/amd64".to_string()),
)
.await?;
}
},
Commands::PullRequest {
image,
tag,
bin_name,
} => {
async fn test(client: Arc<dagger_sdk::Query>, cli: &Command, bin_name: &String) {
let args = &cli.global;
let base_image =
base_rust_image(client.clone(), args, &None, bin_name, &"debug".into())
.await
.unwrap();
test::execute(client.clone(), args, base_image)
.await
.unwrap();
}
async fn build(
client: Arc<dagger_sdk::Query>,
cli: &Command,
bin_name: &String,
image: &String,
tag: &String,
) {
let args = &cli.global;
build::build(client.clone(), args, bin_name, image, tag)
.await
.unwrap();
}
tokio::join!(
test(client.clone(), &cli, bin_name),
build(client.clone(), &cli, bin_name, image, tag),
);
}
Commands::Main {
image,
tag,
bin_name,
} => {
async fn test(client: Arc<dagger_sdk::Query>, cli: &Command, bin_name: &String) {
let args = &cli.global;
let base_image =
base_rust_image(client.clone(), args, &None, bin_name, &"debug".into())
.await
.unwrap();
test::execute(client.clone(), args, base_image)
.await
.unwrap();
}
async fn build(
client: Arc<dagger_sdk::Query>,
cli: &Command,
bin_name: &String,
image: &String,
tag: &String,
) {
let args = &cli.global;
build::build_and_deploy(client.clone(), args, bin_name, image, tag)
.await
.unwrap();
}
async fn cuddle_please(client: Arc<dagger_sdk::Query>, cli: &Command) {
run_release_please(client.clone(), &cli.global)
.await
.unwrap();
}
tokio::join!(
test(client.clone(), &cli, bin_name),
build(client.clone(), &cli, bin_name, image, tag),
cuddle_please(client.clone(), &cli)
);
}
Commands::Release => todo!(),
}
Ok(())
}
mod please_release {
use std::sync::Arc;
use crate::{base_rust_image, GlobalArgs};
pub async fn run_release_please(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
) -> eyre::Result<()> {
let base_image = base_rust_image(
client.clone(),
args,
&Some("linux/amd64".to_string()),
&"cuddle-please".to_string(),
&"release".into(),
)
.await?;
let build_image = base_image.with_exec(vec![
"cargo",
"install",
"--target",
"x86_64-unknown-linux-gnu",
"--path=crates/cuddle-please",
]);
let src = client
.git_opts(
"https://git.front.kjuulh.io/kjuulh/cuddle-please",
dagger_sdk::QueryGitOpts {
experimental_service_host: None,
keep_git_dir: Some(true),
},
)
.branch("main")
.tree();
let res = build_image
.with_secret_variable(
"CUDDLE_PLEASE_TOKEN",
client
.set_secret("CUDDLE_PLEASE_TOKEN", std::env::var("CUDDLE_PLEASE_TOKEN")?)
.id()
.await?,
)
.with_workdir("/mnt/app")
.with_directory(".", src.id().await?)
.with_exec(vec![
"git",
"remote",
"set-url",
"origin",
&format!(
"https://git:{}@git.front.kjuulh.io/kjuulh/cuddle-please.git",
std::env::var("CUDDLE_PLEASE_TOKEN")?
),
])
.with_exec(vec![
"git",
"config",
"http.extraheader",
"'Authorization: token b52c18cab8a95d33f34b0d081440f77a2b156886'",
])
.with_exec(vec![
"cuddle-please",
"release",
"--engine=gitea",
"--owner=kjuulh",
"--repo=cuddle-please",
"--branch=main",
"--api-url=https://git.front.kjuulh.io",
"--log-level=debug",
]);
let exit_code = res.exit_code().await?;
if exit_code != 0 {
eyre::bail!("failed to run cuddle-please");
}
let please_out = res.stdout().await?;
println!("{please_out}");
let please_out = res.stderr().await?;
println!("{please_out}");
Ok(())
}
}
mod docs {
use std::sync::Arc;
use dagger_sdk::Container;
use crate::GlobalArgs;
pub fn get_docs_src(client: Arc<dagger_sdk::Query>) -> eyre::Result<dagger_sdk::Directory> {
let docs_content = client.host().directory_opts(
".",
dagger_sdk::HostDirectoryOpts {
exclude: None,
include: Some(vec!["mkdocs.yml", "docs/"]),
},
);
Ok(docs_content)
}
pub async fn execute(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
_platform: &Option<String>,
) -> eyre::Result<Container> {
let mkdocs_container = client.container().from(
args.mkdocs_image
.as_ref()
.expect("--mkdocs-image to be set"),
);
let built_mkdocs_container = mkdocs_container
.with_directory("/docs", get_docs_src(client.clone())?.id().await?)
.with_exec(vec!["build"]);
let site_output = built_mkdocs_container.directory("/docs/site").id().await?;
let caddy_file = client.host().directory("templates").file("Caddyfile");
let dep_image = client
.container()
.from(args.caddy_image.as_ref().expect("--caddy-image to be set"))
.with_directory("/usr/share/caddy", site_output)
.with_file("/etc/caddy/Caddyfile", caddy_file.id().await?)
.with_exec(vec!["echo", "caddy"]);
Ok(dep_image)
}
pub async fn publish(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
containers: &Vec<dagger_sdk::Container>,
) -> eyre::Result<()> {
let container_ids =
futures::future::join_all(containers.iter().map(|c| c.id()).collect::<Vec<_>>()).await;
let container_ids = container_ids
.into_iter()
.collect::<eyre::Result<Vec<dagger_sdk::ContainerId>>>()?;
client
.container()
.publish_opts(
format!(
"{}:{}",
args.docs_image.as_ref().expect("--docs-image to be set"),
args.docs_image_tag
.as_ref()
.expect("--docs-image-tag to be set")
),
dagger_sdk::ContainerPublishOpts {
platform_variants: Some(container_ids),
},
)
.await?;
Ok(())
}
}
mod build {
use std::sync::Arc;
use dagger_sdk::Container;
use crate::{base_rust_image, get_base_debian_image, GlobalArgs};
pub async fn build_and_deploy(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
bin_name: &String,
image: &String,
tag: &String,
) -> eyre::Result<()> {
// let containers = vec!["linux/amd64", "linux/arm64"];
let base_image = get_base_debian_image(
client.clone(),
&args.clone(),
Some("linux/amd64".to_string()),
)
.await?;
let container = base_rust_image(
client.clone(),
args,
&Some("linux/amd64".to_string()),
&bin_name.clone(),
&"release".into(),
)
.await?;
let build_image = execute(
client.clone(),
args,
&container,
&base_image,
bin_name,
&Some("linux/amd64".to_string()),
)
.await?;
let build_id = build_image.id().await?;
let _container = client
.clone()
.container()
.publish_opts(
format!("{image}:{tag}"),
dagger_sdk::ContainerPublishOpts {
platform_variants: Some(vec![build_id]),
},
)
.await?;
Ok(())
}
pub async fn build(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
bin_name: &String,
_image: &String,
_tag: &String,
) -> eyre::Result<()> {
// let containers = vec!["linux/amd64", "linux/arm64"];
let base_image = get_base_debian_image(
client.clone(),
&args.clone(),
Some("linux/amd64".to_string()),
)
.await?;
let container = base_rust_image(
client.clone(),
args,
&Some("linux/amd64".to_string()),
&bin_name.clone(),
&"release".into(),
)
.await?;
let build_image = execute(
client.clone(),
args,
&container,
&base_image,
bin_name,
&Some("linux/amd64".to_string()),
)
.await?;
build_image.exit_code().await?;
Ok(())
}
pub async fn execute(
_client: Arc<dagger_sdk::Query>,
_args: &GlobalArgs,
container: &dagger_sdk::Container,
base_image: &dagger_sdk::Container,
bin_name: &String,
platform: &Option<String>,
) -> eyre::Result<Container> {
let rust_target = match platform
.clone()
.unwrap_or("linux/amd64".to_string())
.as_str()
{
"linux/amd64" => "x86_64-unknown-linux-gnu",
"linux/arm64" => "aarch64-unknown-linux-gnu",
_ => eyre::bail!("architecture not supported"),
};
let build_image = container.with_exec(vec![
"cargo",
"build",
"--target",
rust_target,
"--release",
"-p",
bin_name,
]);
let final_image = base_image
.with_file(
format!("/usr/local/bin/{}", &bin_name),
build_image
.file(format!("target/{}/release/{}", rust_target, &bin_name))
.id()
.await?,
)
.with_exec(vec![bin_name, "--help"]);
let output = final_image.stdout().await?;
println!("{output}");
//.with_entrypoint(vec![&bin_name, "--log-level=debug"]);
Ok(final_image)
}
}
mod test {
use std::sync::Arc;
use crate::GlobalArgs;
pub async fn execute(
_client: Arc<dagger_sdk::Query>,
_args: &GlobalArgs,
container: dagger_sdk::Container,
) -> eyre::Result<()> {
let test_image = container
.pipeline("rust:test")
.with_exec(vec!["apt", "update"])
.with_exec(vec!["apt", "install", "-y", "git"])
.with_exec(vec!["cargo", "test"]);
test_image.exit_code().await?;
Ok(())
}
}
pub async fn get_base_debian_image(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
platform: Option<String>,
) -> eyre::Result<dagger_sdk::Container> {
let default_platform = client.default_platform().await?;
let platform = platform.map(Platform).unwrap_or(default_platform);
let image = client
.container_opts(QueryContainerOpts {
id: None,
platform: Some(platform),
})
.from(
args.production_image
.clone()
.unwrap_or("debian:bullseye".to_string()),
);
let base_image = image.with_exec(vec!["apt", "update"]).with_exec(vec![
"apt",
"install",
"-y",
"libssl-dev",
"pkg-config",
"openssl",
"git",
]);
Ok(base_image)
}
pub fn get_src(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
) -> eyre::Result<dagger_sdk::Directory> {
let directory = client.host().directory_opts(
args.source
.clone()
.unwrap_or(PathBuf::from("."))
.display()
.to_string(),
dagger_sdk::HostDirectoryOptsBuilder::default()
.exclude(vec!["node_modules/", ".git/", "target/"])
.build()?,
);
Ok(directory)
}
pub async fn get_rust_dep_src(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
) -> eyre::Result<dagger_sdk::Directory> {
let directory = client.host().directory_opts(
args.source
.clone()
.unwrap_or(PathBuf::from("."))
.display()
.to_string(),
dagger_sdk::HostDirectoryOptsBuilder::default()
.include(vec!["**/Cargo.toml", "**/Cargo.lock"])
.build()?,
);
Ok(directory)
}
pub async fn get_rust_skeleton_files(
client: Arc<dagger_sdk::Query>,
_args: &GlobalArgs,
) -> eyre::Result<(dagger_sdk::Directory, Vec<String>)> {
let mut rust_crates = vec![PathBuf::from("ci")];
let mut dirs = tokio::fs::read_dir("crates").await?;
while let Some(entry) = dirs.next_entry().await? {
if entry.metadata().await?.is_dir() {
rust_crates.push(entry.path())
}
}
fn create_skeleton_files(
directory: dagger_sdk::Directory,
path: &Path,
) -> eyre::Result<dagger_sdk::Directory> {
println!("found crates: {}", path.display());
let main_content = r#"
#[allow(dead_code)]
fn main() { panic!("should never be executed"); }"#;
let lib_content = r#"
#[allow(dead_code)]
fn some() { panic!("should never be executed"); }"#;
let directory = directory.with_new_file(
path.join("src").join("main.rs").display().to_string(),
main_content,
);
let directory = directory.with_new_file(
path.join("src").join("lib.rs").display().to_string(),
lib_content,
);
Ok(directory)
}
let mut directory = client.directory();
let mut crate_names = Vec::new();
for rust_crate in rust_crates.iter() {
if let Some(file_name) = rust_crate.file_name() {
crate_names.push(file_name.to_str().unwrap().to_string());
}
directory = create_skeleton_files(directory, rust_crate)?;
}
Ok((directory, crate_names))
}
pub async fn base_rust_image(
client: Arc<dagger_sdk::Query>,
args: &GlobalArgs,
platform: &Option<String>,
bin_name: &String,
profile: &String,
) -> eyre::Result<dagger_sdk::Container> {
let dep_src = get_rust_dep_src(client.clone(), args).await?;
let (skeleton_files, crates) = get_rust_skeleton_files(client.clone(), args).await?;
let src = get_src(client.clone(), args)?;
let client = client.pipeline("rust_base_image");
let rust_target = match platform
.clone()
.unwrap_or("linux/amd64".to_string())
.as_str()
{
"linux/amd64" => "x86_64-unknown-linux-gnu",
"linux/arm64" => "aarch64-unknown-linux-gnu",
_ => eyre::bail!("architecture not supported"),
};
let rust_build_image = client
.container()
.from(
args.rust_builder_image
.as_ref()
.unwrap_or(&"rustlang/rust:nightly".into()),
)
.with_exec(vec!["rustup", "target", "add", rust_target]);
let target_cache = client.cache_volume(format!("rust_target_{}", profile));
let mut build_options = vec!["cargo", "build", "--target", rust_target, "-p", bin_name];
if profile == "release" {
build_options.push("--release");
}
let rust_prebuild = rust_build_image
.with_workdir("/mnt/src")
.with_directory("/mnt/src", dep_src.id().await?)
.with_directory("/mnt/src/", skeleton_files.id().await?)
.with_exec(build_options)
.with_mounted_cache("/mnt/src/target/", target_cache.id().await?);
let exclude = crates
.iter()
.filter(|c| **c != "ci")
.map(|c| format!("**/*{}*", c.replace('-', "_")))
.collect::<Vec<_>>();
let exclude = exclude.iter().map(|c| c.as_str()).collect();
let incremental_dir = client.directory().with_directory_opts(
".",
rust_prebuild.directory("target").id().await?,
dagger_sdk::DirectoryWithDirectoryOpts {
exclude: Some(exclude),
include: None,
},
);
let rust_with_src = rust_build_image
.with_workdir("/mnt/src")
.with_directory(
"/usr/local/cargo",
rust_prebuild.directory("/usr/local/cargo").id().await?,
)
.with_directory("target", incremental_dir.id().await?)
.with_directory("/mnt/src/", src.id().await?);
Ok(rust_with_src)
}

View File

@@ -0,0 +1,19 @@
[package]
name = "cuddle-please-actions"
description = "A release-please inspired release manager tool, built on top of cuddle, but also useful standalone, cuddle-please supports, your ci of choice, as well as gitea, github"
repository = "https://git.front.kjuulh.io/kjuulh/cuddle-please"
readme = "../../README.md"
license-file = "../../LICENSE"
version = "0.1.0"
edition = "2021"
publish = true
[dependencies]
anyhow.workspace = true
tracing.workspace = true
semver.workspace = true
toml_edit.workspace = true
yaml-rust2.workspace = true
[dev-dependencies]
pretty_assertions.workspace = true

View File

@@ -0,0 +1,9 @@
use semver::Version;
use crate::ActionConfig;
pub trait Action {
fn enabled(&self, config: &ActionConfig) -> anyhow::Result<bool>;
fn name(&self) -> String;
fn execute(&self, version: &Version) -> anyhow::Result<()>;
}

View File

@@ -0,0 +1,49 @@
use std::ops::Deref;
use anyhow::Context;
pub enum ActionConfig {
Actual { doc: yaml_rust2::Yaml },
None,
}
impl Deref for ActionConfig {
type Target = yaml_rust2::Yaml;
fn deref(&self) -> &Self::Target {
match &self {
ActionConfig::Actual { doc } => doc,
ActionConfig::None => &yaml_rust2::Yaml::BadValue,
}
}
}
impl TryFrom<&str> for ActionConfig {
type Error = anyhow::Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let mut cuddle = yaml_rust2::YamlLoader::load_from_str(value)?;
if cuddle.len() != 1 {
anyhow::bail!("cuddle.yaml can only be 1 document wide");
}
let doc = cuddle.pop().unwrap();
let doc = doc["please"]["actions"].clone();
if doc.is_badvalue() {
return Ok(Self::None);
}
Ok(Self::Actual { doc })
}
}
impl ActionConfig {
pub fn parse() -> anyhow::Result<Self> {
let cuddle_yaml =
std::fs::read_to_string("cuddle.yaml").context("failed to read cuddle.yaml")?;
Self::try_from(cuddle_yaml.as_str())
}
}

View File

@@ -0,0 +1,45 @@
pub(crate) mod actions;
mod config;
mod rust_action;
use std::{ops::Deref, sync::Arc};
use catalog::RustAction;
pub use config::ActionConfig;
use semver::Version;
pub mod catalog {
pub use crate::rust_action::*;
}
pub struct Action(Arc<dyn actions::Action>);
impl Deref for Action {
type Target = Arc<dyn actions::Action>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub struct Actions(Vec<Action>);
impl Actions {
pub fn from_cuddle() -> anyhow::Result<Self> {
let config = ActionConfig::parse()?;
Ok(Self(
vec![Action(Arc::new(RustAction::new()))]
.into_iter()
.filter(|a| a.enabled(&config).unwrap_or_default())
.collect(),
))
}
pub fn execute(&self, version: &Version) -> anyhow::Result<()> {
for action in &self.0 {
action.execute(version)?;
}
Ok(())
}
}

View File

@@ -0,0 +1,235 @@
use std::io::Write;
use crate::{actions::Action, ActionConfig};
#[derive(Default, Clone)]
pub struct RustAction {}
impl RustAction {
pub fn new() -> Self {
Self {}
}
fn execute_content(
&self,
version: &semver::Version,
cargo_content: &str,
) -> anyhow::Result<String> {
tracing::trace!("parsing Cargo.toml file as tolm");
let mut cargo_doc = cargo_content.parse::<toml_edit::DocumentMut>()?;
tracing::debug!(
"updating cargo workspace package version to {}",
version.to_string()
);
let workspace = if cargo_doc.contains_table("workspace") {
cargo_doc["workspace"].as_table_mut().unwrap()
} else {
let mut t = toml_edit::Table::new();
t.set_implicit(true);
cargo_doc["workspace"] = toml_edit::Item::Table(t);
cargo_doc["workspace"].as_table_mut().unwrap()
};
let package = workspace["package"].or_insert(toml_edit::table());
package["version"] = toml_edit::value(version.to_string());
Ok(cargo_doc.to_string())
}
}
impl Action for RustAction {
fn enabled(&self, config: &ActionConfig) -> anyhow::Result<bool> {
if let Ok(v) = std::env::var("CUDDLE_PLEASE_RUST_ACTION") {
if let Ok(true) = v.parse::<bool>() {
return Ok(true);
}
}
let val = &config[self.name().as_str()];
if val.is_badvalue() {
return Ok(false);
}
Ok(val.as_bool().unwrap_or(true))
}
fn name(&self) -> String {
"rust".into()
}
fn execute(&self, version: &semver::Version) -> anyhow::Result<()> {
tracing::info!(
"running rust action for version: {} and file: Cargo.toml",
version.to_string()
);
let path = std::path::PathBuf::from("Cargo.toml");
tracing::trace!("reading Cargo.toml");
let file = match std::fs::read_to_string(&path) {
Ok(file) => file,
Err(e) => match e.kind() {
std::io::ErrorKind::NotFound => {
anyhow::bail!("err: Cargo.toml was not found in dir")
}
_ => Err(e)?,
},
};
let cargo_doc = self.execute_content(version, &file)?;
let mut cargo_file = std::fs::File::create(&path)?;
cargo_file.write_all(cargo_doc.as_bytes())?;
cargo_file.sync_all()?;
tracing::debug!("finished writing cargo file");
Ok(())
}
}
#[cfg(test)]
mod test {
use semver::{BuildMetadata, Prerelease};
use crate::{actions::Action, ActionConfig};
use super::RustAction;
#[test]
fn test_is_enabled() {
let config = ActionConfig::try_from(
r#"
please:
actions:
rust: true
"#,
)
.unwrap();
let enabled = RustAction::new().enabled(&config).unwrap();
assert!(enabled)
}
#[test]
fn test_is_disabled_by_default() {
let config = ActionConfig::try_from(
r#"
please:
"#,
)
.unwrap();
let enabled = RustAction::new().enabled(&config).unwrap();
assert!(!enabled)
}
#[test]
fn test_is_disabled() {
let config = ActionConfig::try_from(
r#"
please:
actions:
rust: false
"#,
)
.unwrap();
let enabled = RustAction::new().enabled(&config).unwrap();
assert!(!enabled)
}
#[test]
fn test_missing_value_is_enabled() {
let config = ActionConfig::try_from(
r#"
please:
actions:
rust:
"#,
)
.unwrap();
let enabled = RustAction::new().enabled(&config).unwrap();
assert!(enabled)
}
#[test]
fn test_can_edit_empty_file() {
let output = RustAction::default()
.execute_content(
&semver::Version {
major: 0,
minor: 1,
patch: 0,
pre: Prerelease::default(),
build: BuildMetadata::default(),
},
"",
)
.unwrap();
pretty_assertions::assert_eq!(
r#"[workspace.package]
version = "0.1.0"
"#,
&output,
)
}
#[test]
fn test_only_edits_stuff() {
let input = r#"
[workspace]
members = ["."]
[package]
something = {some = "something"}
# Some comment
[workspace.package]
version = "0.0.0" # some comment
readme = "../../"
"#;
let output = RustAction::default()
.execute_content(
&semver::Version {
major: 0,
minor: 1,
patch: 0,
pre: Prerelease::default(),
build: BuildMetadata::default(),
},
input,
)
.unwrap();
pretty_assertions::assert_eq!(
r#"
[workspace]
members = ["."]
[package]
something = {some = "something"}
# Some comment
[workspace.package]
version = "0.1.0"
readme = "../../"
"#,
&output,
)
}
}

View File

@@ -6,18 +6,19 @@ readme = "../../README.md"
license-file = "../../LICENSE"
version = "0.1.0"
edition = "2021"
publishable = true
publish = true
[dependencies]
cuddle-please-frontend.workspace = true
cuddle-please-misc.workspace = true
cuddle-please-actions.workspace = true
anyhow.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
clap.workspace = true
dotenv.workspace = true
dotenvy.workspace = true
serde_yaml.workspace = true
serde.workspace = true
reqwest = { workspace = true, features = ["blocking", "json"] }

View File

@@ -6,6 +6,7 @@ use std::{
};
use clap::{Parser, Subcommand};
use cuddle_please_actions::Actions;
use cuddle_please_frontend::{gatheres::ConfigArgs, PleaseConfig, PleaseConfigBuilder};
use cuddle_please_misc::{
ConsoleUi, DynRemoteGitClient, DynUi, GiteaClient, GlobalArgs, LocalGitClient, StdinFn,
@@ -90,26 +91,27 @@ impl Command {
}
pub fn execute(self, current_dir: Option<&Path>) -> anyhow::Result<()> {
match &self.commands {
Some(Commands::Release {}) => {
let (config, git_client, gitea_client) = self.get_deps(current_dir)?;
ReleaseCommandHandler::new(self.ui, config, git_client, gitea_client)
.execute(self.global.dry_run)?;
}
Some(Commands::Config { command }) => {
let (config, _, _) = self.get_deps(current_dir)?;
ConfigCommandHandler::new(self.ui, config).execute(command)?;
}
Some(Commands::Gitea { command }) => {
let (config, _, gitea_client) = self.get_deps(current_dir)?;
if let Some(c) = &self.commands {
match c {
Commands::Release {} => {
let (config, git_client, gitea_client, actions) = self.get_deps(current_dir)?;
ReleaseCommandHandler::new(self.ui, config, git_client, gitea_client, actions)
.execute(self.global.dry_run)?;
}
Commands::Config { command } => {
let (config, _, _, _) = self.get_deps(current_dir)?;
ConfigCommandHandler::new(self.ui, config).execute(command)?;
}
Commands::Gitea { command } => {
let (config, _, gitea_client, _) = self.get_deps(current_dir)?;
GiteaCommandHandler::new(self.ui, config, gitea_client)
.execute(command, self.global.token.expect("token to be set").deref())?;
GiteaCommandHandler::new(self.ui, config, gitea_client)
.execute(command, self.global.token.expect("token to be set").deref())?;
}
Commands::Doctor {} => {
DoctorCommandHandler::new(self.ui).execute()?;
}
}
Some(Commands::Doctor {}) => {
DoctorCommandHandler::new(self.ui).execute()?;
}
None => {}
}
Ok(())
@@ -118,7 +120,7 @@ impl Command {
fn get_deps(
&self,
current_dir: Option<&Path>,
) -> anyhow::Result<(PleaseConfig, VcsClient, DynRemoteGitClient)> {
) -> anyhow::Result<(PleaseConfig, VcsClient, DynRemoteGitClient, Actions)> {
let config = self.build_config(current_dir)?;
let git_client =
self.get_git(&config, self.global.token.clone().expect("token to be set"))?;
@@ -140,7 +142,9 @@ impl Command {
tracing_subscriber::fmt().with_env_filter(env_filter).init();
}
Ok((config, git_client, gitea_client))
let actions = self.get_actions()?;
Ok((config, git_client, gitea_client, actions))
}
fn build_config(&self, current_dir: Option<&Path>) -> Result<PleaseConfig, anyhow::Error> {
@@ -183,6 +187,10 @@ impl Command {
)),
}
}
fn get_actions(&self) -> anyhow::Result<Actions> {
Actions::from_cuddle()
}
}
#[derive(Debug, Clone, Subcommand)]

View File

@@ -1,3 +1,6 @@
use std::path::Path;
use cuddle_please_actions::Actions;
use cuddle_please_frontend::PleaseConfig;
use ::semver::Version;
@@ -13,6 +16,7 @@ pub struct ReleaseCommandHandler {
config: PleaseConfig,
git_client: VcsClient,
gitea_client: DynRemoteGitClient,
actions: Actions,
}
impl ReleaseCommandHandler {
@@ -21,12 +25,14 @@ impl ReleaseCommandHandler {
config: PleaseConfig,
git_client: VcsClient,
gitea_client: DynRemoteGitClient,
actions: Actions,
) -> Self {
Self {
ui,
config,
git_client,
gitea_client,
actions,
}
}
@@ -69,6 +75,8 @@ impl ReleaseCommandHandler {
let (changelog_placement, changelog, changelog_last_changes) =
compose_changelog(&commit_strs, &next_version, source)?;
self.actions.execute(&next_version)?;
if let Some(first_commit) = commit_strs.first() {
if first_commit.contains("chore(release): ") {
tracing::trace!("creating release");
@@ -122,6 +130,7 @@ impl ReleaseCommandHandler {
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn create_pull_request(
&self,
changelog_placement: std::path::PathBuf,
@@ -219,7 +228,7 @@ impl ReleaseCommandHandler {
fn compose_changelog(
commit_strs: &Vec<String>,
next_version: &Version,
source: &std::path::PathBuf,
source: &Path,
) -> Result<(std::path::PathBuf, String, Option<String>), anyhow::Error> {
let builder = ChangeLogBuilder::new(commit_strs, next_version.to_string()).build();
let changelog_placement = source.join("CHANGELOG.md");

View File

@@ -6,14 +6,14 @@ readme = "../../README.md"
license-file = "../../LICENSE"
version = "0.1.0"
edition = "2021"
publishable = true
publish = true
[dependencies]
anyhow.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
clap.workspace = true
dotenv.workspace = true
dotenvy.workspace = true
serde_yaml.workspace = true
serde.workspace = true
chrono.workspace = true

View File

@@ -28,14 +28,8 @@ pub struct PleaseConfigBuilder {
impl PleaseConfigBuilder {
pub fn merge(&mut self, config: &PleaseConfigBuilder) -> &Self {
let config = config.clone();
let mut fproject = match self.project.clone() {
None => PleaseProjectConfigBuilder::default(),
Some(project) => project,
};
let mut fsettings = match self.settings.clone() {
None => PleaseSettingsConfigBuilder::default(),
Some(settings) => settings,
};
let mut fproject = self.project.clone().unwrap_or_default();
let mut fsettings = self.settings.clone().unwrap_or_default();
if let Some(project) = config.project {
if let Some(owner) = project.owner {

View File

@@ -6,7 +6,7 @@ readme = "../../README.md"
license-file = "../../LICENSE"
version = "0.1.0"
edition = "2021"
publishable = true
publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -15,7 +15,7 @@ anyhow.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
clap.workspace = true
dotenv.workspace = true
dotenvy.workspace = true
serde_yaml.workspace = true
serde.workspace = true
reqwest = { workspace = true, features = ["blocking", "json"] }
@@ -29,6 +29,9 @@ chrono.workspace = true
lazy_static.workspace = true
parse-changelog.workspace = true
# Cliff depends on 13.1.0, which is a broken release
cacache = "=13.0.0"
[dev-dependencies]
tracing-test = { workspace = true, features = ["no-env-filter"] }
pretty_assertions.workspace = true

View File

@@ -3,7 +3,7 @@ use chrono::{DateTime, NaiveDate, Utc};
use git_cliff_core::{
changelog::Changelog,
commit::Commit,
config::{ChangelogConfig, CommitParser, Config, GitConfig},
config::{Bump, ChangelogConfig, CommitParser, Config, GitConfig, RemoteConfig},
release::Release,
};
use regex::Regex;
@@ -77,6 +77,9 @@ impl ChangeLogBuilder {
commit_id: None,
timestamp,
previous: None,
message: None,
repository: None,
extra: None,
},
config: self.config,
release_link: self.release_link,
@@ -86,7 +89,7 @@ impl ChangeLogBuilder {
fn release_timestamp(&self) -> i64 {
self.release_date
.and_then(|date| date.and_hms_opt(0, 0, 0))
.map(|d| DateTime::<Utc>::from_utc(d, Utc))
.map(|d| DateTime::<Utc>::from_naive_utc_and_offset(d, Utc))
.unwrap_or_else(Utc::now)
.timestamp()
}
@@ -139,6 +142,8 @@ impl ChangeLog<'_> {
let config = Config {
changelog: default_changelog_config(None, self.release_link.as_deref()),
git: default_git_config(),
remote: RemoteConfig::default(),
bump: Bump::default(),
};
config
@@ -148,6 +153,8 @@ impl ChangeLog<'_> {
let config = Config {
changelog: default_changelog_config(header, self.release_link.as_deref()),
git: default_git_config(),
remote: RemoteConfig::default(),
bump: Bump::default(),
};
config
@@ -173,6 +180,10 @@ fn default_commit_parsers() -> Vec<CommitParser> {
default_scope: None,
scope: None,
skip: None,
field: None,
pattern: None,
sha: None,
footer: None,
}
}
@@ -191,6 +202,10 @@ fn default_commit_parsers() -> Vec<CommitParser> {
default_scope: None,
skip: None,
scope: None,
field: None,
pattern: None,
sha: None,
footer: None,
},
]
}
@@ -210,6 +225,9 @@ fn default_changelog_config(header: Option<String>, release_link: Option<&str>)
body: Some(default_changelog_body_config(release_link)),
footer: None,
trim: Some(true),
postprocessors: None,
render_always: None,
output: None,
}
}
@@ -225,6 +243,11 @@ fn default_changelog_body_config(release_link: Option<&str>) -> String {
{% else -%}
- {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message }}
{% endif -%}
{%- if commit.body -%}
{%- if commit.body | length > 0 -%}
{% raw %} {% endraw %}{{ commit.body | trim }}
{% endif -%}
{% endif -%}
{% endfor -%}
{% endfor %}"#;
@@ -497,7 +520,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#[test]
fn generates_changelog() {
let commits: Vec<&str> = vec![
"feat: some feature",
"feat: some feature
some body",
"some random commit",
"fix: some fix",
"chore(scope): some chore",
@@ -518,6 +543,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- some feature
some body
### Fixed
- some fix

View File

@@ -79,6 +79,13 @@ impl VcsClient {
let stdout = std::str::from_utf8(&checkout_branch.stdout)?;
let stderr = std::str::from_utf8(&checkout_branch.stderr)?;
tracing::debug!(stdout = stdout, stderr = stderr, "git {}", args.join(" "));
let exit_code = checkout_branch.status;
if !exit_code.success() {
anyhow::bail!(
"failed to run git command: {}",
exit_code.code().unwrap_or(-1)
)
}
}
}

View File

@@ -517,6 +517,7 @@ impl TryFrom<Tag> for Version {
tracing::trace!(name = &value.name, "parsing tag into version");
value
.name
.trim_start_matches("v")
.parse::<Version>()
.context("could not get version from tag")
}
@@ -601,7 +602,7 @@ mod test {
let (expected, actual) = get_commits("second-sha".into()).unwrap();
assert_eq!(
expected.get(0).unwrap().clone().as_slice(),
expected.first().unwrap().clone().as_slice(),
actual.as_slice()
);
}

View File

@@ -1,5 +1,6 @@
use crate::RemoteGitEngine;
#[derive(Default)]
pub struct LocalGitClient {}
impl LocalGitClient {

View File

@@ -0,0 +1,31 @@
[package]
name = "cuddle-please-release-strategy"
description = "A release-please inspired release manager tool, built on top of cuddle, but also useful standalone, cuddle-please supports, your ci of choice, as well as gitea, github"
repository = "https://git.front.kjuulh.io/kjuulh/cuddle-please"
version = "0.1.0"
edition = "2021"
readme = "../../README.md"
license-file = "../../LICENSE"
publish = true
[dependencies]
anyhow.workspace = true
tracing.workspace = true
serde.workspace = true
semver.workspace = true
[dev-dependencies]
tracing-test = { workspace = true, features = ["no-env-filter"] }
pretty_assertions.workspace = true
tempdir.workspace = true
[features]
rust-workspace = []
rust-crate = []
toml-edit = []
json-edit = []
yaml-edit = []
default = [
"json-edit"
]

View File

@@ -0,0 +1,60 @@
use std::path::Path;
use anyhow::Context;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct JsonEditOptions {
pub jq: String,
}
impl JsonEditOptions {
pub fn execute(&self, path: &Path, next_version: impl AsRef<str>) -> anyhow::Result<()> {
let next_version = next_version.as_ref();
if !path.exists() {
anyhow::bail!("could not find file at: {}", path.display());
}
if let Ok(metadata) = path.metadata() {
if !metadata.is_file() {
anyhow::bail!("{} is not a file", path.display());
}
}
let abs_path = path.canonicalize().context(anyhow::anyhow!(
"could not get absolute path from {}",
path.display()
))?;
let output = std::process::Command::new("jq")
.arg("--arg")
.arg("version")
.arg(next_version)
.arg(&self.jq)
.arg(
abs_path
.to_str()
.ok_or(anyhow::anyhow!("path contains non utf-8 chars"))?,
)
.output()
.context(anyhow::anyhow!(
"failed to run jq on file, jq may not be installed or query was invalid"
))?;
if !output.status.success() {
let err_content = std::str::from_utf8(output.stderr.as_slice())?;
anyhow::bail!("failed to run jq with output: {}", err_content);
}
let edited_json_content = std::str::from_utf8(output.stdout.as_slice())?;
tracing::trace!(
new_content = edited_json_content,
file = &abs_path.display().to_string(),
"applied jq to file"
);
std::fs::write(abs_path, edited_json_content)?;
Ok(())
}
}

View File

@@ -0,0 +1,6 @@
#[cfg(feature = "json-edit")]
mod json_edit;
mod strategy;
#[cfg(feature = "json-edit")]
pub use json_edit::JsonEditOptions;

View File

@@ -0,0 +1,60 @@
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
#[allow(dead_code)]
pub struct UpdateOptions {
next_version: String,
global_changelog: String,
}
#[allow(dead_code)]
pub type Projects = Vec<Project>;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Project {
path: Option<PathBuf>,
r#type: ProjectType,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(tag = "type")]
pub enum ProjectType {
#[cfg(feature = "rust-workspace")]
#[serde(alias = "rust_workspace")]
RustWorkspace,
#[cfg(feature = "rust-crate")]
#[serde(alias = "rust_crate")]
RustCrate,
#[cfg(feature = "toml-edit")]
#[serde(alias = "toml_edit")]
TomlEdit,
#[cfg(feature = "yaml-edit")]
#[serde(alias = "yaml_edit")]
YamlEdit,
#[cfg(feature = "json-edit")]
#[serde(alias = "json_edit")]
JsonEdit,
}
#[allow(dead_code)]
impl Project {
pub fn new(path: Option<PathBuf>, r#type: ProjectType) -> Self {
Self { path, r#type }
}
pub fn execute(&self, _options: &UpdateOptions) -> anyhow::Result<()> {
match self.r#type {
#[cfg(feature = "rust-workspace")]
ProjectType::RustWorkspace => todo!(),
#[cfg(feature = "rust-crate")]
ProjectType::RustCrate => todo!(),
#[cfg(feature = "toml-edit")]
ProjectType::TomlEdit => todo!(),
#[cfg(feature = "yaml-edit")]
ProjectType::YamlEdit => todo!(),
#[cfg(feature = "json-edit")]
ProjectType::JsonEdit => todo!(),
}
}
}

View File

@@ -0,0 +1,50 @@
use tracing_test::traced_test;
#[test]
#[traced_test]
#[cfg(feature = "json-edit")]
pub fn test_can_update_version_in_jq() {
use cuddle_please_release_strategy::JsonEditOptions;
let dir = tempdir::TempDir::new("can_update_version_in_jq").unwrap();
let dir_path = dir.path();
let json_file = dir_path.join("some-test.json");
let initial_content = r#"{
"some": {
"nested": [
{
"structure": {
"version": "v1.0.1"
}
}
]
}
}
"#;
let expected = r#"{
"some": {
"nested": [
{
"structure": {
"version": "v1.0.2"
}
}
]
}
}
"#;
std::fs::write(&json_file, initial_content).unwrap();
let actual_file = std::fs::read_to_string(&json_file).unwrap();
pretty_assertions::assert_eq!(initial_content, actual_file);
let edit_options = JsonEditOptions {
jq: r#".some.nested[].structure.version=$version"#.into(),
};
edit_options.execute(&json_file, "v1.0.2").unwrap();
let actual_file = std::fs::read_to_string(&json_file).unwrap();
pretty_assertions::assert_eq!(expected, &actual_file);
}

View File

@@ -6,7 +6,7 @@ readme = "../../README.md"
license-file = "../../LICENSE"
version = "0.1.0"
edition = "2021"
publishable = true
publish = true
[dependencies]
cuddle-please-frontend.workspace = true
@@ -17,7 +17,7 @@ anyhow.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
clap.workspace = true
dotenv.workspace = true
dotenvy.workspace = true
serde_yaml.workspace = true
serde.workspace = true
reqwest = { workspace = true, features = ["blocking", "json"] }

View File

@@ -1,14 +1,12 @@
use cuddle_please_commands::PleaseCommand;
fn main() -> anyhow::Result<()> {
dotenv::dotenv().ok();
dotenvy::dotenv().ok();
let current_dir = std::env::current_dir().ok();
let current_dir = current_dir.as_deref();
PleaseCommand::new().execute(current_dir)?;
//something else asdfa 123
Ok(())
}

View File

@@ -6,6 +6,7 @@ use tracing_test::traced_test;
use crate::common::{assert_output, get_test_data_path};
#[allow(dead_code)]
fn get_base_args<'a>() -> Vec<&'a str> {
vec![
"cuddle-please",
@@ -25,7 +26,7 @@ PleaseConfig
api_url: https://some-example.gitea-instance
"#;
#[test]
#[allow(dead_code)]
#[traced_test]
fn test_config_from_current_dir() {
let args = get_base_args();
@@ -39,7 +40,7 @@ fn test_config_from_current_dir() {
assert_output(ui, EXPECTED_OUTPUT, "");
}
#[test]
#[allow(dead_code)]
#[traced_test]
fn test_config_from_source_dir() {
let mut args = get_base_args();
@@ -55,7 +56,7 @@ fn test_config_from_source_dir() {
assert_output(ui, EXPECTED_OUTPUT, "");
}
#[test]
#[allow(dead_code)]
#[traced_test]
fn test_config_from_stdin() {
let mut args = get_base_args();
@@ -75,7 +76,7 @@ settings:
assert_output(ui, EXPECTED_OUTPUT, "");
}
#[test]
#[allow(dead_code)]
#[traced_test]
fn test_config_fails_when_not_path_is_set() {
let args = get_base_args();

View File

@@ -16,15 +16,18 @@ fn test_vcs_get_noop() {
#[traced_test]
fn test_vcs_get_git_found() {
let testdata = get_test_data_path("git-found");
if let Err(e) = std::fs::create_dir_all(&testdata) {
tracing::error!("failed to create dir: {}", e);
}
if let Err(e) = std::process::Command::new("git")
.arg("init")
.arg(".")
.current_dir(&testdata)
.output()
{
println!("{e}");
println!("testdata git dir not found: {e}");
}
return;
let git = VcsClient::new_git(&testdata, None::<String>, None::<String>, "".into()).unwrap();
assert_eq!(
git,

View File

@@ -16,6 +16,18 @@ please:
branch: main
settings:
api_url: https://git.front.kjuulh.io
actions:
rust:
components:
packages:
debian:
dev:
- jq
- git
release:
- jq
- git
scripts:
"mkdocs:new":
@@ -34,4 +46,4 @@ scripts:
type: shell
"ci:release":
type: shell

View File

@@ -12,6 +12,4 @@ fi
$CMD_PREFIX pull-request \
--mkdocs-image "$MKDOCS_IMAGE" \
--caddy-image "$CADDY_IMAGE" \
--image "$REGISTRY/$SERVICE" \
--tag "main-$(date +%s)" \
--bin-name "$SERVICE"

17
scripts/ci:release.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -e
CMD_PREFIX="cargo run -p ci --"
if [[ -n "$CI_PREFIX" ]]; then
CMD_PREFIX="$CI_PREFIX"
fi
$CMD_PREFIX release \
--mkdocs-image "$MKDOCS_IMAGE" \
--caddy-image "$CADDY_IMAGE" \
--image "$REGISTRY/$SERVICE" \
--tag "$DRONE_TAG" \
--bin-name "$SERVICE"