From 8236ae0642e6154a467a812b2336140802f208b5 Mon Sep 17 00:00:00 2001 From: Konstantin Smetanin Date: Fri, 3 May 2024 12:25:20 +0300 Subject: [PATCH] Init --- .cargo/config.toml | 4 + .env.example | 5 ++ .gitea/workflows/build.yaml | 95 ++++++++++++++++++++++ .gitea/workflows/pull-request.yaml | 122 +++++++++++++++++++++++++++++ .gitignore | 3 + .rusty-hook.toml | 6 ++ .vscode/settings.json | 8 ++ Cargo.toml | 18 +++++ Dockerfile | 52 ++++++++++++ LICENSE.txt | 2 + Makefile | 16 ++++ README.md | 16 ++++ cargo-generate.toml | 13 +++ docker-compose.yaml | 26 ++++++ rust-toolchain.toml | 2 + rustfmt.toml | 40 ++++++++++ src/lib.rs | 11 +++ src/main.rs | 5 ++ tests/pg-connection.rs | 8 ++ 19 files changed, 452 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .env.example create mode 100644 .gitea/workflows/build.yaml create mode 100644 .gitea/workflows/pull-request.yaml create mode 100644 .gitignore create mode 100644 .rusty-hook.toml create mode 100644 .vscode/settings.json create mode 100644 Cargo.toml create mode 100644 Dockerfile create mode 100644 LICENSE.txt create mode 100644 Makefile create mode 100644 README.md create mode 100644 cargo-generate.toml create mode 100644 docker-compose.yaml create mode 100644 rust-toolchain.toml create mode 100644 rustfmt.toml create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 tests/pg-connection.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..526af0a --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,4 @@ +[registries] + +[registries.git_{{git-owner}}] +index = "sparse+https://git.smetan.ru/api/packages/{{git-owner}}/cargo/" diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a64d333 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +DB_DATABASE=test_db +DB_USERNAME=user +DB_PASSWORD=pass +DB_PORT=5432 +DATABASE_URL=postgres://user:pass@localhost:5432/test_db \ No newline at end of file diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..d42b6cb --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,95 @@ +name: "Build {{project-name}}" + +# You must add gitea secrets and enable actions for repository: +# CRATES_TOKEN +# DOCKER_USER +# DOCKER_PASSWORD + +{%- if crate_type == "bin" %} +env: + DOCKER_REGISTRY: git.smetan.ru + IMAGE_NAME: "{{git-owner}}/{{project-name}}" + IMAGE_RELEASE_TAG: latest + IMAGE_PRE_RELEASE_TAG: pre-release +{%- endif %} + +on: +{%- if crate_type == "bin" %} + push: + branches: + - master + - main +{%- endif %} + release: + types: + - published + +jobs: +{%- if crate_type == "bin" -%} +{% raw %} + build_docker_app: + name: Build Docker Image + runs-on: docker-ubuntu-latest + steps: + - name: Checkout sources + uses: https://github.com/actions/checkout@v4 + + - name: Set Image Name + run: | + echo "Event type: $GITHUB_EVENT_NAME" + if [[ $GITHUB_EVENT_NAME == 'release' ]]; then + echo "IMAGE_TAG=$DOCKER_REGISTRY/$IMAGE_NAME:$IMAGE_RELEASE_TAG" >> "$GITHUB_ENV" + else + echo "IMAGE_TAG=$DOCKER_REGISTRY/$IMAGE_NAME:$IMAGE_PRE_RELEASE_TAG" >> "$GITHUB_ENV" + fi + cat $GITHUB_ENV + + - name: Build Image + run: docker build --build-arg CRATES_TOKEN=${{ secrets.CRATES_TOKEN }} --file Dockerfile --tag ${{ env.IMAGE_TAG }} . + + - name: Docker Login + run: docker login --username ${{ secrets.DOCKER_USER }} --password ${{ secrets.DOCKER_PASSWORD }} ${{ env.DOCKER_REGISTRY }} + + - name: Push Image + run: docker push ${{ env.IMAGE_TAG }} + + - name: Docker Logout + run: docker logout ${{ env.DOCKER_REGISTRY }} +{% endraw %} +{%- endif -%} +{%- if crate_type == "lib" -%} +{% raw %} + publish_crate_version: + name: Publish new crate version + runs-on: ubuntu-latest + container: + image: git.smetan.ru/infra/ci-cargo:latest + credentials: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + steps: + - name: Checkout sources + uses: https://github.com/actions/checkout@v4 + + - name: Set cargo registry + run: | + cat <<"EOF" | tee ~/.cargo/config.toml + [registry] + global-credential-providers = ["cargo:token"] + EOF + + - name: Set Cargo version + run: | + export VERSION=`sed 's/v//'<<<"${{ github.ref_name }}"` + echo "Set Cargo version: $VERSION" + sed -i "s/0.0.0-git/$VERSION/g" Cargo.toml + sed -i "s/0.0.0-git/$VERSION/g" Cargo.lock + + - name: Publish package version + env: +{%- endraw %} + CARGO_REGISTRY: git_{{git-owner}} +{%- raw %} + run: cargo publish --registry $CARGO_REGISTRY --token "Bearer ${{ secrets.CRATES_TOKEN }}" --allow-dirty +{% endraw %} +{%- endif -%} diff --git a/.gitea/workflows/pull-request.yaml b/.gitea/workflows/pull-request.yaml new file mode 100644 index 0000000..d58c4ae --- /dev/null +++ b/.gitea/workflows/pull-request.yaml @@ -0,0 +1,122 @@ +name: Validate Pull Request + +# You must add gitea secrets and enable actions for repository: +# CRATES_TOKEN +# DOCKER_USER +# DOCKER_PASSWORD +# ISSUE_API_RW_TOKEN + +on: + pull_request: + types: + - opened + - edited + - reopened + - synchronize + branches: + - master + - main + +jobs: + {%- raw %} + validate: + name: Validate + runs-on: ubuntu-latest + container: + image: git.smetan.ru/infra/ci-cargo:latest + credentials: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + steps: + - name: Checkout sources + uses: https://github.com/actions/checkout@v4 + + - name: Set cargo registry + run: | + cat <<"EOF" | tee ~/.cargo/config.toml + [registry] + global-credential-providers = ["cargo:token"] + EOF + + - name: Login to cargo registry + env: + {%- endraw %} + CARGO_REGISTRY: git_{{git-owner}} + {%- raw %} + run: cargo login --registry $CARGO_REGISTRY "Bearer ${{ secrets.CRATES_TOKEN }}" + + - name: Cargo fmt + run: cargo fmt --all --check + + - name: Cargo clippy + run: cargo clippy + {%- endraw %} + + test: + {%- raw %} + name: Tests + runs-on: ubuntu-latest + container: + image: git.smetan.ru/infra/ci-cargo:latest + credentials: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + {%- endraw %} + {%- if use_postgres == true %} + services: + db: + # Docker Hub image + image: postgres:15 + # Provide the password for postgres + env: + POSTGRES_DB: test_db + POSTGRES_USER: user + POSTGRES_PASSWORD: pass + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + # Maps tcp port 5432 on service container to the host + - 5432:5432 + {%- endif %} + steps: + {%- raw %} + - name: Checkout sources + uses: https://github.com/actions/checkout@v4 + + {% endraw -%} + {%- if use_postgres == true -%} + - name: Setup .env + run: | + echo "DATABASE_URL=postgres://user:pass@db:5432/test_db" >> .env + {%- endif -%} + {%- raw %} + + - name: Set cargo registry + run: | + cat <<"EOF" | tee ~/.cargo/config.toml + [registry] + global-credential-providers = ["cargo:token"] + EOF + + - name: Login to cargo registry + env: + {%- endraw %} + CARGO_REGISTRY: git_{{git-owner}} + {%- raw %} + run: cargo login --registry $CARGO_REGISTRY "Bearer ${{ secrets.CRATES_TOKEN }}" + + - name: Run Tests + run: | + make prepare + make test + curl --silent -X 'POST' \ + '${{ github.api_url }}/repos/${{ github.repository }}/issues/${{ github.event.number }}/comments' \ + -H 'accept: application/json' \ + -H 'Authorization: token ${{ secrets.ISSUE_API_RW_TOKEN }}' \ + -H 'Content-Type: application/json' \ + -d "{\"body\":\"# Code Coverage:\n$(cat target/coverage/statistic.md)\"}" + {%- endraw %} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c24ec3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/coverage +.env \ No newline at end of file diff --git a/.rusty-hook.toml b/.rusty-hook.toml new file mode 100644 index 0000000..410eddc --- /dev/null +++ b/.rusty-hook.toml @@ -0,0 +1,6 @@ +[hooks] +pre-commit = "cargo check && cargo clippy && cargo fmt -- --check" +pre-push = "cargo test" + +[logging] +verbose = true diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3d889e8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.linkedProjects": ["./Cargo.toml"], + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer", + "editor.formatOnSave": true, + "editor.formatOnSaveMode": "file" + } +} diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1ef1d83 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "{{project-name}}" +description = "" +version = "0.0.0-git" +authors = ["{{authors}}"] +edition = "2021" +license-file = "LICENSE.txt" +documentation = "https://git.smetan.ru/{{git-owner}}/{{project-name}}" +homepage = "https://git.smetan.ru/{{git-owner}}/{{project-name}}" + +[dependencies] +{%- if use_postgres == true %} +sqlx = { version = "0.7", features = ["runtime-tokio", "postgres"] } +tokio = { version = "1.36.0", features = ["rt", "macros"] } +{%- endif %} + +[dev-dependencies] +rusty-hook = "0.11.2" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f2ad756 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,52 @@ +FROM rust:latest as builder + + RUN \ + # Setup image building for scratch image... + apt update \ + && apt install -y musl-tools musl-dev \ + && apt-get install -y build-essential \ + && yes | apt install gcc-x86-64-linux-gnu \ + # Add open ssl support + && apt install ca-certificates \ + && update-ca-certificates \ + && apt install pkg-config libssl-dev make \ + # Add our own user and group to avoid permission problems + && addgroup --gid 131313 app \ + && adduser --uid 131313 --gid 131313 --shell /bin/false --home /app --disabled-password app \ + # Prepare user data for final image + && cat /etc/passwd | grep app > /etc/passwd_app + + WORKDIR /app + + COPY . . + + RUN rustup toolchain install nightly \ + && rustup target add x86_64-unknown-linux-musl + + # Login to Gitea + ARG CRATES_TOKEN + RUN mkdir ~/.cargo && cat <<"EOF" | tee ${CARGO_HOME}/config.toml +[registry] +global-credential-providers = ["cargo:token"] +EOF + RUN cargo login --registry git_{{git-owner}} "Bearer ${CRATES_TOKEN}" + + # Build bin for scratch + ENV RUSTFLAGS='-C linker=x86_64-linux-gnu-gcc' + RUN cargo build --release --target x86_64-unknown-linux-musl + +# Final image +FROM scratch + + WORKDIR /app + + # Copy user settings + COPY --from=builder /etc/passwd_app /etc/passwd + # Copy builded image + COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/{{project-name}} ./ + # Copy CA Certificates + COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ + + USER app + + CMD ["./{{project-name}}"] \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..333e3f5 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,2 @@ +Copyright (c) {{authors}}. +All rights reserved. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4ace425 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +prepare-mac: prepare + brew install llvm + +prepare: + rustup component add llvm-tools-preview + +test: + cargo clean + mkdir -p coverage/ + mkdir -p target/coverage + rm -rf coverage/* + rm -rf target/coverage/* + RUSTFLAGS='-Cinstrument-coverage' LLVM_PROFILE_FILE='coverage/cargo-test-%p-%m.profraw' cargo test --workspace + grcov . --binary-path ./target/debug/deps/ -s . -t lcov --branch --ignore-not-existing --ignore '../*' --ignore "/*" --ignore "*.test.rs" --excl-line '^[ \t]*///.$$' -o target/coverage/lcov.info + grcov . --binary-path ./target/debug/deps/ -s . -t markdown --branch --ignore-not-existing --ignore '../*' --ignore "/*" --ignore "*.test.rs" --excl-line '^[ \t]*///.$$' -o target/coverage/statistic.md + cat target/coverage/statistic.md \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..693b33f --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Template for RUST project +## Prerequisites +### Install and update RUST +https://www.rust-lang.org/tools/install + +### Install cargo-generate lib +https://cargo-generate.github.io/cargo-generate/installation.html + +## Create new Project from template +```bash +cargo generate -g https://git.smetan.ru/template/rust.git -n project-name --bin +``` +Params: +`-g` - git path for this template +`-n` - new project name +`--bin/--lib` - binary(default) or library project. This also affects on building process: `bin` builds as Docker image, `lib` builds as Cargo package \ No newline at end of file diff --git a/cargo-generate.toml b/cargo-generate.toml new file mode 100644 index 0000000..1d1df0e --- /dev/null +++ b/cargo-generate.toml @@ -0,0 +1,13 @@ +[placeholders] +git-owner = { prompt = "Gitea owner or organization name", type = "string", regex = "^[A-Za-z0-9][A-Za-z0-9-]{0,38}$" } +use_postgres = { prompt = "Do you use PostgreSQL for tests?", type = "bool", default = false } + +[template] +cargo_generate_version = ">=0.20.0" +ignore = ["README.md"] + +[conditional.'crate_type == "lib"'] +ignore = [ "src/main.rs", "Dockerfile" ] + +[conditional.'use_postgres == false'] +ignore = [ ".env.example", "docker-compose.yaml", "tests" ] \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..6e56041 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,26 @@ +version: '3.8' + +services: + db: + container_name: postgres + image: postgres:15 + environment: + - POSTGRES_DB=${DB_DATABASE} + - POSTGRES_USER=${DB_USERNAME} + - POSTGRES_PASSWORD=${DB_PASSWORD} + restart: always + ports: + - ${DB_PORT}:5432 + networks: + - postgres + volumes: + - type: volume + source: db + target: /var/lib/postgresql/data + +volumes: + db: + +networks: + postgres: + name: postgres_network \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..d13a01b --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,40 @@ +array_width = 100 +attr_fn_like_width = 100 +binop_separator = "Front" +blank_lines_lower_bound = 0 +blank_lines_upper_bound = 1 +brace_style = "SameLineWhere" +control_brace_style = "AlwaysSameLine" +chain_width = 100 +combine_control_expr = true +comment_width = 120 +condense_wildcard_suffixes = true +edition = "2021" +empty_item_single_line = true +enum_discrim_align_threshold = 20 +fn_call_width = 100 +fn_params_layout = "Tall" +fn_single_line = true +force_multiline_blocks = false +format_code_in_doc_comments = true +doc_comment_code_block_width = 120 +format_generated_files = true +format_macro_matchers = true +format_macro_bodies = true +format_strings = true +hard_tabs = false +imports_indent = "Visual" +imports_layout = "HorizontalVertical" +indent_style = "Block" +inline_attribute_width = 0 +max_width = 120 +imports_granularity = "Module" +newline_style = "Unix" +reorder_impl_items = true +reorder_imports = true +group_imports = "StdExternalCrate" +single_line_let_else_max_width = 0 +spaces_around_ranges = false +struct_lit_single_line = false +tab_spaces = 2 +trailing_comma = "Vertical" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..59f3107 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,11 @@ +pub fn main() -> &'static str { "Test it!" } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn check() { + assert_eq!("Test it!", main()); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..055c948 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,5 @@ +use {{crate_name}}; + +fn main() { + println!("{}", {{crate_name}}::main()); +} diff --git a/tests/pg-connection.rs b/tests/pg-connection.rs new file mode 100644 index 0000000..30e1ec0 --- /dev/null +++ b/tests/pg-connection.rs @@ -0,0 +1,8 @@ +use sqlx::{Pool, Postgres}; + +#[sqlx::test] +async fn basic_test(pool: Pool) -> sqlx::Result<()> { + let mut conn = pool.acquire().await?; + let db_test = sqlx::query("SELECT 1").fetch_one(&mut *conn).await?; + Ok(()) +} \ No newline at end of file