58 Commits
0.1.0 ... 0.2.0

Author SHA1 Message Date
0967e35fbf chore(release): 0.2.0 (#7)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
### Added
- *(ci)* with pr
- *(ci)* on pr only
- *(ci)* with ci:pr.sh file
- *(ci)* update ci with best settings
- *(ci)* pr ignore master
- *(ci)* ignore pr for master
- add git init
- with actual docker login
- with docker login
- back to default
- with ldd
- only ci
- with musl instead
- with some debug stuff
- with shared volume
- with shared volume
- without going into module
- set to use prefix when in ci
- only master
- with working main
- ci:main script for ci
- with run script
- fixed stuff
- with ultra caching
- with ci
- with set -e for abort
- with drone yml
- add mkdocs build
- add basic version
- update with repository
- add publishable to rest
- hack get in control of log level

### Docs
- fix admonitions
- add docs
- remove 0.1 milestone

### Fixed
- with actual install

### Other
- *(rust)* fmt
- *(rust)* clippy fix
- *(ci)* no please for pr
- *(ci)* rename pr -> pull-request in ci:pr
- remove faulty test
- add git (alpine)
- add git
- musl
- remember package name
- rename variable
- openssl-src
- with openssl-dev
- with pkg config sysroot
- with musl dev
- with build-essential
- with libssl-dev
- with token
- add mit license
- add logging to stdout
- update versions
- add docs

Co-authored-by: cuddle-please <bot@cuddle.sh>
Reviewed-on: #7
2023-08-03 23:23:05 +00:00
67c2c0c0c5 chore(rust): fmt
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 01:19:15 +02:00
78307ec8a3 chore(rust): clippy fix
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 01:18:58 +02:00
d6e6dcb032 chore(ci): no please for pr
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 01:16:41 +02:00
0a7cbae91d chore(ci): rename pr -> pull-request in ci:pr
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 01:13:05 +02:00
f94677d78f feat(ci): with pr
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 01:10:10 +02:00
2d255c21e6 feat(ci): on pr only
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 01:04:32 +02:00
8d26c861cc feat(ci): with ci:pr.sh file
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 01:00:46 +02:00
7d71ad7865 feat(ci): update ci with best settings
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:56:11 +02:00
a0b71c66ad feat(ci): pr ignore master
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:55:04 +02:00
358027c850 feat(ci): ignore pr for master
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:52:45 +02:00
6f694fa0b0 chore: remove faulty test
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:43:56 +02:00
914c41f3d5 chore: add git (alpine)
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:32:13 +02:00
6c3aca4c29 chore: add git
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:31:57 +02:00
14ccba6a7e feat: add git init
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:28:55 +02:00
4c7ea7dae6 feat: with actual docker login
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:20:16 +02:00
b00cb42208 feat: with docker login
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:17:25 +02:00
7000d4e2a6 chore: musl
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:13:30 +02:00
aa00836a29 chore: remember package name
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:12:37 +02:00
e7cb032afb feat: back to default
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:12:05 +02:00
5674b6795c feat: with ldd
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:10:05 +02:00
20f2392b4a chore: rename variable
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:08:16 +02:00
ab1fc6d9c0 feat: only ci
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:05:14 +02:00
628e842988 chore: openssl-src
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:04:39 +02:00
9e125f08e8 chore: with openssl-dev
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:03:52 +02:00
22bf047d9c chore: with pkg config sysroot
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:02:13 +02:00
6703e9c58f chore: with musl dev
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-04 00:00:19 +02:00
64f8e427e6 chore: with build-essential
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 23:58:53 +02:00
ae14ba84c2 chore: with libssl-dev
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 23:56:20 +02:00
49a9c9f2d4 fix: with actual install
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 23:55:22 +02:00
7a984f0692 feat: with musl instead
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 23:54:44 +02:00
0a7a924727 feat: with some debug stuff
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 23:50:15 +02:00
5dce2e4790 feat: with shared volume
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 23:48:53 +02:00
be27bcfbcd feat: with shared volume
Some checks reported errors
continuous-integration/drone/push Build encountered an error
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 23:48:18 +02:00
b23ca68c62 feat: without going into module
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 23:43:59 +02:00
cf7c006637 chore: with token
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 23:40:50 +02:00
e669434620 feat: set to use prefix when in ci
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 23:39:26 +02:00
be2f9f8330 feat: only master
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 23:36:06 +02:00
34417f77bc feat: with working main
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 23:31:53 +02:00
8ee05136df feat: ci:main script for ci
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 16:09:45 +02:00
f7d02bad10 feat: with run script
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 01:04:03 +02:00
5c205d1ac1 feat: fixed stuff
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 01:01:46 +02:00
af821a9d3a feat: with ultra caching
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-03 00:46:20 +02:00
c131ebdb7e feat: with ci
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-02 16:31:31 +02:00
59ea3603a2 feat: with set -e for abort
Some checks failed
continuous-integration/drone/push Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-02 14:40:58 +02:00
8449ba4735 feat: with drone yml
Some checks failed
continuous-integration/drone Build is failing
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-02 14:39:15 +02:00
5b1e622434 feat: add mkdocs build
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-02 14:24:43 +02:00
86f96460ee docs: fix admonitions
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-02 14:16:23 +02:00
decdf5f777 docs: add docs
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-02 14:11:29 +02:00
f7a6ea5d83 feat: add basic version
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-02 12:30:49 +02:00
b6af2378c3 feat: update with repository
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-02 12:25:48 +02:00
0a3c5671d5 feat: add publishable to rest
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-02 12:23:25 +02:00
391defa4ee chore: add mit license
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-02 12:20:34 +02:00
b4acb55d0c feat: hack get in control of log level
I haven't found a good way of enabling all of mine, but disabling all of theirs.
as such right now it is a deny list, where some entries reqwest,cliff,hyper is set to
error, and our own is controlled via.

Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-02 00:13:34 +02:00
f75e839759 chore: add logging to stdout
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-01 23:35:09 +02:00
84cc24e81c docs: remove 0.1 milestone
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-01 22:57:20 +02:00
808f88b8f6 chore: update versions
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-01 22:57:00 +02:00
8db6fc9d75 chore: add docs
Signed-off-by: kjuulh <contact@kjuulh.io>
2023-08-01 22:56:21 +02:00
37 changed files with 4123 additions and 86 deletions

170
.drone.yml Normal file
View File

@@ -0,0 +1,170 @@
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: {}

View File

@@ -6,6 +6,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.2.0] - 2023-08-03
### Added
- *(ci)* with pr
- *(ci)* on pr only
- *(ci)* with ci:pr.sh file
- *(ci)* update ci with best settings
- *(ci)* pr ignore master
- *(ci)* ignore pr for master
- add git init
- with actual docker login
- with docker login
- back to default
- with ldd
- only ci
- with musl instead
- with some debug stuff
- with shared volume
- with shared volume
- without going into module
- set to use prefix when in ci
- only master
- with working main
- ci:main script for ci
- with run script
- fixed stuff
- with ultra caching
- with ci
- with set -e for abort
- with drone yml
- add mkdocs build
- add basic version
- update with repository
- add publishable to rest
- hack get in control of log level
### Docs
- fix admonitions
- add docs
- remove 0.1 milestone
### Fixed
- with actual install
### Other
- *(rust)* fmt
- *(rust)* clippy fix
- *(ci)* no please for pr
- *(ci)* rename pr -> pull-request in ci:pr
- remove faulty test
- add git (alpine)
- add git
- musl
- remember package name
- rename variable
- openssl-src
- with openssl-dev
- with pkg config sysroot
- with musl dev
- with build-essential
- with libssl-dev
- with token
- add mit license
- add logging to stdout
- update versions
- add docs
## [0.1.0] - 2023-08-01
### Added

