Compare commits

...

12 commits

8 changed files with 75 additions and 74 deletions

View file

@ -30,6 +30,9 @@ jobs:
- name: 🔗 Artifactview PR comment
if: ${{ always() && github.event_name == 'pull_request' }}
run: |
echo "Run: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_NUMBER"
if [[ "$GITEA_ACTIONS" == "true" ]]; then RUN_NUMBER="$GITHUB_RUN_NUMBER"; else RUN_NUMBER="$GITHUB_RUN_ID"; fi
echo "Run: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$RUN_NUMBER"
echo "Pull: ${{ github.event.number }}"
curl -SsL --fail-with-body -w "\n" -X POST https://av.thetadev.de/.well-known/api/prComment -H "Content-Type: application/json" --data "{\"url\": \"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_NUMBER\", \"pr\": ${{ github.event.number }}, \"artifact_titles\": {\"test\":\"🧪 Test report\"}, \"artifact_paths\": {\"test\":\"/junit.xml?viewer=1\"}}"
curl -SsL --fail-with-body -w "\n" -X POST https://av.thetadev.de/.well-known/api/prComment -H "Content-Type: application/json" \
--data '{"url": "'"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$RUN_NUMBER"'", "pr": ${{ github.event.number }}, "artifact_titles": {"test":"🧪 Test report"}, "artifact_paths": {"test":"/junit.xml?viewer=1"}}'

View file

