Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9146d14bd1 | |||
| 17762e08b5 | |||
| a670e0c6db | |||
| f77cfa26ce | |||
| 5831820a14 | |||
| 498cd40955 | |||
| fd121e288e | |||
| c863e318e7 | |||
| 579c495afb | |||
| c5ca27c958 | |||
| ee924054b4 | |||
| b85bf9ae0d | |||
| c70bcf427a |
@@ -1,79 +0,0 @@
|
||||
name: build
|
||||
|
||||
on: [ push ]
|
||||
|
||||
jobs:
|
||||
linting:
|
||||
runs-on: nixos-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: bootstrap
|
||||
uses: actions/nix/develop@main
|
||||
|
||||
- name: formatting
|
||||
uses: actions/nix/develop@main
|
||||
with:
|
||||
run: cargo fmt --check
|
||||
|
||||
- name: clippy
|
||||
uses: actions/nix/develop@main
|
||||
with:
|
||||
run: cargo clippy
|
||||
|
||||
coverage:
|
||||
runs-on: nixos-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: bootstrap
|
||||
uses: actions/nix/develop@main
|
||||
|
||||
- name: test & coverage
|
||||
id: coverage
|
||||
uses: actions/nix/develop@main
|
||||
with:
|
||||
run: |
|
||||
cargo tarpaulin --tests --engine llvm --exclude-files src/main.rs | tee build.log
|
||||
tail -n 1 build.log | sed -e 's/^/coverage=/' >>"${GITHUB_OUTPUT}"
|
||||
|
||||
- name: summary
|
||||
run: |
|
||||
echo ${{ steps.coverage.outputs.coverage }}
|
||||
|
||||
build:
|
||||
runs-on: nixos-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: build
|
||||
uses: actions/nix/build@main
|
||||
with:
|
||||
package: ironforge
|
||||
|
||||
- name: build the container image
|
||||
uses: actions/nix/build@main
|
||||
with:
|
||||
package: container-image
|
||||
|
||||
- name: load the container image into docker
|
||||
run: |
|
||||
docker load <result
|
||||
|
||||
publish:
|
||||
runs-on: nixos-latest
|
||||
needs: [ linting, coverage, build ]
|
||||
steps:
|
||||
- name: login to the container registry
|
||||
uses: https://code.forgejo.org/docker/login-action@v2
|
||||
with:
|
||||
registry: git.madhouse-project.org
|
||||
username: algernon
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: publish
|
||||
run: |
|
||||
docker push git.madhouse-project.org/algernon/ironforge:latest
|
||||
@@ -0,0 +1,30 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_USERNAME }}/${{ github.event.repository.name }}:latest
|
||||
@@ -8,14 +8,14 @@ ironforge
|
||||
[container:badge]: https://img.shields.io/badge/container-ironforge:latest-blue?style=for-the-badge
|
||||
[container:url]: https://git.madhouse-project.org/algernon/-/packages/container/ironforge/latest
|
||||
|
||||
A lightweight bridge between Forgejo Actions and shields.io.
|
||||
A lightweight bridge between Gitea Actions and shields.io.
|
||||
|
||||
## Usage
|
||||
|
||||
In `docker-compose-yaml`:
|
||||
|
||||
``` yaml
|
||||
version: 3
|
||||
version: "3"
|
||||
services:
|
||||
ironforge:
|
||||
image: git.madhouse-project.org/algernon/ironforge:latest
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# This is a more or less default configuration for Ironforge.
|
||||
# Unless noted otherwise, all settings here use the default values.
|
||||
|
||||
[server]
|
||||
# The address and port to listen on.
|
||||
#
|
||||
# ⚠ There is no default, this must be set explicitly.
|
||||
listen_addr = "0.0.0.0:3000"
|
||||
|
||||
[server.cache]
|
||||
# Whether to enable caching.
|
||||
#
|
||||
# If enabled, badges will be cached in-memory (keyed by the URL they are
|
||||
# fetched from), with no expiry. Caching improves performance a *lot*, and
|
||||
# reduces the reliance on shields.io.
|
||||
#
|
||||
# Becaue it is all stored in-memory, only enable this if you expect to have a
|
||||
# small number of badges.
|
||||
#enable = false
|
||||
|
||||
[forge]
|
||||
# URL for the Forgejo/Gitea instance to ask for Action statuses.
|
||||
#
|
||||
# ⚠ There is no default, this must be set explicitly.
|
||||
url = "https://gitea.nicholaspease.com"
|
||||
# Branch to check for action statuses. The `branch` query parameter can override this.
|
||||
#branch = "main"
|
||||
|
||||
[badges]
|
||||
# A shields.io URL to redirect to for badges.
|
||||
#shields_url = "https://img.shields.io"
|
||||
# Label to use for the badge. The `label` query parameter can override this.
|
||||
#label = "ci"
|
||||
# Style to use for the badge. The `style` query parameter can override this.
|
||||
#style = "for-the-badge"
|
||||
|
||||
# Action status to color mappings. These set the background color of the
|
||||
# content part of the badge.
|
||||
[badges.color]
|
||||
#success = "brightgreen"
|
||||
#pending = "lightgrey"
|
||||
#error = "lightgrey"
|
||||
#failure = "crimson"
|
||||
#warning = "orange"
|
||||
#running = "gold"
|
||||
#unknown = "lightgrey"
|
||||
+40
-22
@@ -50,8 +50,13 @@ struct ActionAPI {
|
||||
fallback_url: FallbackURL,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
struct ActionAPIResponse {
|
||||
statuses: Vec<ActionAPIStatuses>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
struct ActionAPIStatuses {
|
||||
status: String,
|
||||
target_url: String,
|
||||
}
|
||||
@@ -92,7 +97,7 @@ impl ActionAPI {
|
||||
.forge
|
||||
.url
|
||||
.join(&format!(
|
||||
"/api/v1/repos/{}/{}/statuses/{}",
|
||||
"/api/v1/repos/{}/{}/commits/{}/status",
|
||||
encode(owner),
|
||||
encode(repo),
|
||||
encode(&config.forge.branch)
|
||||
@@ -100,11 +105,7 @@ impl ActionAPI {
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let request = reqwest::Client::new()
|
||||
.get(&url)
|
||||
.query(&[("sort", "leastindex"), ("limit", "1")])
|
||||
.send()
|
||||
.await;
|
||||
let request = reqwest::Client::new().get(&url).send().await;
|
||||
|
||||
match request {
|
||||
Err(e) => {
|
||||
@@ -134,7 +135,7 @@ impl ActionAPI {
|
||||
}
|
||||
|
||||
pub async fn send(self) -> Result<ActionAPIResponse, AppError> {
|
||||
let status = self.response.json::<Vec<ActionAPIResponse>>().await;
|
||||
let status = self.response.json::<ActionAPIResponse>().await;
|
||||
|
||||
match status {
|
||||
Err(e) => {
|
||||
@@ -146,8 +147,8 @@ impl ActionAPI {
|
||||
)))
|
||||
}
|
||||
Ok(r) => {
|
||||
if !r.is_empty() {
|
||||
Ok(r[0].clone())
|
||||
if !r.statuses.is_empty() {
|
||||
Ok(r.clone())
|
||||
} else {
|
||||
log::error!(target: "ironforge::actions", "No statuses received.");
|
||||
Err(AppError::ActionStatusEmpty(self.fallback_url.to_string(
|
||||
@@ -174,15 +175,22 @@ async fn latest_log(
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let redir_url = if status.target_url.starts_with('/') {
|
||||
config
|
||||
.forge
|
||||
.url
|
||||
.join(&status.target_url)
|
||||
.unwrap()
|
||||
.to_string()
|
||||
let redir_url = status
|
||||
.statuses
|
||||
.iter()
|
||||
.fold(None, |u, item| {
|
||||
if item.target_url.starts_with('/') {
|
||||
Some(item.target_url.clone())
|
||||
} else {
|
||||
u.or(Some(item.target_url.clone()))
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let redir_url = if redir_url.starts_with('/') {
|
||||
config.forge.url.join(&redir_url).unwrap().to_string()
|
||||
} else {
|
||||
status.target_url
|
||||
redir_url
|
||||
};
|
||||
|
||||
Ok(Redirect::temporary(&redir_url))
|
||||
@@ -197,13 +205,23 @@ async fn badge(
|
||||
let Query(q) = q.unwrap();
|
||||
let config = q.merge_into(&config);
|
||||
|
||||
let status = ActionAPI::new(&config, &owner, &repo, FallbackURL::Badge)
|
||||
let statuses = ActionAPI::new(&config, &owner, &repo, FallbackURL::Badge)
|
||||
.await?
|
||||
.send()
|
||||
.await?
|
||||
.status;
|
||||
.statuses;
|
||||
|
||||
let color = match status.as_str() {
|
||||
let state = if statuses.iter().all(|item| item.status == "success") {
|
||||
"success"
|
||||
} else {
|
||||
&statuses
|
||||
.iter()
|
||||
.find(|item| item.status != "success")
|
||||
.unwrap()
|
||||
.status
|
||||
};
|
||||
|
||||
let color = match state {
|
||||
"pending" => config.badges.color.pending,
|
||||
"success" => config.badges.color.success,
|
||||
"error" => config.badges.color.error,
|
||||
@@ -219,7 +237,7 @@ async fn badge(
|
||||
.join(&format!(
|
||||
"/badge/{}-{}-{}",
|
||||
encode(&config.badges.label),
|
||||
encode(&status),
|
||||
encode(state),
|
||||
encode(&color)
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ impl FallbackURL {
|
||||
.join(&format!(
|
||||
"/badge/{}-{}-{}",
|
||||
encode(&config.badges.label),
|
||||
encode("internal error"),
|
||||
encode("none"),
|
||||
encode("lightgrey"),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
+9
-9
@@ -20,12 +20,12 @@ pub fn status_with_relative_url(status: &str) -> (ServerGuard, Mock) {
|
||||
let mut server = mockito::Server::new();
|
||||
|
||||
let mock = server
|
||||
.mock("GET", "/api/v1/repos/test/test/statuses/main")
|
||||
.mock("GET", "/api/v1/repos/test/test/commits/main/status")
|
||||
.match_query(mockito::Matcher::Any)
|
||||
.with_status(200)
|
||||
.with_header("content-type", "application/json")
|
||||
.with_body(&format!(
|
||||
r#"[{{"status":"{}", "target_url":"/totally-latest-log"}}]"#,
|
||||
.with_body(format!(
|
||||
r#"{{"statuses":[{{"status":"{}","target_url":"/totally-latest-log"}}]}}"#,
|
||||
status
|
||||
))
|
||||
.create();
|
||||
@@ -37,12 +37,12 @@ pub fn status_with_absolute_url(status: &str) -> (ServerGuard, Mock) {
|
||||
let mut server = mockito::Server::new();
|
||||
|
||||
let mock = server
|
||||
.mock("GET", "/api/v1/repos/test/test/statuses/main")
|
||||
.mock("GET", "/api/v1/repos/test/test/commits/main/status")
|
||||
.match_query(mockito::Matcher::Any)
|
||||
.with_status(200)
|
||||
.with_header("content-type", "application/json")
|
||||
.with_body(&format!(
|
||||
r#"[{{"status":"{}", "target_url":"https://somewhere/totally-latest-log"}}]"#,
|
||||
.with_body(format!(
|
||||
r#"{{"statuses":[{{"status":"{}","target_url":"https://somewhere/totally-latest-log"}}]}}"#,
|
||||
status
|
||||
))
|
||||
.create();
|
||||
@@ -54,11 +54,11 @@ pub fn no_statuses() -> (ServerGuard, Mock) {
|
||||
let mut server = mockito::Server::new();
|
||||
|
||||
let mock = server
|
||||
.mock("GET", "/api/v1/repos/test/test/statuses/main")
|
||||
.mock("GET", "/api/v1/repos/test/test/commits/main/status")
|
||||
.match_query(mockito::Matcher::Any)
|
||||
.with_status(200)
|
||||
.with_header("content-type", "application/json")
|
||||
.with_body("[]")
|
||||
.with_body(r#"{{"statuses":[]}}"#)
|
||||
.create();
|
||||
|
||||
(server, mock)
|
||||
@@ -68,7 +68,7 @@ pub fn status_with_bad_json() -> (ServerGuard, Mock) {
|
||||
let mut server = mockito::Server::new();
|
||||
|
||||
let mock = server
|
||||
.mock("GET", "/api/v1/repos/test/test/statuses/main")
|
||||
.mock("GET", "/api/v1/repos/test/test/commits/main/status")
|
||||
.match_query(mockito::Matcher::Any)
|
||||
.with_status(200)
|
||||
.with_header("content-type", "application/json")
|
||||
|
||||
Reference in New Issue
Block a user