@yunuseon/kayra 0.4.0 → 0.5.0

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"
@@ -103,7 +142,9 @@
103
142
  # the content is pushed by hand.
104
143
  - name: Create missing repos
105
144
  ansible.builtin.uri:
106
- url: "{{ forgejo_config.url }}/api/v1/admin/users/{{ item.name.split('/')[0] }}/repos"
145
+ url: >-
146
+ {{ forgejo_config.url }}/api/v1/{{ 'orgs' if item.name.split('/')[0] in (forgejo_config.orgs | default([]) | map(attribute='name'))
147
+ else 'admin/users' }}/{{ item.name.split('/')[0] }}/repos
107
148
  method: POST
108
149
  headers: {Authorization: "token {{ forgejo_admin_token }}"}
109
150
  body_format: json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yunuseon/kayra",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
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",