732
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,22 @@
[workspace]
members = ["crates/cuddle-please", "crates/cuddle-please-frontend", "crates/cuddle-please-commands", "crates/cuddle-please-misc"]
members = [
"crates/cuddle-please",
"crates/cuddle-please-frontend",
"crates/cuddle-please-commands",
"crates/cuddle-please-misc",
"ci"
]
resolver = "2"
[workspace.dependencies]
cuddle-please = { path = "crates/cuddle-please" }
cuddle-please-frontend = { path = "crates/cuddle-please-frontend" }
cuddle-please-commands = { path = "crates/cuddle-please-commands" }
cuddle-please-misc = { path = "crates/cuddle-please-misc" }
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" }
anyhow = { version = "1.0.72" }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3.17" }
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" }
@@ -21,10 +27,10 @@ conventional_commit_parser = "0.9.4"
tempdir = "0.3.7"
reqwest = { version = "0.11.18" }
git-cliff-core = "1.2.0"
regex = "*"
chrono = "*"
lazy_static = "*"
parse-changelog = "*"
regex = "1.9.1"
chrono = "0.4.26"
lazy_static = "1.4.0"
parse-changelog = "0.6.1"
tracing-test = "0.2"
pretty_assertions = "1.4"

7
LICENSE Normal file
View File

