Commit
+107 -1 +/-9 browse
1 | diff --git a/ayllu/src/web2/routes/index.rs b/ayllu/src/web2/routes/index.rs |
2 | index ca1cf86..e300ca9 100644 |
3 | --- a/ayllu/src/web2/routes/index.rs |
4 | +++ b/ayllu/src/web2/routes/index.rs |
5 | @@ -32,6 +32,7 @@ struct Repository { |
6 | timestamp: Option<u64>, |
7 | age: String, |
8 | activity: String, |
9 | + is_mirror: bool, |
10 | } |
11 | |
12 | async fn load_repositories(collection_path: &str, db: &Database) -> Result<Vec<Repository>, Error> { |
13 | @@ -52,6 +53,7 @@ async fn load_repositories(collection_path: &str, db: &Database) -> Result<Vec<R |
14 | .unwrap_or(String::from("no description present")); |
15 | let timestamp = repository.last_modified()?; |
16 | let age = timestamp.map_or(String::from("?"), timeutil::friendly); |
17 | + let is_mirror = git_config.is_mirror; |
18 | // TODO move to plugin based system |
19 | let db_activity: Vec<(i64, i64, i64)> = match latest_hash { |
20 | Some(hash) => { |
21 | @@ -72,6 +74,7 @@ async fn load_repositories(collection_path: &str, db: &Database) -> Result<Vec<R |
22 | timestamp, |
23 | age, |
24 | activity, |
25 | + is_mirror, |
26 | }); |
27 | } |
28 | repositories.sort_by(|a, b| { |
29 | diff --git a/ayllu/src/web2/routes/rss.rs b/ayllu/src/web2/routes/rss.rs |
30 | index e309cf3..4a45b70 100644 |
31 | --- a/ayllu/src/web2/routes/rss.rs |
32 | +++ b/ayllu/src/web2/routes/rss.rs |
33 | @@ -153,9 +153,14 @@ impl Builder { |
34 | for entry in entries { |
35 | let repository = Wrapper::new(entry.0.as_path())?; |
36 | let config = repository.config()?; |
37 | + // skip hidden repositories |
38 | if config.hidden.is_some_and(|hidden| hidden) { |
39 | continue; |
40 | }; |
41 | + // skip mirrors |
42 | + if config.is_mirror { |
43 | + continue; |
44 | + } |
45 | for tag in repository.tags_range(Some(( |
46 | start.unix_timestamp(), |
47 | self.current_time.unix_timestamp(), |
48 | @@ -207,6 +212,9 @@ impl Builder { |
49 | if config.hidden.is_some_and(|hidden| hidden) { |
50 | continue; |
51 | }; |
52 | + if config.is_mirror { |
53 | + continue; |
54 | + } |
55 | let tags = |
56 | repository.tags_range(Some((start.unix_timestamp(), end.unix_timestamp())))?; |
57 | let commits = repository |
58 | diff --git a/ayllu/themes/default/templates/index.html b/ayllu/themes/default/templates/index.html |
59 | index dd5fc44..e5c5d11 100644 |
60 | --- a/ayllu/themes/default/templates/index.html |
61 | +++ b/ayllu/themes/default/templates/index.html |
62 | @@ -27,6 +27,7 @@ |
63 | <article class="segmented-4"> |
64 | <div class="name"> |
65 | <a href="/{{ collection.name }}/{{ repo.name }}">{{ repo.name }}</a> |
66 | + {%- if repo.is_mirror %} <span class="tiny-highlight">[mirror]</span> {%- endif -%} |
67 | </div> |
68 | <div class="description">{{ repo.description }}</div> |
69 | <div class="age">{{ repo.age }}</div> |
70 | diff --git a/ayllu/themes/default/theme.css b/ayllu/themes/default/theme.css |
71 | index 9ea20ef..0ce6f03 100644 |
72 | --- a/ayllu/themes/default/theme.css |
73 | +++ b/ayllu/themes/default/theme.css |
74 | @@ -79,6 +79,15 @@ span.hint { |
75 | text-decoration: underline; |
76 | } |
77 | |
78 | + span.tiny-highlight { |
79 | + font-size: var(--font-size-0); |
80 | + font-weight: var(--font-weight-0); |
81 | + color: var(--text-2); |
82 | + text-shadow: |
83 | + 0 0 10px var(--green-5), |
84 | + 0 0 25px var(--green-7); |
85 | + } |
86 | + |
87 | .rss-links { |
88 | display: flex; |
89 | align-items: center; |
90 | diff --git a/crates/git/src/config.rs b/crates/git/src/config.rs |
91 | index aa5794c..1bd8223 100644 |
92 | --- a/crates/git/src/config.rs |
93 | +++ b/crates/git/src/config.rs |
94 | @@ -36,6 +36,25 @@ pub struct ChatLink { |
95 | pub kind: ChatKind, |
96 | } |
97 | |
98 | + /// represents a remote as defined in the local git configuration |
99 | + #[derive(Serialize, Clone, Debug)] |
100 | + pub struct Remote { |
101 | + pub url: String, |
102 | + pub mirror: bool, |
103 | + } |
104 | + |
105 | + /// array of remotes |
106 | + pub struct Remotes(pub Vec<Remote>); |
107 | + |
108 | + impl Remotes { |
109 | + /// determine if any of the remotes are mirrors which usually would |
110 | + /// indicate that the repository is a mirror rather than a cannonical |
111 | + /// source. |
112 | + pub fn has_mirror(&self) -> bool { |
113 | + self.0.iter().any(|remote| remote.mirror) |
114 | + } |
115 | + } |
116 | + |
117 | // ayllu specific configuration from a git repository |
118 | #[derive(Serialize, Clone, Debug)] |
119 | pub struct Config { |
120 | @@ -46,6 +65,41 @@ pub struct Config { |
121 | pub hidden: Option<bool>, |
122 | pub default_branch: Option<String>, |
123 | pub sites: Sites, |
124 | + /// an array of remotes where mirror = true, typically one when a |
125 | + /// repository is cloned with with the mirror flag, i.e. |
126 | + /// git clone --mirror ... |
127 | + pub remotes: Vec<Remote>, |
128 | + /// if the repository looks to be a mirror |
129 | + pub is_mirror: bool, |
130 | + } |
131 | + |
132 | + /// return a vec of all the remotes defined in the git configuration |
133 | + pub fn remotes(cfg: &GitConfig) -> Remotes { |
134 | + let entries: Vec<(String, String)> = |
135 | + cfg.entries(Some(r#"remote.*.url"#)) |
136 | + .map_or(Vec::new(), |entries| { |
137 | + let mut remotes: Vec<(String, String)> = Vec::new(); |
138 | + let _ = entries.for_each(|entry| { |
139 | + if let Some(name) = entry.name() { |
140 | + remotes.push(( |
141 | + name.replace(".url", "").to_string(), |
142 | + entry.value().unwrap_or("").to_string(), |
143 | + )); |
144 | + }; |
145 | + }); |
146 | + remotes |
147 | + }); |
148 | + Remotes( |
149 | + entries |
150 | + .into_iter() |
151 | + .map(|entry| Remote { |
152 | + url: entry.1.clone(), |
153 | + mirror: cfg |
154 | + .get_bool(&(entry.0 + ".mirror")) |
155 | + .is_ok_and(|mirror| mirror), |
156 | + }) |
157 | + .collect(), |
158 | + ) |
159 | } |
160 | |
161 | pub fn string(cfg: &GitConfig, path: &str) -> Option<String> { |
162 | diff --git a/crates/git/src/wrapper.rs b/crates/git/src/wrapper.rs |
163 | index 9112ec7..f2f960a 100644 |
164 | --- a/crates/git/src/wrapper.rs |
165 | +++ b/crates/git/src/wrapper.rs |
166 | @@ -496,6 +496,8 @@ impl Wrapper { |
167 | .map(|entry| config::Email(entry.clone())) |
168 | .collect() |
169 | }); |
170 | + let remotes = config::remotes(git_config); |
171 | + let is_mirror = remotes.has_mirror(); |
172 | Ok(config::Config { |
173 | description: config::string(git_config, "ayllu.description"), |
174 | homepage: config::string(git_config, "ayllu.homepage"), |
175 | @@ -508,6 +510,8 @@ impl Wrapper { |
176 | content: config::string(git_config, "ayllu-sites.content"), |
177 | branch: config::string(git_config, "ayllu-sites.branch"), |
178 | }, |
179 | + remotes: remotes.0, |
180 | + is_mirror, |
181 | }) |
182 | } |
183 | |
184 | diff --git a/www/content/docs/configuration.md b/www/content/docs/configuration.md |
185 | index d6811fd..c6cce19 100644 |
186 | --- a/www/content/docs/configuration.md |
187 | +++ b/www/content/docs/configuration.md |
188 | @@ -200,3 +200,25 @@ diff = """ |
189 | (command) @variable.builtin |
190 | """ |
191 | ``` |
192 | + |
193 | + # Mirroring |
194 | + |
195 | + Git mirrors will be automatically detected by looking at git configuration to |
196 | + determine if the repository has a `mirror = true` flag present in any of it's |
197 | + configured remotes. If any remote is flagged as a mirror then the repository |
198 | + will be considered a mirror. Mirror repositories will not be listed in RSS |
199 | + feeds, be subject to certain jobs types, or be available in the remote API. |
200 | + |
201 | + ## Creating a Mirror |
202 | + |
203 | + The simplest way to create a mirror is by cloning a repository with the |
204 | + `--mirror` flag. For example: `git clone https://ayllu-forge.org/ayllu/ayllu --mirror` |
205 | + will result in a git config such as: |
206 | + |
207 | + ``` |
208 | + [remote "origin"] |
209 | + url = https://ayllu-forge.org/ayllu/ayllu |
210 | + fetch = +refs/*:refs/* |
211 | + mirror = true |
212 | + |
213 | + ``` |
214 | diff --git a/www/public/docs/configuration/index.html b/www/public/docs/configuration/index.html |
215 | index 1f99c9a..d5f74a4 100644 |
216 | --- a/www/public/docs/configuration/index.html |
217 | +++ b/www/public/docs/configuration/index.html |
218 | @@ -79,4 +79,9 @@ diff = """ |
219 | (location) @attribute |
220 | (command) @variable.builtin |
221 | """ |
222 | + </code></pre><h1 id=mirroring><a aria-label="Anchor link for: mirroring" class=zola-anchor href=#mirroring>Mirroring</a></h1><p>Git mirrors will be automatically detected by looking at git configuration to determine if the repository has a <code>mirror = true</code> flag present in any of it's configured remotes. If any remote is flagged as a mirror then the repository will be considered a mirror. Mirror repositories will not be listed in RSS feeds, be subject to certain jobs types, or be available in the remote API.<h2 id=creating-a-mirror><a aria-label="Anchor link for: creating-a-mirror" class=zola-anchor href=#creating-a-mirror>Creating a Mirror</a></h2><p>The simplest way to create a mirror is by cloning a repository with the <code>--mirror</code> flag. For example: <code>git clone https://ayllu-forge.org/ayllu/ayllu --mirror</code> will result in a git config such as:<pre><code>[remote "origin"] |
223 | + url = https://ayllu-forge.org/ayllu/ayllu |
224 | + fetch = +refs/*:refs/* |
225 | + mirror = true |
226 | + |
227 | </code></pre></div></div></main><footer class=page-footer>2024, <a href=/ayllu/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div> |
228 | \ No newline at end of file |
229 | diff --git a/www/public/docs/mail/index.html b/www/public/docs/mail/index.html |
230 | index 1bdbdcc..8dbbad2 100644 |
231 | --- a/www/public/docs/mail/index.html |
232 | +++ b/www/public/docs/mail/index.html |
233 | @@ -1 +1 @@ |
234 | - <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li class=active><a href=https://ayllu-forge.org/docs/mail/>Mail</a><li><a href=https://ayllu-forge.org/docs/proxy/>Reverse Proxy</a><li><a href=https://ayllu-forge.org/docs/themes/>Themes</a></ul></div><div class=doc-content><h1 id=mailing-list-support><a aria-label="Anchor link for: mailing-list-support" class=zola-anchor href=#mailing-list-support>Mailing List Support</a></h1><p>Ayllu has full featured support for email based development workflows. It is based on the excellent <a href=https://git.meli-email.org/meli/mailpot>mailpot</a> mailing list manager.</div></div></main><footer class=page-footer>2024, <a href=/ayllu/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div> |
235 | \ No newline at end of file |
236 | + <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li class=active><a href=https://ayllu-forge.org/docs/mail/>Mail</a><li><a href=https://ayllu-forge.org/docs/proxy/>Reverse Proxy</a><li><a href=https://ayllu-forge.org/docs/themes/>Themes</a></ul></div><div class=doc-content><h1 id=mailing-list-support><a aria-label="Anchor link for: mailing-list-support" class=zola-anchor href=#mailing-list-support>Mailing List Support</a></h1><p>Ayllu has full featured support for email based development workflows. It is based on the excellent <a href=https://git.meli-email.org/meli/mailpot>mailpot</a> mailing list manager.<h2 id=configuration><a aria-label="Anchor link for: configuration" class=zola-anchor href=#configuration>Configuration</a></h2><p>The configuration of mail servers can be complex. Ayllu encapsulates most of these settings in the <a href=https://ayllu-forge.org/ayllu/ayllu/tree/main/containers/multiuser>mutliuser</a> container which can be used as a reference for configuring your own mail server.<h3 id=minimum-recommended-security-settings><a aria-label="Anchor link for: minimum-recommended-security-settings" class=zola-anchor href=#minimum-recommended-security-settings>Minimum Recommended Security Settings</a></h3><h4 id=dkim><a aria-label="Anchor link for: dkim" class=zola-anchor href=#dkim>DKIM</a></h4><h4 id=spf-validation><a aria-label="Anchor link for: spf-validation" class=zola-anchor href=#spf-validation>SPF Validation</a></h4><p>The container provides SPF validation and the recommended DNS configuration for a single host e.g. <code>ayllu-forge.org</code> is <code>"v=spf1 a mx ~all"</code>.</div></div></main><footer class=page-footer>2024, <a href=/ayllu/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div> |
237 | \ No newline at end of file |