@ -3,6 +3,27 @@
All notable changes to this project will be documented in this file.
## [v0.4.4](https://codeberg.org/ThetaDev/artifactview/compare/v0.4.3..v0.4.4) - 2024-06-22
### 🐛 Bug Fixes
- Use forge aliases for PR comment links - ([3690b02](https://codeberg.org/ThetaDev/artifactview/commit/3690b0244cf47d0d73511f5f69f5d12abe0f1837))
## [v0.4.3](https://codeberg.org/ThetaDev/artifactview/compare/v0.4.2..v0.4.3) - 2024-06-22
### 🐛 Bug Fixes
- 404 error on GitHub comment creation - ([d8c3ab4](https://codeberg.org/ThetaDev/artifactview/commit/d8c3ab4f36727f118b31683db87d287d9945ee14))
## [v0.4.2](https://codeberg.org/ThetaDev/artifactview/compare/v0.4.1..v0.4.2) - 2024-06-22
### 🐛 Bug Fixes
- PR comment emoji prefix detection - ([1e36edf](https://codeberg.org/ThetaDev/artifactview/commit/1e36edf49978e8ba24a85d4663ff3ebaf9642a29))
## [v0.4.1](https://codeberg.org/ThetaDev/artifactview/compare/v0.4.0..v0.4.1) - 2024-06-22
### 🚀 Features

44
Cargo.lock generated
View file

@ -141,7 +141,7 @@ dependencies = [
[[package]]
name = "artifactview"
version = "0.4.1"
version = "0.4.4"
dependencies = [
"async_zip",
"axum",
@ -187,7 +187,6 @@ dependencies = [
"tower-http",
"tracing",
"tracing-subscriber",
"unic-emoji-char",
"url",
"yarte",
"yarte_helpers",
@ -3145,47 +3144,6 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
[[package]]
name = "unic-char-property"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
dependencies = [
"unic-char-range",
]
[[package]]
name = "unic-char-range"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
[[package]]
name = "unic-common"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
[[package]]
name = "unic-emoji-char"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d"
dependencies = [
"unic-char-property",
"unic-char-range",
"unic-ucd-version",
]
[[package]]
name = "unic-ucd-version"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
dependencies = [
"unic-common",
]
[[package]]
name = "unicase"
version = "2.7.0"

View file

@ -1,6 +1,6 @@
[package]
name = "artifactview"
version = "0.4.1"
version = "0.4.4"
edition = "2021"
authors = ["ThetaDev <thetadev@magenta.de>"]
license = "MIT"
@ -73,7 +73,6 @@ tokio-util = { version = "0.7.11", features = ["io"] }
tower-http = { version = "0.5.2", features = ["trace", "set-header"] }
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
unic-emoji-char = "0.9.0"
url = "2.5.0"
yarte = { version = "0.15.7", features = ["json"] }

View file

@ -1,5 +1,7 @@
# Artifactview
**This is a test PR**: 4
View CI build artifacts from Forgejo/GitHub using your web browser!
Forgejo and GitHub's CI systems allow you to upload files and directories as
@ -82,7 +84,8 @@ artifacts).
- name: 🔗 Artifactview PR comment
if: ${{ always() && github.event_name == 'pull_request' }}
run: |
curl -SsL --fail-with-body -w "\n" -X POST https://av.thetadev.de/.well-known/api/prComment -H "Content-Type: application/json" --data "{\"url\": \"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_NUMBER\", \"pr\": ${{ github.event.number }}}"
if [[ "$GITEA_ACTIONS" == "true" ]]; then RUN_NUMBER="$GITHUB_RUN_NUMBER"; else RUN_NUMBER="$GITHUB_RUN_ID"; fi
curl -SsL --fail-with-body -w "\n" -X POST https://av.thetadev.de/.well-known/api/prComment -H "Content-Type: application/json" --data "{\"url\": \"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$RUN_NUMBER\", \"pr\": ${{ github.event.number }}}"
```
## API
@ -254,11 +257,12 @@ Example list: `foo;bar`, example map: `foo=>f1;bar=>b1`
### Access tokens
GitHub does not allow downloading artifacts for public repositories for unauthenticated
users. So you need to setup an access token to use Artifactview with GitHub. These are
the permissions that need to be enabled:
users. So you need to setup an access token to use Artifactview with GitHub.
- Repository access: All repositories
- Repository permissions: Pull requests (Read and write)
If you are not using the `prComment` feature, you can use a fine-grained access token
with the "Public repositories (read-only)" permission. If you want to create pull
request comments, you have to use a classic token with the "public_repo" scope enabled
(the fine-grained tokens did not work in my test).
Forgejo does not require access tokens to download artifacts on public repositories, so
you only need to create a token if you want to use the `prComment`-API. In this case,

View file

@ -1,5 +1,4 @@
use std::{
borrow::Cow,
collections::{BTreeMap, HashMap},
fmt::Write,
net::{IpAddr, SocketAddr},
@ -673,7 +672,7 @@ impl App {
.extract::<Json<PrCommentReq>, _>()
.await
.map_err(|e| Error::BadRequest(e.body_text().into()))?;
let query = RunQuery::from_forge_url(&req.url)?;
let query = RunQuery::from_forge_url_alias(&req.url, &state.i.cfg.load().site_aliases)?;
if let Some(limiter) = &state.i.lim_pr_comment {
limiter.check_key(&ip).map_err(Error::from)?;
@ -866,20 +865,7 @@ fn pr_comment_text(p: PrCommentTextParams) -> String {
};
let write_link_icon = |s: &mut String, title: &str, href: &str| {
// Move leading emoji into a prefix variable since including them in the link does not look good
let mut title_pfx = String::new();
let mut title = Cow::Borrowed(title);
if let Some((i, c)) = title
.char_indices()
// Some emoji use variation selectors that are not included in is_emoji
.find(|(_, c)| !unic_emoji_char::is_emoji(*c) && !('\u{fe00}'..='\u{fe0f}').contains(c))
{
if i > 0 && c == ' ' {
title[..i + 1].clone_into(&mut title_pfx);
title = title[i + 1..].to_owned().into();
}
}
let (title_pfx, title) = util::split_icon_prefix(title);
_ = write!(
s,
r#"{title_pfx}<a href="{href}" target="_blank" rel="noopener noreferrer">{title}</a>"#,

View file

@ -269,6 +269,7 @@ impl ArtifactApi {
}
}
#[tracing::instrument(level = "error", skip_all)]
pub async fn list(&self, query: &RunQuery, cached: bool) -> Result<Vec<Artifact>> {
let cache_key = query.cache_key();
let fut = async {
@ -290,6 +291,7 @@ impl ArtifactApi {
}
}
#[tracing::instrument(level = "error", skip_all)]
pub async fn fetch(&self, query: &ArtifactQuery) -> Result<Artifact> {
if query.is_github() {
self.fetch_github(query).await
@ -305,6 +307,7 @@ impl ArtifactApi {
}
}
#[tracing::instrument(level = "error", skip_all)]
pub async fn download(&self, artifact: &Artifact, path: &Path) -> Result<()> {
if artifact.expired {
return Err(Error::Expired);
@ -416,10 +419,9 @@ impl ArtifactApi {
if let Err(e) = resp.error_for_status_ref() {
let status = resp.status();
let msg = resp.json::<ApiError>().await.ok();
Err(Error::HttpClient(
msg.map(|msg| msg.message).unwrap_or(e.to_string()).into(),
status,
))
let msg_str = msg.map(|msg| msg.message).unwrap_or(e.to_string()).into();
tracing::error!("API error: {msg_str}");
Err(Error::HttpClient(msg_str, status))
} else {
Ok(resp)
}
@ -492,6 +494,7 @@ impl ArtifactApi {
.header(header::AUTHORIZATION, format!("token {token}")))
}
#[tracing::instrument(level = "error", skip_all)]
pub async fn workflow_run(&self, query: &RunQuery) -> Result<WorkflowRun> {
if query.is_github() {
self.workflow_run_github(query).await
@ -554,6 +557,7 @@ impl ArtifactApi {
Ok(run.into())
}
#[tracing::instrument(level = "error", skip_all)]
pub async fn add_comment(
&self,
query: QueryRef<'_>,
@ -621,8 +625,8 @@ impl ArtifactApi {
) -> Result<u64> {
if let Some(old_comment_id) = old_comment_id {
let url = format!(
"https://api.github.com/repos/{}/{}/issues/{}/comments/{}",
query.user, query.repo, issue_id, old_comment_id
"https://api.github.com/repos/{}/{}/issues/comments/{}",
query.user, query.repo, old_comment_id
);
if recreate {
Self::send_api_req_empty(self.req_github(Method::DELETE, url)?).await?;
@ -650,6 +654,7 @@ impl ArtifactApi {
Ok(new_c.id)
}
#[tracing::instrument(level = "error", skip_all)]
pub async fn find_comment(
&self,
query: QueryRef<'_>,
@ -702,6 +707,7 @@ impl ArtifactApi {
Ok(None)
}
#[tracing::instrument(level = "error", skip_all)]
pub async fn get_pr(&self, query: QueryRef<'_>, pr_id: u64) -> Result<PullRequest> {
let req = if query.is_github() {
self.get_github(format!(

View file

@ -302,6 +302,18 @@ pub fn extract_delim<'a>(s: &'a str, start: &str, end: &str) -> Option<&'a str>
None
}
pub fn split_icon_prefix(s: &str) -> (&str, &str) {
if let Some((i, c)) = s
.char_indices()
.find(|(_, c)| c.is_ascii() || c.is_alphanumeric())
{
if i > 0 && c == ' ' && s.get(i + 1..).is_some() {
return (&s[..i + 1], &s[i + 1..]);
}
}
("", s)
}
#[cfg(test)]
pub(crate) mod tests {
use std::path::{Path, PathBuf};
@ -390,4 +402,16 @@ pub(crate) mod tests {
let res = super::filename_ext(filename);
assert_eq!(res, expect);
}
#[rstest]
#[case("🧪 Test", ("🧪 ", "Test"))]
#[case("🧪👨‍👩‍👦 Test", ("🧪👨‍👩‍👦 ", "Test"))]
#[case("🧪 👨‍👩‍👦 Test", ("🧪 ", "👨‍👩‍👦 Test"))]
#[case("", ("", ""))]
#[case("Test", ("", "Test"))]
#[case("運命 Test", ("", "運命 Test"))]
fn split_icon_prefix(#[case] s: &str, #[case] expect: (&str, &str)) {
let res = super::split_icon_prefix(s);
assert_eq!(res, expect);
}
}