From 4281b536dbc07efff4b65e6d22f5ce53c32d4f11 Mon Sep 17 00:00:00 2001 From: "R. Tyler Croy" Date: Sun, 12 Jan 2020 13:22:06 -0800 Subject: [PATCH] Add initial version of the irc-push Azure Function This relies on spawning a thread for dispatching things to IRC, since GitHub webhooks will timeout in 10s https://github.community/t5/GitHub-API-Development-and/Extended-Service-Timeout-for-webhooks/td-p/19639 Really though, it's not like webhook logs are terribly useful in GitHub's VUI --- .dockerignore | 5 + .gitignore | 6 + .vscode/launch.json | 16 +++ .vscode/tasks.json | 46 +++++++ Cargo.toml | 17 +++ Dockerfile | 32 +++++ README.adoc | 17 +++ events.push.json | 136 +++++++++++++++++++++ host.json | 8 ++ local.settings.json | 9 ++ src/functions/mod.rs | 8 ++ src/functions/webhook.rs | 252 +++++++++++++++++++++++++++++++++++++++ src/main.rs | 5 + 13 files changed, 557 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 Cargo.toml create mode 100644 Dockerfile create mode 100644 README.adoc create mode 100644 events.push.json create mode 100644 host.json create mode 100644 local.settings.json create mode 100644 src/functions/mod.rs create mode 100644 src/functions/webhook.rs create mode 100644 src/main.rs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6d3f8da --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +target/ +Cargo.lock +.vscode/ +.git/ +.cargo/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a70fa7a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/target +**/*.rs.bk +*.swp +package-lock.json +node_modules/ +.cargo/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c9ec8d2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "attach", + "name": "Debug", + "windows": { + "program": "irc-push.exe" + }, + "program": "irc-push", + "preLaunchTask": "Launch Azure Functions Application", + "postDebugTask": "Terminate Azure Functions Application" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..a204f31 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,46 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Launch Azure Functions Application", + "type": "shell", + "command": "cargo", + "args": [ + "func", + "run" + ], + "presentation": { + "reveal": "always", + "clear": true, + "focus": true + }, + "problemMatcher": [ + { + "owner": "azureFunctions", + "pattern": [ + { + "regexp": "\\b\\B", + "file": 1, + "location": 2, + "message": 3 + } + ], + "background": { + "activeOnStart": true, + "beginsPattern": "^Azure Functions Core Tools", + "endsPattern": "^Application started." + } + } + ], + "isBackground": true + }, + { + "label": "Terminate Azure Functions Application", + "type": "process", + "command":"${command:workbench.action.tasks.terminate}", + "args": [ + "Launch Azure Functions Application" + ] + } + ] +} diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..182a515 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "irc-push" +version = "0.1.0" +authors = ["tyler"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +azure-functions = "0.11.0" +irc = "~0.13.6" +log = "~0.4.8" +pretty_env_logger = "~0.3.1" + +# Needed for deserializing objects +serde = "~1.0.103" +serde_json = "~1.0.0" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..57bb662 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +# syntax=docker/dockerfile-upstream:experimental + +FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image + +WORKDIR /src +COPY . /src + +RUN apt-get install -qy libssl-dev pkg-config + +# Run with mounted cache +RUN --mount=type=cache,target=/src/target \ + --mount=type=cache,target=/usr/local/cargo/git \ + --mount=type=cache,target=/usr/local/cargo/registry \ + ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] + +FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image + +FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 + +ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ + HOME=/home \ + FUNCTIONS_WORKER_RUNTIME=Rust \ + languageWorkers__workersDirectory=/home/site/wwwroot/workers + +# Copy the Azure Functions host from the runtime image +COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] + +# Copy the script root contents from the build image +COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] + +WORKDIR /home/site/wwwroot +CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..dc2dcad --- /dev/null +++ b/README.adoc @@ -0,0 +1,17 @@ += IRC push function + +== Development + +=== Local invocations + +[source,bash] +---- +curl -XPOST --data @events.push.json http://localhost:8100/api/webhook +---- + +=== Deployment + +[source,bash] +---- +docker build -t functionsbrokencode.azurecr.io/irc-push . && docker push functionsbrokencode.azurecr.io/irc-push +---- diff --git a/events.push.json b/events.push.json new file mode 100644 index 0000000..720a5ec --- /dev/null +++ b/events.push.json @@ -0,0 +1,136 @@ +{ + "ref": "refs/tags/simple-tag", + "before": "6113728f27ae82c7b1a177c8d03f9e96e0adf246", + "after": "0000000000000000000000000000000000000000", + "created": false, + "deleted": true, + "forced": false, + "base_ref": null, + "compare": "https://github.com/Codertocat/Hello-World/compare/6113728f27ae...000000000000", + "commits": [ + + ], + "head_commit": null, + "repository": { + "id": 186853002, + "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": false, + "owner": { + "name": "Codertocat", + "email": "21031067+Codertocat@users.noreply.github.com", + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/Codertocat/Hello-World", + "description": null, + "fork": false, + "url": "https://github.com/Codertocat/Hello-World", + "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks", + "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams", + "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks", + "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}", + "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events", + "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}", + "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}", + "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags", + "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages", + "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers", + "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors", + "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription", + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}", + "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges", + "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads", + "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}", + "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}", + "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}", + "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments", + "created_at": 1557933565, + "updated_at": "2019-05-15T15:20:41Z", + "pushed_at": 1557933657, + "git_url": "git://github.com/Codertocat/Hello-World.git", + "ssh_url": "git@github.com:Codertocat/Hello-World.git", + "clone_url": "https://github.com/Codertocat/Hello-World.git", + "svn_url": "https://github.com/Codertocat/Hello-World", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Ruby", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 1, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 2, + "license": null, + "forks": 1, + "open_issues": 2, + "watchers": 0, + "default_branch": "master", + "stargazers": 0, + "master_branch": "master" + }, + "pusher": { + "name": "Codertocat", + "email": "21031067+Codertocat@users.noreply.github.com" + }, + "sender": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} diff --git a/host.json b/host.json new file mode 100644 index 0000000..15e042a --- /dev/null +++ b/host.json @@ -0,0 +1,8 @@ +{ + "version": "2.0", + "logging": { + "logLevel": { + "default": "Information" + } + } +} diff --git a/local.settings.json b/local.settings.json new file mode 100644 index 0000000..94106c3 --- /dev/null +++ b/local.settings.json @@ -0,0 +1,9 @@ +{ + "IsEncrypted": false, + "Values": { + "FUNCTIONS_WORKER_RUNTIME": "Rust", + "languageWorkers:workersDirectory": "workers" + }, + "ConnectionStrings": { + } +} diff --git a/src/functions/mod.rs b/src/functions/mod.rs new file mode 100644 index 0000000..5bdbdb6 --- /dev/null +++ b/src/functions/mod.rs @@ -0,0 +1,8 @@ +// WARNING: This file is regenerated by the `cargo func new` command. + +mod webhook; + +// Export the Azure Functions here. +azure_functions::export! { + webhook::webhook, +} diff --git a/src/functions/webhook.rs b/src/functions/webhook.rs new file mode 100644 index 0000000..10b9c1e --- /dev/null +++ b/src/functions/webhook.rs @@ -0,0 +1,252 @@ +extern crate irc; +extern crate serde; +extern crate serde_json; + +use azure_functions::{ + bindings::{HttpRequest, HttpResponse}, + func, +}; +use irc::client::prelude::*; +use log::*; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use std::default::Default; +use std::thread; + +fn dispatch_to_irc(event: PushEvent) { + thread::spawn(move || { + /* + * Currently uncertain on how to import a file + */ + //let config = Config::load("Irc.toml").expect("failed to load Irc.toml"); + let config = Config { + nickname: Some("otto-gh-bot".to_owned()), + server: Some("chat.freenode.net".to_owned()), + channels: Some(vec!["#otto".to_owned()]), + ..Default::default() + }; + + /* + * Grabbing a clone of our configured channels before the config + * moves into IrcClient + */ + let channels = &config.channels.clone(); + + let client = IrcClient::from_config(config) + .expect("Failed to construct the IrcClient"); + client.identify().unwrap(); + + client.for_each_incoming(|irc_msg| { + let m = irc_msg.to_string(); + + /* + * This effectively means the channel has been synchronized and we can + * send the message we need to send + */ + if m.contains("End of /NAMES list") { + if let Some(channel) = channels { + for chan in channel.iter() { + info!("Sending message to {}", chan); + client.send_privmsg(chan, + format!("{} pushed {} commits to {} in {} <{}>", + &event.sender.login, + &event.commits.len(), + &event.refname, + &event.repository.full_name, + &event.compare)) + .expect(format!("Failed to send message to channel: {}", chan).as_str()); + client.send_quit("rusty , over and out") + .expect("Failed to send QUIT"); + } + } + } + }).unwrap(); + }); +} + +#[func] +pub fn webhook(req: HttpRequest) -> HttpResponse { + if let Ok(event) = req.body().as_json::() { + info!("Parsed push event from {}", event.sender.login); + dispatch_to_irc(event); + return "Messages dispatched to IRC".into(); + } + else { + return "Failed to parse event".into(); + } +} + +#[derive(Serialize, Deserialize)] +struct Sender { + login: String, +} +#[derive(Serialize, Deserialize)] +struct Repo { + name: String, + full_name: String, +} + +#[derive(Serialize, Deserialize)] +struct PushEvent { + #[serde(rename = "ref")] + refname: String, + sender: Sender, + compare: String, + commits: Vec, + repository: Repo, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_push_event() { + /* + * Example event taken from: + * + */ + let push = r#"{ + "ref": "refs/tags/simple-tag", + "before": "6113728f27ae82c7b1a177c8d03f9e96e0adf246", + "after": "0000000000000000000000000000000000000000", + "created": false, + "deleted": true, + "forced": false, + "base_ref": null, + "compare": "https://github.com/Codertocat/Hello-World/compare/6113728f27ae...000000000000", + "commits": [ + + ], + "head_commit": null, + "repository": { + "id": 186853002, + "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": false, + "owner": { + "name": "Codertocat", + "email": "21031067+Codertocat@users.noreply.github.com", + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/Codertocat/Hello-World", + "description": null, + "fork": false, + "url": "https://github.com/Codertocat/Hello-World", + "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks", + "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams", + "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks", + "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}", + "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events", + "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}", + "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}", + "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags", + "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages", + "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers", + "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors", + "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription", + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}", + "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges", + "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads", + "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}", + "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}", + "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}", + "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments", + "created_at": 1557933565, + "updated_at": "2019-05-15T15:20:41Z", + "pushed_at": 1557933657, + "git_url": "git://github.com/Codertocat/Hello-World.git", + "ssh_url": "git@github.com:Codertocat/Hello-World.git", + "clone_url": "https://github.com/Codertocat/Hello-World.git", + "svn_url": "https://github.com/Codertocat/Hello-World", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Ruby", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 1, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 2, + "license": null, + "forks": 1, + "open_issues": 2, + "watchers": 0, + "default_branch": "master", + "stargazers": 0, + "master_branch": "master" + }, + "pusher": { + "name": "Codertocat", + "email": "21031067+Codertocat@users.noreply.github.com" + }, + "sender": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} + "#; + + let event = serde_json::from_str::(push).expect("Failed to deserialized event"); + assert_eq!("Codertocat", event.sender.login); + assert_eq!("refs/tags/simple-tag", event.refname); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..29b8cd7 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,5 @@ +mod functions; + +fn main() { + azure_functions::worker_main(std::env::args(), functions::EXPORTS); +}