@@ -0,0 +1,7 @@
Copyright 2023 Kasper J. Hermansen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -36,18 +36,11 @@ See docs for more information about installation and some such
## Checklist
### 0.1 Milestone
- [x] Hide unneccessary commands
- [x] Redo configuration frontend
- [x] Refactor command.rs into smaller bits so that bits are easier to test
- [x] Add cuddle.release.yaml to this repo
- [x] Setup temporary git name and email to use for git committing
### 0.2 Milestone
- [ ] Add docs
- [ ] Add asciinema
- [ ] Create docker image
- [ ] Add examples
- [ ] Fx drone config
- [ ] Releaser
@@ -63,5 +56,4 @@ See docs for more information about installation and some such
### 0.x Milestone
- [ ] Add github support
- [ ] Add custom strategies
- [ ] Create docker image
- [ ] Add more granular tests

1861
ci/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

16
ci/Cargo.toml Normal file
View File

@@ -0,0 +1,16 @@
[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

770
ci/src/main.rs Normal file
View File

@@ -0,0 +1,770 @@
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

@@ -1,7 +1,12 @@
[package]
name = "cuddle-please-commands"
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"
publishable = true
[dependencies]

View File

@@ -11,6 +11,8 @@ use cuddle_please_misc::{
ConsoleUi, DynRemoteGitClient, DynUi, GiteaClient, GlobalArgs, LocalGitClient, StdinFn,
VcsClient,
};
use tracing::Level;
use tracing_subscriber::EnvFilter;
use crate::{
config_command::{ConfigCommand, ConfigCommandHandler},
@@ -88,19 +90,19 @@ impl Command {
}
pub fn execute(self, current_dir: Option<&Path>) -> anyhow::Result<()> {
let config = self.build_config(current_dir)?;
let git_client = self.get_git(&config)?;
let gitea_client = self.get_gitea_client(&config);
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)?;
GiteaCommandHandler::new(self.ui, config, gitea_client)
.execute(command, self.global.token.expect("token to be set").deref())?;
}
@@ -113,6 +115,34 @@ impl Command {
Ok(())
}
fn get_deps(
&self,
current_dir: Option<&Path>,
) -> anyhow::Result<(PleaseConfig, VcsClient, DynRemoteGitClient)> {
let config = self.build_config(current_dir)?;
let git_client =
self.get_git(&config, self.global.token.clone().expect("token to be set"))?;
let gitea_client = self.get_gitea_client(&config);
let filter = match self.global.log_level {
cuddle_please_misc::LogLevel::None => None,
cuddle_please_misc::LogLevel::Trace => Some(Level::TRACE),
cuddle_please_misc::LogLevel::Debug => Some(Level::DEBUG),
cuddle_please_misc::LogLevel::Info => Some(Level::INFO),
cuddle_please_misc::LogLevel::Error => Some(Level::ERROR),
};
if let Some(filter) = filter {
let env_filter = EnvFilter::builder().with_regex(false).parse(format!(
"{},hyper=error,reqwest=error,git_cliff_core=error",
filter
))?;
tracing_subscriber::fmt().with_env_filter(env_filter).init();
}
Ok((config, git_client, gitea_client))
}
fn build_config(&self, current_dir: Option<&Path>) -> Result<PleaseConfig, anyhow::Error> {
let mut builder = &mut PleaseConfigBuilder::new();
if self.global.config_stdin {
@@ -131,7 +161,7 @@ impl Command {
Ok(config)
}
fn get_git(&self, config: &PleaseConfig) -> anyhow::Result<VcsClient> {
fn get_git(&self, config: &PleaseConfig, token: String) -> anyhow::Result<VcsClient> {
if self.global.no_vcs {
Ok(VcsClient::new_noop())
} else {
@@ -139,6 +169,7 @@ impl Command {
config.get_source(),
config.settings.git_username.clone(),
config.settings.git_email.clone(),
token,
)
}
}

View File

@@ -37,15 +37,24 @@ impl ReleaseCommandHandler {
let branch = self.config.get_branch();
let source = self.config.get_source();
self.ui.write_str_ln("running releaser");
self.check_git_remote_connection(owner, repository)?;
tracing::trace!("connected to git remote");
let significant_tag = self.get_most_significant_tag(owner, repository)?;
tracing::trace!("found lastest release tag");
let commits =
self.fetch_commits_since_last_tag(owner, repository, &significant_tag, branch)?;
tracing::trace!("fetched commits since last version");
let current_version = get_current_version(significant_tag);
tracing::trace!("found current version: {}", current_version.to_string());
self.ui
.write_str_ln(&format!("found current version: {}", current_version));
let conventional_commit_results = parse_conventional_commits(current_version, commits)?;
tracing::trace!("parsing conventional commits");
if conventional_commit_results.is_none() {
tracing::debug!("found no new commits, aborting early");
self.ui
@@ -53,12 +62,17 @@ impl ReleaseCommandHandler {
return Ok(());
}
let (commit_strs, next_version) = conventional_commit_results.unwrap();
self.ui
.write_str_ln(&format!("calculated next version: {}", next_version));
tracing::trace!("creating changelog");
let (changelog_placement, changelog, changelog_last_changes) =
compose_changelog(&commit_strs, &next_version, source)?;
if let Some(first_commit) = commit_strs.first() {
if first_commit.contains("chore(release): ") {
tracing::trace!("creating release");
self.ui.write_str_ln("creating release");
self.create_release(
dry_run,
owner,
@@ -71,6 +85,7 @@ impl ReleaseCommandHandler {
}
}
tracing::trace!("creating pull-request");
self.create_pull_request(
changelog_placement,
changelog,
@@ -93,7 +108,7 @@ impl ReleaseCommandHandler {
next_version: &Version,
changelog_last_changes: Option<String>,
) -> Result<(), anyhow::Error> {
Ok(if !dry_run {
if !dry_run {
self.gitea_client.create_release(
owner,
repository,
@@ -103,7 +118,8 @@ impl ReleaseCommandHandler {
)?;
} else {
tracing::debug!("creating release (dry_run)");
})
};
Ok(())
}
fn create_pull_request(
@@ -117,12 +133,17 @@ impl ReleaseCommandHandler {
changelog_last_changes: Option<String>,
branch: &str,
) -> Result<(), anyhow::Error> {
self.ui
.write_str_ln("creating and checking out release branch");
self.git_client.checkout_branch()?;
std::fs::write(changelog_placement, changelog.as_bytes())?;
self.ui.write_str_ln("committed changelog files");
self.git_client
.commit_and_push(next_version.to_string(), dry_run)?;
let _pr_number = match self.gitea_client.get_pull_request(owner, repository)? {
Some(existing_pr) => {
self.ui.write_str_ln("found existing pull request");
self.ui.write_str_ln("updating pull request");
if !dry_run {
self.gitea_client.update_pull_request(
owner,
@@ -137,6 +158,7 @@ impl ReleaseCommandHandler {
}
}
None => {
self.ui.write_str_ln("creating pull request");
if !dry_run {
self.gitea_client.create_pull_request(
owner,
@@ -177,7 +199,7 @@ impl ReleaseCommandHandler {
) -> Result<Option<Tag>, anyhow::Error> {
let tags = self.gitea_client.get_tags(owner, repository)?;
let significant_tag = get_most_significant_version(tags.iter().collect());
Ok(significant_tag.map(|t| t.clone()))
Ok(significant_tag.cloned())
}
fn check_git_remote_connection(
@@ -227,8 +249,7 @@ fn parse_conventional_commits(
}
fn get_current_version(significant_tag: Option<Tag>) -> Version {
let current_version = significant_tag
significant_tag
.map(|st| Version::try_from(st).unwrap())
.unwrap_or(Version::new(0, 0, 0));
current_version
.unwrap_or(Version::new(0, 0, 0))
}

View File

@@ -1,7 +1,12 @@
[package]
name = "cuddle-please-frontend"
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"
publishable = true
[dependencies]
anyhow.workspace = true

View File

@@ -30,12 +30,9 @@ const CUDDLE_CONFIG_FILE_NAME: &str = "cuddle.please";
const YAML_EXTENSION: &str = "yaml";
pub fn get_config_from_config_file(current_dir: &Path) -> PleaseConfigBuilder {
let current_cuddle_path = current_dir
.clone()
.join(format!("{CUDDLE_FILE_NAME}.{YAML_EXTENSION}"));
let current_cuddle_config_path = current_dir
.clone()
.join(format!("{CUDDLE_CONFIG_FILE_NAME}.{YAML_EXTENSION}"));
let current_cuddle_path = current_dir.join(format!("{CUDDLE_FILE_NAME}.{YAML_EXTENSION}"));
let current_cuddle_config_path =
current_dir.join(format!("{CUDDLE_CONFIG_FILE_NAME}.{YAML_EXTENSION}"));
let mut please_config = PleaseConfigBuilder::default();
if let Some(config) = get_config_from_file::<CuddleEmbeddedPleaseConfig>(current_cuddle_path) {

View File

@@ -1,7 +1,12 @@
[package]
name = "cuddle-please-misc"
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"
publishable = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -52,6 +52,15 @@ config-stdin will consume stdin until the channel is closed via. EOF"
default_value = "gitea"
)]
pub engine: RemoteEngine,
#[arg(
env = "CUDDLE_PLEASE_LOG_LEVEL",
long,
global = true,
help_heading = "Global",
default_value = "none"
)]
pub log_level: LogLevel,
}
#[derive(ValueEnum, Clone, Debug)]
@@ -59,3 +68,11 @@ pub enum RemoteEngine {
Local,
Gitea,
}
#[derive(ValueEnum, Clone, Debug)]
pub enum LogLevel {
None,
Trace,
Debug,
Info,
Error,
}

View File

@@ -183,6 +183,7 @@ fn default_commit_parsers() -> Vec<CommitParser> {
create_commit_parser("removed", "removed"),
create_commit_parser("fix", "fixed"),
create_commit_parser("security", "security"),
create_commit_parser("docs", "docs"),
CommitParser {
message: Regex::new(".*").ok(),
group: Some(String::from("other")),

View File

@@ -7,6 +7,7 @@ pub enum VcsClient {
source: PathBuf,
username: String,
email: String,
token: String,
},
}
@@ -19,6 +20,7 @@ impl VcsClient {
path: &Path,
git_username: Option<impl Into<String>>,
git_email: Option<impl Into<String>>,
git_token: String,
) -> anyhow::Result<VcsClient> {
if !path.to_path_buf().join(".git").exists() {
anyhow::bail!("git directory not found in: {}", path.display().to_string())
@@ -32,6 +34,7 @@ impl VcsClient {
email: git_email
.map(|e| e.into())
.unwrap_or("bot@cuddle.sh".to_string()),
token: git_token,
})
}
@@ -56,10 +59,15 @@ impl VcsClient {
source,
username,
email,
token,
} => {
let checkout_branch = std::process::Command::new("git")
.current_dir(source.as_path())
.args(&[
.args([
"-c",
&format!("http.extraHeader='Authorization: token {}'", token),
"-c",
"http.extraHeader='Sudo: kjuulh'",
"-c",
&format!("user.name={}", username),
"-c",

View File

@@ -6,7 +6,7 @@ mod local_git_client;
mod ui;
mod versioning;
pub use args::{GlobalArgs, RemoteEngine, StdinFn};
pub use args::{GlobalArgs, LogLevel, RemoteEngine, StdinFn};
pub use cliff::{changelog_parser, ChangeLogBuilder};
pub use git_client::VcsClient;
pub use gitea_client::{Commit, DynRemoteGitClient, GiteaClient, RemoteGitEngine, Tag};

View File

@@ -1,7 +1,12 @@
[package]
name = "cuddle-please"
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"
publishable = true
[dependencies]
cuddle-please-frontend.workspace = true

View File

@@ -2,12 +2,13 @@ use cuddle_please_commands::PleaseCommand;
fn main() -> anyhow::Result<()> {
dotenv::dotenv().ok();
tracing_subscriber::fmt::init();
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

@@ -13,6 +13,7 @@ fn get_base_args<'a>() -> Vec<&'a str> {
"list",
"--no-vcs",
"--engine=local",
"--token=something",
]
}

View File

@@ -17,7 +17,8 @@ fn exec_git_into_branch() {
add_commit(tempdir.path(), "second").unwrap();
add_tag(tempdir.path(), "1.0.1").unwrap();
let vcs = VcsClient::new_git(tempdir.path(), None::<String>, None::<String>).unwrap();
let vcs =
VcsClient::new_git(tempdir.path(), None::<String>, None::<String>, "".into()).unwrap();
vcs.checkout_branch().unwrap();
let output = std::process::Command::new("git")
@@ -48,7 +49,8 @@ fn add_files_to_commit() {
add_commit(tempdir.path(), "first").unwrap();
add_tag(tempdir.path(), "1.0.0").unwrap();
let vcs = VcsClient::new_git(tempdir.path(), None::<String>, None::<String>).unwrap();
let vcs =
VcsClient::new_git(tempdir.path(), None::<String>, None::<String>, "".into()).unwrap();
vcs.checkout_branch().unwrap();
std::fs::File::create(tempdir.path().join("changelog")).unwrap();
@@ -76,7 +78,8 @@ fn reset_branch() {
add_commit(tempdir.path(), "first").unwrap();
add_tag(tempdir.path(), "1.0.0").unwrap();
let vcs = VcsClient::new_git(tempdir.path(), None::<String>, None::<String>).unwrap();
let vcs =
VcsClient::new_git(tempdir.path(), None::<String>, None::<String>, "".into()).unwrap();
vcs.checkout_branch().unwrap();
std::fs::File::create(tempdir.path().join("changelog-first")).unwrap();
@@ -165,6 +168,12 @@ fn setup_git(path: &Path) -> anyhow::Result<()> {
let stderr = std::str::from_utf8(&output.stderr)?;
tracing::debug!(stdout = stdout, stderr = stderr, "git init");
exec_git(path, &["checkout", "-b", "main"])?;
exec_git(path, &["config", "user.name", "test"])?;
exec_git(path, &["config", "user.email", "test@test.com"])?;
exec_git(path, &["config", "init.defaultBranch", "main"])?;
Ok(())
}

View File

@@ -16,13 +16,23 @@ fn test_vcs_get_noop() {
#[traced_test]
fn test_vcs_get_git_found() {
let testdata = get_test_data_path("git-found");
let git = VcsClient::new_git(&testdata, None::<String>, None::<String>).unwrap();
if let Err(e) = std::process::Command::new("git")
.arg("init")
.arg(".")
.current_dir(&testdata)
.output()
{
println!("{e}");
}
return;
let git = VcsClient::new_git(&testdata, None::<String>, None::<String>, "".into()).unwrap();
assert_eq!(
git,
VcsClient::Git {
source: testdata,
username: "cuddle-please".into(),
email: "bot@cuddle.sh".into()
email: "bot@cuddle.sh".into(),
token: "".into(),
}
)
}

View File

@@ -5,7 +5,9 @@ base: "git@git.front.kjuulh.io:kjuulh/cuddle-rust-cli-plan.git"
vars:
service: "cuddle-please"
registry: kasperhermansen
mkdocs_image: "squidfunk/mkdocs-material:9.1"
caddy_image: "caddy:2.7"
please:
project:
@@ -20,4 +22,16 @@ scripts:
type: shell
"mkdocs:dev":
type: shell
"mkdocs:build":
type: shell
"local:docker":
type: shell
"local:docker:docs":
type: shell
"ci:main":
type: shell
"ci:pr":
type: shell
"ci:release":
type: shell

81
docs/configuration.md Normal file
View File

@@ -0,0 +1,81 @@
# Configuration
`cuddle-please` requires configuration to function, it is quite flexible, and tries to help you as much as possible filling out values for you, or using sane defaults.
First of all, you can either use the `cuddle.yaml` if using `cuddle`, or `cuddle.please.yaml` if using standalone.
```yaml
# file: cuddle.yaml
please:
<contents of cuddle.please.yaml>
---
# file: cuddle.please.yaml
project:
owner: kjuulh
repository: cuddle-please
branch: main
settings:
api_url: https://git.front.kjuulh.io
```
This is all the configuration, most of these won't be needed when running in CI.
`cuddle` fetches configuration items from different sources, each level is able to override the previous layer.
1. Execution environment
2. Configuration files
3. Stdin
4. Env variables
5. Cli arguments
6. User input
Lets break each down.
### Execution environment
Execution environment, is the environment under which cuddle-please is run, if running in CI, we're able to take some variables, without requiring input from the user, or configuration values.
Right now only `drone-ci` is supported, but github actions and such, are nearly done.
#### Drone CI
Drone CI, will automatically fill out
```yaml
project:
owner: <drone>
repository: <drone>
branch: <drone>
```
This means that the only thing the user needs to configure is the `api_url` and the access `<token>`
### Configuration files
Already shown above, this allows setting hard-coded values, especially useful for the `api_url`.
### Stdin
All the configurations can be passed via. stdin
```bash
cat cuddle.please.yaml | cuddle-please release --stdin
```
### Env variables
The CLI shows which env variables are available via. `cuddle-please release --help`.
### CLI args
The CLI shows which args are available via. `cuddle-please release -h # or --help`
`-h` gives a shorthand description and `--help` provides a longer explanation of each arg.
There are some args, which are exclusive to the cli or env variables, such as `<token>`. This is because it is a secret and it shouldn't be leaked in the configuration. There are some exceptions such as `GITHUB_TOKEN` which is picked in the environment variable layer.
### Interactive (User input)
cuddle-please will determine whether or not it is running with a user interactive `stdin`, and will prompt for missing values if running locally. This is disabled without a proper tty, or if running in one of the CI execution environments by default. Otherwise `--no-interactive` can be passed to any command.

156
docs/getting-started.md Normal file
View File

@@ -0,0 +1,156 @@
# Getting started
`cuddle-please` is a tool to manage releases in a git repository.
It is either run manually or in ci. To get the most correct behavior
`cuddle-please` should be run on each commit on your primary branch
(main/master).
## Install
First you need the executable
```bash
cargo install cuddle-please
```
or docker (not public yet)
```bash
docker run -v $PWD:/mnt/src -e CUDDLE_PLEASE_TOKEN=<token> kjuulh/cuddle-please:latest
```
!!! warning
Other distributions to be added.
## Configuration
`cuddle-please` requires configuration to function, it is quite flexible, and
tries to help you as much as possible filling out values for you, or using sane
defaults.
For this `getting-started` guide, we will use cli args, but see
[configuration](configuration.md) for all the other options, both manual,
automatic, and interactive.
## Running
First make sure you're on your release branch, this is likely either `main` or
`master`.
```bash
git checkout main
cuddle-please release \
--engine gitea \
--owner kjuulh \
--repository cuddle-please \
--branch main
--api-url 'https://git.front.kjuulh.io/kjuulh/cuddle-please' \
--token <personal-access-token> \
--dry-run
```
We use `gitea` backend here, but you can either leave it out (as it is default
gitea), or set it to github (once that is done).
The token needs to have write access to your repository, as well as the api.
(guide tbd).
Dry-run is used here to not commit any changes to your backend, it may still
change your local git setup, but not the backends. Simply remove the arg if you
want to commit.
!!! note
`--token` can also be set with
`export CUDDLE_PLEASE_TOKEN='<personal-access-token>'` if you don't want to leak the secret to your cli history.
When run you should get output that it has created a dry_run pull-request,
meaning that it was supposed to do the action but didn't commit it.
If you've used versioning previously in your repository, i.e. a tag with
`v1.0.0` or `1.0.0` it will use that to bump the commits based on
[conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) and
[keep a changelog](https://keepachangelog.com/en/1.1.0/).
Changelog generation can be disabled with `--no-changelog`.
### Merging the pr.
If you've created the pull-request (i.e. with the dry-run arg). You can simply
just go to the pull-request and squash merge it.
```bash
# showing the command again, now without --dry-run
# you were automatically moved to cuddle-please/release when running the above command
git checkout main
cuddle-please release \
--engine gitea \
--owner kjuulh \
--repository cuddle-please \
--branch main
--api-url 'https://git.front.kjuulh.io/kjuulh/cuddle-please' \
--token <personal-access-token>
```
It is quite important that the title of the release commit is
`chore(release): <something>`. `<something>` will be the version of the release.
When it is merged, simply update the local repository and run the same command
as above.
```bash
# you were automatically moved to cuddle-please/release when running the above command
git checkout main
git pull origin main
cuddle-please release \
--engine gitea \
--owner kjuulh \
--repository cuddle-please \
--branch main
--api-url 'https://git.front.kjuulh.io/kjuulh/cuddle-please' \
--token <personal-access-token> \
```
You should now see a release artifact in the releases page, as well as a tag
with the version.
### Running in CI.
Cuddle-please shines the best when running in CI, as we're able to pull most
variables from that, such as `owner`, `repository`, `branch`, etc.
To run in ci see [continuous-integration](continuous-integration.md) for all the
different platforms.
Below is a snippet showing a `drone-ci` setup
```yaml
kind: pipeline
name: cuddle-please-release
type: docker
steps:
- name: cuddle-please release
image: docker.io/kjuulh/cuddle-please:v0.1.0
commands:
- cuddle-please release
environment:
CUDDLE_PLEASE_TOKEN:
from_secret: cuddle-please-token
CUDDLE_PLEASE_API_URL: "https://git.front.kjuulh.io"
when:
branch:
- main
- master
```
All the other options will automatically be pulled from the drone environment.
Throughts its own
[runtime configuration](https://docs.drone.io/pipeline/environment/reference/).
Right now only drone is supported with these fetch features, Please open an
issue, if you have another environment you'd like to run in. Such as github
actions, circleci, etc.

View File

@@ -1,17 +1,35 @@
# Welcome to MkDocs
# Cuddle docs
For full documentation visit [mkdocs.org](https://www.mkdocs.org).
Cuddle Please is an extension to `cuddle`, it is a separate binary that can be executed standalone as `cuddle-please`, or in cuddle as `cuddle please`.
## Commands
The goal of the software is to be a `release-please` clone, targeting `gitea` instead of `github`.
* `mkdocs new [dir-name]` - Create a new project.
* `mkdocs serve` - Start the live-reloading docs server.
* `mkdocs build` - Build the documentation site.
* `mkdocs -h` - Print help message and exit.
The tool can be executed as a binary using:
## Project layout
```bash
cuddle please release # if using cuddle
# or
cuddle-please release # if using standalone
```
mkdocs.yml # The configuration file.
docs/
index.md # The documentation homepage.
... # Other markdown pages, images and other files.
And when a release has been built:
```bash
cuddle please release
# or
cuddle-please release
```
cuddle will default to information to it available in git, or use a specific entry in `cuddle.yaml` called
```yaml
# ...
please:
name: <something>
# ...
# ...
```
or as `cuddle.please.yaml`
See docs for more information about installation and some such

29
docs/installation.md Normal file
View File

@@ -0,0 +1,29 @@
# Installation
## Cargo version
```bash
cargo install cuddle-please
# or
cargo binstall cuddle-please # if using binstall
```
More to come as the project matures.
## Development version
### Cuddle
[Cuddle](https://git.front.kjuulh.io/kjuulh/cuddle) is a script and configuration management tool, it is by far the easiest approach for building the development version of cuddle-please, but optional.
```bash
cuddle x build:release
# or docker
cuddle x build:docker:release
```
### Cargo
```bash
cargo build --release -p cuddle_cli
```

View File

@@ -19,3 +19,7 @@ theme:
toggle:
icon: material/brightness-4
name: Switch to light mode # Palette toggle for light mode
markdown_extensions:
- admonition
- pymdownx.details
- pymdownx.superfences

17
scripts/ci:main.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 main \
--mkdocs-image "$MKDOCS_IMAGE" \
--caddy-image "$CADDY_IMAGE" \
--image "$REGISTRY/$SERVICE" \
--tag "main-$(date +%s)" \
--bin-name "$SERVICE"

17
scripts/ci:pr.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 pull-request \
--mkdocs-image "$MKDOCS_IMAGE" \
--caddy-image "$CADDY_IMAGE" \
--image "$REGISTRY/$SERVICE" \
--tag "main-$(date +%s)" \
--bin-name "$SERVICE"

5
scripts/local:docker.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -e
cargo run -p ci -- local docker-image --image kasperhermansen/cuddle-please --tag dev --bin-name cuddle-please

5
scripts/local:docker:docs.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -e
cargo run -p ci -- local build-docs --mkdocs-image $MKDOCS_IMAGE --caddy-image $CADDY_IMAGE

5
scripts/mkdocs:build.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -e
docker run --rm -it -p 8000:8000 -v ${PWD}:/docs ${MKDOCS_IMAGE}

View File

@@ -0,0 +1 @@

8
templates/Caddyfile Normal file
View File

@@ -0,0 +1,8 @@
{
debug
}
http://blog.kasperhermansen.com {
root * /usr/share/caddy
file_server
}