Compare commits
10 commits
d18ac0baa6
...
ad7b9ec602
Author | SHA1 | Date | |
---|---|---|---|
|
ad7b9ec602 | ||
|
99cf060e08 | ||
|
48c95b975a | ||
|
d6e0d933d8 | ||
|
721d134c6a | ||
|
09c4d95376 | ||
|
3271450011 | ||
|
d23ae91b74 | ||
|
fc90b3fb71 | ||
|
6aa251e9ec |
44
TODO.md
44
TODO.md
|
@ -1,5 +1,45 @@
|
||||||
# TODO:
|
# TODO:
|
||||||
|
|
||||||
- add CI
|
- publish to gitea repo: [container](https://docs.gitea.io/en-us/usage/packages/container/) [badge](https://docs.gitea.io/en-us/usage/packages/generic/)
|
||||||
- add flake
|
|
||||||
- use bool value for `generate_gitea_project` when the feature is available
|
- use bool value for `generate_gitea_project` when the feature is available
|
||||||
|
|
||||||
|
### Package
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -i --upload-file README.md --user "histausse:`secret-tool lookup Title 'Gitea Token'`" https://git.pains-perdus.fr/api/packages/histausse/generic/test_flake_poetry2nix/latest/README.md
|
||||||
|
curl https://git.pains-perdus.fr/api/packages/histausse/generic/test_flake_poetry2nix/latest/README.md
|
||||||
|
curl -i -X DELETE --user "histausse:`secret-tool lookup Title 'Gitea Token'`" https://git.pains-perdus.fr/api/packages/histausse/generic/test_flake_poetry2nix/latest/pyproject.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
podman login -u histausse -p `secret-tool lookup Title 'Gitea Token'` git.pains-perdus.fr
|
||||||
|
podman push test_flake_poetry2nix:latest git.pains-perdus.fr/histausse/test_flake_poetry2nix:latest
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Put secret in CI:
|
||||||
|
|
||||||
|
```
|
||||||
|
connection.request(
|
||||||
|
"POST",
|
||||||
|
f"/api/repos/{repo}/secrets",
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"name": "test_token",
|
||||||
|
"value": "loren ispum",
|
||||||
|
"image": ["test"],
|
||||||
|
"event": ["push", "tag"],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
{
|
||||||
|
"Authorization": f"Bearer {API_KEY}",
|
||||||
|
"content-type": "application/json",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Gen badge:
|
||||||
|
```
|
||||||
|
https://github.com/smarie/python-genbadge/issues
|
||||||
|
```
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
"email": "",
|
"email": "",
|
||||||
"git_user": "",
|
"git_user": "",
|
||||||
"project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}",
|
"project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}",
|
||||||
"gitea_url": "",
|
"gitea_url": "https://git.pains-perdus.fr",
|
||||||
"project_url": "{{ cookiecutter.gitea_url }}/{{ cookiecutter.git_user }}/{{ cookiecutter.project_slug }}",
|
"project_url": "{{ cookiecutter.gitea_url }}/{{ cookiecutter.git_user }}/{{ cookiecutter.project_slug }}",
|
||||||
"generate_gitea_project": [ true, false ],
|
"generate_gitea_project": [ true, false ],
|
||||||
"configure_ci": [ true, false ],
|
"configure_ci": [ true, false ],
|
||||||
"woodpecker_gitea_user": "ci",
|
"woodpecker_gitea_user": "ci",
|
||||||
"woodpecker_server": "ci.pains-perdus.fr",
|
"woodpecker_server": "https://ci.pains-perdus.fr",
|
||||||
"git_origin": "{{ cookiecutter.gitea_url.replace('https://', 'gitea@').replace('http://', 'gitea') }}:{{ cookiecutter.git_user }}/{{ cookiecutter.project_slug }}.git",
|
"git_origin": "{{ cookiecutter.gitea_url.replace('https://', 'gitea@').replace('http://', 'gitea') }}:{{ cookiecutter.git_user }}/{{ cookiecutter.project_slug }}.git",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"open_source_license": ["AGPL-3.0-only", "GPL-3.0-only", "MIT", "BSD-3-Clause", "ISC", "Apache-2.0", "Proprietary"],
|
"open_source_license": ["AGPL-3.0-only", "GPL-3.0-only", "MIT", "BSD-3-Clause", "ISC", "Apache-2.0", "Proprietary"],
|
||||||
|
|
|
@ -1,11 +1,28 @@
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import http.client
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from base64 import b64encode
|
||||||
|
|
||||||
|
REMOVE_PATHS = [
|
||||||
|
{% if cookiecutter.open_source_license == "Proprietary" %} "LICENSE", {% endif %}
|
||||||
|
{% if cookiecutter.configure_ci == "False" %} ".woodpecker.yml", {% endif %}
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in REMOVE_PATHS:
|
||||||
|
if path and os.path.exists(path):
|
||||||
|
if os.path.isdir(path):
|
||||||
|
os.rmdir(path)
|
||||||
|
else:
|
||||||
|
os.unlink(path)
|
||||||
|
|
||||||
|
subprocess.call(["poetry", "lock"])
|
||||||
subprocess.call(["git", "init"])
|
subprocess.call(["git", "init"])
|
||||||
subprocess.call(["git", "checkout", "-b", "main"])
|
subprocess.call(["git", "checkout", "-b", "main"])
|
||||||
subprocess.call(["git", "add", "*"])
|
subprocess.call(["git", "add", "*"])
|
||||||
subprocess.call(["git", "commit", "-m", "Initial commit"])
|
|
||||||
subprocess.call(["git", "config", "user.name", "{{ cookiecutter.git_user }}"])
|
subprocess.call(["git", "config", "user.name", "{{ cookiecutter.git_user }}"])
|
||||||
subprocess.call(["git", "config", "user.email", "{{ cookiecutter.email }}"])
|
subprocess.call(["git", "config", "user.email", "{{ cookiecutter.email }}"])
|
||||||
|
subprocess.call(["git", "commit", "-m", "Initial commit"])
|
||||||
|
|
||||||
# subprocess.call(["python", "-m", "venv", "venv"])
|
# subprocess.call(["python", "-m", "venv", "venv"])
|
||||||
|
|
||||||
|
@ -28,6 +45,69 @@ def get_secret(secret_name: str):
|
||||||
"please enter you gitea api token:"
|
"please enter you gitea api token:"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_new_gitea_token(
|
||||||
|
connection: http.client.HTTPSConnection, new_token_name: str, user: str, token: str
|
||||||
|
) -> str:
|
||||||
|
auth = b64encode(f"{user}:{token}".encode("utf-8")).decode("ascii")
|
||||||
|
connection.request(
|
||||||
|
"POST",
|
||||||
|
f"/api/v1/users/{user}/tokens",
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"name": new_token_name,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
{
|
||||||
|
"Authorization": f"Basic {auth}",
|
||||||
|
"content-type": "application/json",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
response = connection.getresponse()
|
||||||
|
status = response.status
|
||||||
|
if status != 201:
|
||||||
|
print(
|
||||||
|
f"\033[38;2;255;0;0mInvalid response from gitea while creating a new token: {status} ({response.reason})\033[0m"
|
||||||
|
)
|
||||||
|
print(response.read())
|
||||||
|
exit(1)
|
||||||
|
data = json.load(response)
|
||||||
|
return data["sha1"]
|
||||||
|
|
||||||
|
def push_woodpecker_secret(
|
||||||
|
connection: http.client.HTTPSConnection,
|
||||||
|
repo: str,
|
||||||
|
secret_name: str,
|
||||||
|
secret: str,
|
||||||
|
woodpecker_token: str,
|
||||||
|
images: list[str] | None = None
|
||||||
|
):
|
||||||
|
if images is None: images = []
|
||||||
|
connection.request(
|
||||||
|
"POST",
|
||||||
|
f"/api/repos/{repo}/secrets",
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"name": secret_name,
|
||||||
|
"value": secret,
|
||||||
|
"image": images,
|
||||||
|
"event": ["push", "tag"],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
{
|
||||||
|
"Authorization": f"Bearer {woodpecker_token}",
|
||||||
|
"content-type": "application/json",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
response = connection.getresponse()
|
||||||
|
status = response.status
|
||||||
|
if status != 200:
|
||||||
|
print(
|
||||||
|
f"\033[38;2;255;0;0mInvalid response from woodpecker when sending a new secret: {status} ({response.reason})\033[0m"
|
||||||
|
)
|
||||||
|
print(response.read())
|
||||||
|
exit(1)
|
||||||
|
response.read()
|
||||||
|
|
||||||
|
|
||||||
if {{cookiecutter.generate_gitea_project}}:
|
if {{cookiecutter.generate_gitea_project}}:
|
||||||
try:
|
try:
|
||||||
|
@ -35,9 +115,9 @@ if {{cookiecutter.generate_gitea_project}}:
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
print("module `giteapy` is not availabled, repository not created")
|
print("module `giteapy` is not availabled, repository not created")
|
||||||
exit()
|
exit()
|
||||||
API_KEY = get_secret("Gitea Token")
|
GITEA_API_KEY = get_secret("Gitea Token")
|
||||||
configuration = giteapy.Configuration()
|
configuration = giteapy.Configuration()
|
||||||
configuration.api_key["access_token"] = API_KEY
|
configuration.api_key["access_token"] = GITEA_API_KEY
|
||||||
client = giteapy.ApiClient(configuration)
|
client = giteapy.ApiClient(configuration)
|
||||||
client.configuration.host = "{{ cookiecutter.gitea_url }}/api/v1"
|
client.configuration.host = "{{ cookiecutter.gitea_url }}/api/v1"
|
||||||
api_instance = giteapy.UserApi(client)
|
api_instance = giteapy.UserApi(client)
|
||||||
|
@ -48,10 +128,6 @@ if {{cookiecutter.generate_gitea_project}}:
|
||||||
|
|
||||||
|
|
||||||
if {{cookiecutter.generate_gitea_project}} and {{cookiecutter.configure_ci}}:
|
if {{cookiecutter.generate_gitea_project}} and {{cookiecutter.configure_ci}}:
|
||||||
import http.client
|
|
||||||
|
|
||||||
# import json
|
|
||||||
|
|
||||||
api_instance = giteapy.RepositoryApi(client)
|
api_instance = giteapy.RepositoryApi(client)
|
||||||
options = giteapy.AddCollaboratorOption("read")
|
options = giteapy.AddCollaboratorOption("read")
|
||||||
api_instance.repo_add_collaborator(
|
api_instance.repo_add_collaborator(
|
||||||
|
@ -60,16 +136,18 @@ if {{cookiecutter.generate_gitea_project}} and {{cookiecutter.configure_ci}}:
|
||||||
"{{ cookiecutter.woodpecker_gitea_user }}",
|
"{{ cookiecutter.woodpecker_gitea_user }}",
|
||||||
body=options,
|
body=options,
|
||||||
)
|
)
|
||||||
|
connection_gitea = http.client.HTTPSConnection("git.pains-perdus.fr")
|
||||||
API_KEY = get_secret("Woodpecker Token")
|
GITEA_API_KEY = get_new_gitea_token(connection_gitea, "CI {{ cookiecutter.git_user }}/{{ cookiecutter.project_slug }}", "{{ cookiecutter.git_user }}", GITEA_API_KEY)
|
||||||
|
WOODPECKER_API_KEY = get_secret("Woodpecker Token")
|
||||||
WOODPECKER_SERVER = "{{ cookiecutter.woodpecker_server }}".removeprefix("https://")
|
WOODPECKER_SERVER = "{{ cookiecutter.woodpecker_server }}".removeprefix("https://")
|
||||||
origin = "{{ cookiecutter.git_origin }}"
|
origin = "{{ cookiecutter.git_origin }}"
|
||||||
repo = "/".join(origin.removesuffix(".git").split(":")[-1].split("/")[-2:])
|
repo = "/".join(origin.removesuffix(".git").split(":")[-1].split("/")[-2:])
|
||||||
|
|
||||||
connection = http.client.HTTPSConnection(WOODPECKER_SERVER)
|
connection = http.client.HTTPSConnection(WOODPECKER_SERVER)
|
||||||
connection.request(
|
connection.request(
|
||||||
"GET",
|
"GET",
|
||||||
"/api/user/repos?all=true&flush=true",
|
"/api/user/repos?all=true&flush=true",
|
||||||
headers={"Authorization": f"Bearer {API_KEY}"},
|
headers={"Authorization": f"Bearer {WOODPECKER_API_KEY}"},
|
||||||
)
|
)
|
||||||
response = connection.getresponse()
|
response = connection.getresponse()
|
||||||
status = response.status
|
status = response.status
|
||||||
|
@ -84,7 +162,7 @@ if {{cookiecutter.generate_gitea_project}} and {{cookiecutter.configure_ci}}:
|
||||||
"POST",
|
"POST",
|
||||||
f"/api/repos/{repo}",
|
f"/api/repos/{repo}",
|
||||||
headers={
|
headers={
|
||||||
"Authorization": f"Bearer {API_KEY}",
|
"Authorization": f"Bearer {WOODPECKER_API_KEY}",
|
||||||
"referer": f"https://{WOODPECKER_SERVER}/repo/add",
|
"referer": f"https://{WOODPECKER_SERVER}/repo/add",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -94,7 +172,16 @@ if {{cookiecutter.generate_gitea_project}} and {{cookiecutter.configure_ci}}:
|
||||||
print(
|
print(
|
||||||
f"\033[38;2;255;0;0mInvalid response from woodpecker while linking repo: {status} ({response.reason})\033[0m"
|
f"\033[38;2;255;0;0mInvalid response from woodpecker while linking repo: {status} ({response.reason})\033[0m"
|
||||||
)
|
)
|
||||||
|
print(response.read())
|
||||||
exit(1)
|
exit(1)
|
||||||
|
response.read()
|
||||||
|
push_woodpecker_secret(
|
||||||
|
connection,
|
||||||
|
repo,
|
||||||
|
"gitea_token",
|
||||||
|
GITEA_API_KEY,
|
||||||
|
WOODPECKER_API_KEY
|
||||||
|
)
|
||||||
|
|
||||||
if {{cookiecutter.generate_gitea_project}}:
|
if {{cookiecutter.generate_gitea_project}}:
|
||||||
subprocess.call(["git", "remote", "add", "origin", "{{ cookiecutter.git_origin }}"])
|
subprocess.call(["git", "remote", "add", "origin", "{{ cookiecutter.git_origin }}"])
|
||||||
|
|
|
@ -1,5 +1,39 @@
|
||||||
pipeline:
|
pipeline:
|
||||||
a-test-step:
|
test:
|
||||||
image: debian
|
group: test
|
||||||
|
image: python:${PYTHON_VERSION}
|
||||||
|
pull: true
|
||||||
|
environment:
|
||||||
|
- POETRY_VIRTUALENVS_IN_PROJECT=true
|
||||||
commands:
|
commands:
|
||||||
- echo "Testing.."
|
- pip install poetry
|
||||||
|
- poetry install
|
||||||
|
- poetry run pytest
|
||||||
|
|
||||||
|
nix:
|
||||||
|
group: test
|
||||||
|
image: nixos/nix:latest
|
||||||
|
pull: true
|
||||||
|
commands:
|
||||||
|
- nix build --experimental-features 'nix-command flakes'
|
||||||
|
- nix build -o image_link --experimental-features 'nix-command flakes' .#docker
|
||||||
|
- cp image_link image
|
||||||
|
when:
|
||||||
|
matrix:
|
||||||
|
PYTHON_VERSION: {{ cookiecutter.python_min_version}} # Still not sure about how to make flake for different python version
|
||||||
|
|
||||||
|
push_image:
|
||||||
|
image: quay.io/podman/stable:latest
|
||||||
|
pull: true
|
||||||
|
commands:
|
||||||
|
- podman login -u {{ cookiecutter.git_user }} -p $GITEA_TOKEN {{ cookiecutter.gitea_url.removeprefix('https://') }}
|
||||||
|
- podman load < image
|
||||||
|
- podman push {{ cookiecutter.project_slug }}:latest {{ cookiecutter.gitea_url.removeprefix('https://') }}/{{ cookiecutter.git_user }}/{{ cookiecutter.project_slug }}:latest
|
||||||
|
secrets: [ gitea_token ]
|
||||||
|
when:
|
||||||
|
matrix:
|
||||||
|
PYTHON_VERSION: {{ cookiecutter.python_min_version}} # Still not sure about how to make flake for different python version
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
PYTHON_VERSION:
|
||||||
|
- {{ cookiecutter.python_min_version}}
|
||||||
|
|
|
@ -1,16 +1,50 @@
|
||||||
{% set is_open_source = cookiecutter.open_source_license != 'Proprietary' -%}
|
{% set is_open_source = cookiecutter.open_source_license != 'Proprietary' -%}
|
||||||
|
{% set repo = "/".join(cookiecutter.git_origin.removesuffix(".git").split(":")[-1].split("/")[-2:]) %}
|
||||||
# {{ cookiecutter.project_name }}
|
# {{ cookiecutter.project_name }}
|
||||||
|
{% if cookiecutter.configure_ci == "True" %}[](https://ci.pains-perdus.fr/{{ repo }}){% endif %}
|
||||||
|
|
||||||
|
|
||||||
{{ cookiecutter.project_short_description }}
|
{{ cookiecutter.project_short_description }}
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
|
|
||||||
|
### With pip
|
||||||
|
|
||||||
This project can be installed using pip:
|
This project can be installed using pip:
|
||||||
|
|
||||||
```
|
```
|
||||||
pip install git+{{ cookiecutter.project_url }}.git
|
pip install git+{{ cookiecutter.project_url }}.git
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### With Nix
|
||||||
|
|
||||||
|
There is a `flake.nix`, so you can clone the repo and use `nix shell` if you want.
|
||||||
|
|
||||||
|
### Docker/Podman
|
||||||
|
|
||||||
|
{% if cookiecutter.configure_ci == "True" %}
|
||||||
|
You can run this projet with docker or podman:
|
||||||
|
|
||||||
|
```
|
||||||
|
podman run --rm -it {{ cookiecutter.gitea_url.removeprefix('https://') }}/{{ cookiecutter.git_user }}/{{ cookiecutter.project_slug }}:latest {{ cookiecutter.project_slug }}
|
||||||
|
```
|
||||||
|
{% else %}
|
||||||
|
You can build a container image using nix. To build the image, in the repo, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix build -o {{ cookiecutter.project_slug }}.img .#docker
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then load the image with:
|
||||||
|
|
||||||
|
```
|
||||||
|
podman load < {{ cookiecutter.project_slug }}.img
|
||||||
|
```
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
Notice the image is build with nix and is very minimalist.
|
||||||
|
|
||||||
{% if is_open_source %}
|
{% if is_open_source %}
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
36
{{ cookiecutter.project_slug }}/flake.nix
Normal file
36
{{ cookiecutter.project_slug }}/flake.nix
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
description = "{{ cookiecutter.project_short_description }}";
|
||||||
|
|
||||||
|
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
inputs.poetry2nix = {
|
||||||
|
url = "github:nix-community/poetry2nix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils, poetry2nix }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
inherit (poetry2nix.legacyPackages.${system}) mkPoetryApplication;
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = {
|
||||||
|
{{ cookiecutter.project_slug }} = mkPoetryApplication { projectDir = self; };
|
||||||
|
docker = pkgs.dockerTools.buildImage {
|
||||||
|
name = "{{ cookiecutter.project_slug }}";
|
||||||
|
tag = "latest";
|
||||||
|
copyToRoot = pkgs.buildEnv {
|
||||||
|
name = "{{ cookiecutter.project_slug }}_root_img";
|
||||||
|
paths = [ self.packages.${system}.{{ cookiecutter.project_slug }} ];
|
||||||
|
pathsToLink = [ "/bin" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = self.packages.${system}.{{ cookiecutter.project_slug }};
|
||||||
|
};
|
||||||
|
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
packages = [ poetry2nix.packages.${system}.poetry ];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue