@yunuseon/kayra 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -66,10 +66,11 @@ kayra status
|
|
|
66
66
|
|
|
67
67
|
| | |
|
|
68
68
|
|---|---|
|
|
69
|
+
| [**The docs site**](https://yunuseon.github.io/kayra/) | architecture diagrams, getting started, the fleet, the CLI |
|
|
70
|
+
| [The app contract](https://yunuseon.github.io/kayra/concepts/app-contract/) | what an app provides and what it may not do |
|
|
71
|
+
| [CI isolation](https://yunuseon.github.io/kayra/concepts/ci-isolation/) | why app code cannot reach the host or the platform |
|
|
72
|
+
| [Authentication](https://yunuseon.github.io/kayra/concepts/authentication/) | staff login (Authelia), adding people, recovery |
|
|
69
73
|
| [`CLAUDE.md`](CLAUDE.md) | how everything fits together, and the rules that keep it working |
|
|
70
|
-
| [`docs/app-contract.md`](docs/app-contract.md) | what an app provides and what it may not do |
|
|
71
|
-
| [`docs/authentication.md`](docs/authentication.md) | staff login (Authelia), adding people, recovery |
|
|
72
|
-
| [`docs/ci-isolation.md`](docs/ci-isolation.md) | why app code cannot reach the host or the platform |
|
|
73
74
|
|
|
74
75
|
## Contributing
|
|
75
76
|
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
#
|
|
9
9
|
# Never deletes: a stack dropped from the list keeps running until it is
|
|
10
10
|
# taken down by hand (docker compose -p <name> down).
|
|
11
|
+
#
|
|
12
|
+
# Private images: compose_registry_logins (in secrets.yml, a list of
|
|
13
|
+
# {registry, username, password}) are logged in with a Docker config kept in
|
|
14
|
+
# <dir>/.docker, which every compose call here uses (DOCKER_CONFIG) — the
|
|
15
|
+
# host's own Docker config is never written.
|
|
11
16
|
# =============================================================================
|
|
12
17
|
|
|
13
18
|
- name: Create stack directories
|
|
@@ -52,7 +57,28 @@
|
|
|
52
57
|
loop: "{{ compose_stacks.stacks | select('in', compose_stack_secrets | default({})) }}"
|
|
53
58
|
no_log: true
|
|
54
59
|
|
|
60
|
+
- name: Create the stacks' Docker config directory
|
|
61
|
+
ansible.builtin.file:
|
|
62
|
+
path: "{{ compose_stacks.dir }}/.docker"
|
|
63
|
+
state: directory
|
|
64
|
+
owner: root
|
|
65
|
+
group: root
|
|
66
|
+
mode: "0700"
|
|
67
|
+
when: compose_registry_logins | default([]) | length > 0
|
|
68
|
+
|
|
69
|
+
- name: Log in to private registries
|
|
70
|
+
community.docker.docker_login:
|
|
71
|
+
registry_url: "{{ item.registry }}"
|
|
72
|
+
username: "{{ item.username }}"
|
|
73
|
+
password: "{{ item.password }}"
|
|
74
|
+
config_path: "{{ compose_stacks.dir }}/.docker/config.json"
|
|
75
|
+
loop: "{{ compose_registry_logins | default([]) }}"
|
|
76
|
+
loop_control: {label: "{{ item.registry }}"}
|
|
77
|
+
no_log: true
|
|
78
|
+
|
|
55
79
|
- name: Start stacks
|
|
80
|
+
environment:
|
|
81
|
+
DOCKER_CONFIG: "{{ compose_stacks.dir }}/.docker"
|
|
56
82
|
community.docker.docker_compose_v2:
|
|
57
83
|
project_src: "{{ compose_stacks.dir }}/{{ item }}"
|
|
58
84
|
project_name: "{{ item }}"
|
|
@@ -64,6 +90,8 @@
|
|
|
64
90
|
# when a file it mounts did (a Caddyfile, Homepage's config): those take
|
|
65
91
|
# effect on a restart.
|
|
66
92
|
- name: Restart stacks whose files changed
|
|
93
|
+
environment:
|
|
94
|
+
DOCKER_CONFIG: "{{ compose_stacks.dir }}/.docker"
|
|
67
95
|
ansible.builtin.command: docker compose --project-name {{ item.item }} --file compose.yaml restart
|
|
68
96
|
args:
|
|
69
97
|
chdir: "{{ compose_stacks.dir }}/{{ item.item }}"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
# =============================================================================
|
|
3
3
|
# forgejo-config role — what a Forgejo holds beyond its own config file, as
|
|
4
|
-
# code: users and their SSH keys, Actions runners, repos (created empty
|
|
4
|
+
# code: users and their SSH keys, organizations, Actions runners, repos (created empty
|
|
5
5
|
# where missing) and their settings (issues, pull requests, actions), Actions
|
|
6
6
|
# secrets, collaborators, webhooks and push mirrors, from
|
|
7
7
|
# forgejo_config in the server's vars.yml. Adds what is missing through the
|
|
@@ -46,6 +46,45 @@
|
|
|
46
46
|
loop_control: {label: "{{ item.name }}"}
|
|
47
47
|
no_log: true
|
|
48
48
|
|
|
49
|
+
- name: Look up organizations
|
|
50
|
+
ansible.builtin.uri:
|
|
51
|
+
url: "{{ forgejo_config.url }}/api/v1/orgs/{{ item.name }}"
|
|
52
|
+
headers: {Authorization: "token {{ forgejo_admin_token }}"}
|
|
53
|
+
status_code: [200, 404]
|
|
54
|
+
register: forgejo_orgs
|
|
55
|
+
loop: "{{ forgejo_config.orgs | default([]) }}"
|
|
56
|
+
loop_control: {label: "{{ item.name }}"}
|
|
57
|
+
no_log: true
|
|
58
|
+
|
|
59
|
+
# An organization owns repos and packages alike, and its visibility is
|
|
60
|
+
# theirs: a private one keeps what it owns, images included, from the world.
|
|
61
|
+
# Created with its visibility, so it is never public for a moment.
|
|
62
|
+
- name: Create missing organizations
|
|
63
|
+
ansible.builtin.uri:
|
|
64
|
+
url: "{{ forgejo_config.url }}/api/v1/admin/users/{{ item.owner }}/orgs"
|
|
65
|
+
method: POST
|
|
66
|
+
headers: {Authorization: "token {{ forgejo_admin_token }}"}
|
|
67
|
+
body_format: json
|
|
68
|
+
body: {username: "{{ item.name }}", visibility: "{{ item.visibility }}"}
|
|
69
|
+
status_code: [201]
|
|
70
|
+
changed_when: true
|
|
71
|
+
loop: "{{ forgejo_orgs.results | selectattr('status', 'eq', 404) | map(attribute='item') | list }}"
|
|
72
|
+
loop_control: {label: "{{ item.name }}"}
|
|
73
|
+
no_log: true
|
|
74
|
+
|
|
75
|
+
- name: Apply organization visibility
|
|
76
|
+
ansible.builtin.uri:
|
|
77
|
+
url: "{{ forgejo_config.url }}/api/v1/orgs/{{ item.item.name }}"
|
|
78
|
+
method: PATCH
|
|
79
|
+
headers: {Authorization: "token {{ forgejo_admin_token }}"}
|
|
80
|
+
body_format: json
|
|
81
|
+
body: {visibility: "{{ item.item.visibility }}"}
|
|
82
|
+
changed_when: true
|
|
83
|
+
when: item.status == 200 and item.json.visibility != item.item.visibility
|
|
84
|
+
loop: "{{ forgejo_orgs.results }}"
|
|
85
|
+
loop_control: {label: "{{ item.item.name }}"}
|
|
86
|
+
no_log: true
|
|
87
|
+
|
|
49
88
|
- name: Look up SSH keys
|
|
50
89
|
ansible.builtin.uri:
|
|
51
90
|
url: "{{ forgejo_config.url }}/api/v1/users/{{ item.user }}/keys"
|
|
@@ -76,10 +115,14 @@
|
|
|
76
115
|
# --keep-labels: the runner declares its labels when it starts, and a
|
|
77
116
|
# register without it replaces them with none, so no job finds a runner
|
|
78
117
|
# until the runner restarts.
|
|
118
|
+
# A scope (an owner, or owner/repo) limits a runner to those repos; without
|
|
119
|
+
# one it serves every repo on the instance.
|
|
79
120
|
- name: Register runners
|
|
80
121
|
ansible.builtin.command:
|
|
81
|
-
argv:
|
|
82
|
-
|
|
122
|
+
argv: >-
|
|
123
|
+
{{ ['docker', 'exec', '-i', forgejo_config.container, 'forgejo', 'forgejo-cli', 'actions', 'register',
|
|
124
|
+
'--secret-file', '/dev/stdin', '--name', item.name, '--keep-labels']
|
|
125
|
+
+ (['--scope', item.scope] if item.scope | default('') | length > 0 else []) }}
|
|
83
126
|
stdin: "{{ forgejo_runner_secrets[item.name] }}"
|
|
84
127
|
# Exactly 40 characters: a trailing newline would be a 41st.
|
|
85
128
|
stdin_add_newline: false
|
|
@@ -103,7 +146,9 @@
|
|
|
103
146
|
# the content is pushed by hand.
|
|
104
147
|
- name: Create missing repos
|
|
105
148
|
ansible.builtin.uri:
|
|
106
|
-
url:
|
|
149
|
+
url: >-
|
|
150
|
+
{{ forgejo_config.url }}/api/v1/{{ 'orgs' if item.name.split('/')[0] in (forgejo_config.orgs | default([]) | map(attribute='name'))
|
|
151
|
+
else 'admin/users' }}/{{ item.name.split('/')[0] }}/repos
|
|
107
152
|
method: POST
|
|
108
153
|
headers: {Authorization: "token {{ forgejo_admin_token }}"}
|
|
109
154
|
body_format: json
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yunuseon/kayra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "A self-hosted app platform and the CLI that runs it: Traefik, Authelia, Jenkins, a registry and rootless BuildKit per server, set up with OpenTofu and Ansible.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Yunus Öztürk",
|