diff --git a/.appveyor.yml b/.appveyor.yml index b4ef80f3af..f28d6cd444 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,6 +18,8 @@ init: set GITHUB_API_PENDING="{"""state""": """pending""", """target_url""": """https://ci.appveyor.com/project/%APPVEYOR_REPO_NAME%/build/%APPVEYOR_BUILD_VERSION%""", """description""": """Starting the appveyor build.""", """context""": """continuous-integration/appveyor"""}" + set APPVEYOR_BUILD_URL=https://ci.appveyor.com/project/%APPVEYOR_ACCOUNT_NAME%/%APPVEYOR_PROJECT_SLUG%/build/%APPVEYOR_BUILD_VERSION% + curl -u wolfman2000:%access_token% -H "User-Agent: Appveyor-wolfman2000-stepmania-app" -H "Accept: application/vnd.github.v3+json" -H "Content-Type: application/json" -X POST -d %GITHUB_API_PENDING% %GITHUB_API_URL% install: @@ -27,18 +29,25 @@ before_build: - cmd: >- cd Build - cmake -G "Visual Studio 14 2015" .. + cmake -G "Visual Studio 14 2015" -DWITH_IRC_POST_HOOK=ON .. build: project: C:\Repos\wolfmania\Build\StepMania.sln verbosity: normal on_success: - cmd: >- - set GITHUB_API_SUCCESS="{"""state""": """success""", """target_url""": """https://ci.appveyor.com/project/%APPVEYOR_REPO_NAME%/build/%APPVEYOR_BUILD_VERSION%""", """description""": """Successfully compiled in appveyor.""", """context""": """continuous-integration/appveyor"""}" + set GITHUB_API_SUCCESS="{"""state""": """success""", """target_url""": """%APPVEYOR_BUILD_URL%""", """description""": """Successfully compiled in appveyor.""", """context""": """continuous-integration/appveyor"""}" curl -u wolfman2000:%access_token% -H "User-Agent: Appveyor-wolfman2000-stepmania-app" -H "Accept: application/vnd.github.v3+json" -H "Content-Type: application/json" -X POST -d %GITHUB_API_SUCCESS% %GITHUB_API_URL% + + echo "Posting to IRC now..." + + C:\Repos\wolfmania\Program\irc-reporter.exe "%APPVEYOR_REPO_NAME%" "%APPVEYOR_BUILD_VERSION%" "%APPVEYOR_REPO_COMMIT%" "%APPVEYOR_REPO_COMMIT_AUTHOR%" "%APPVEYOR_BUILD_URL%" "success" on_failure: - cmd: >- - set GITHUB_API_FAILURE="{"""state""": """failure""", """target_url""": """https://ci.appveyor.com/project/%APPVEYOR_REPO_NAME%/build/%APPVEYOR_BUILD_VERSION%""", """description""": """Build failure on appveyor.""", """context""": """continuous-integration/appveyor"""}" + set GITHUB_API_FAILURE="{"""state""": """failure""", """target_url""": """%APPVEYOR_BUILD_URL%""", """description""": """Build failure on appveyor.""", """context""": """continuous-integration/appveyor"""}" curl -u wolfman2000:%access_token% -H "User-Agent: Appveyor-wolfman2000-stepmania-app" -H "Accept: application/vnd.github.v3+json" -H "Content-Type: application/json" -X POST -d %GITHUB_API_FAILURE% %GITHUB_API_URL% + echo "Posting to IRC now..." + + C:\Repos\wolfmania\Program\irc-reporter.exe "%APPVEYOR_REPO_NAME%" "%APPVEYOR_BUILD_VERSION%" "%APPVEYOR_REPO_COMMIT%" "%APPVEYOR_REPO_COMMIT_AUTHOR%" "%APPVEYOR_BUILD_URL%" "failure" diff --git a/CMake/DefineOptions.cmake b/CMake/DefineOptions.cmake index 5d5b6daf86..cb433dcdf7 100644 --- a/CMake/DefineOptions.cmake +++ b/CMake/DefineOptions.cmake @@ -47,6 +47,8 @@ endif() if(WIN32) option(WITH_MINIMAID "Build with Minimaid Lights Support." ON) + # Developer only option: connect to IRC to report the result. Only use with build servers. + option(WITH_IRC_POST_HOOK "Report via IRC of the success afterwards." OFF) elseif(LINUX) # Builder beware: later versions of ffmpeg may break! option(WITH_SYSTEM_FFMPEG "Build with the system's FFMPEG, disabled build with bundled's FFMPEG" OFF) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7bd7c0396b..78f518fcf8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,9 @@ file(MAKE_DIRECTORY "${SM_SRC_DIR}/generated") if(MSVC) # Add the mapconv program of the past to allow for debugging information. include("CMakeProject-mapconv.cmake") + if (WITH_IRC_POST_HOOK) + include("CMakeProject-irc.cmake") + endif() endif() # Keep the module path local for easier grabbing. diff --git a/src/CMakeProject-irc.cmake b/src/CMakeProject-irc.cmake new file mode 100644 index 0000000000..d444654e4a --- /dev/null +++ b/src/CMakeProject-irc.cmake @@ -0,0 +1,33 @@ +if (NOT MSVC) + return() +endif() + +set(IRC_DIR "${SM_SRC_DIR}/irc") + +list(APPEND IRC_SRC + "${IRC_DIR}/appveyor.cpp" +) + +source_group("" FILES ${IRC_SRC}) + +add_executable("irc-reporter" ${IRC_SRC}) + +set_property(TARGET "irc-reporter" PROPERTY FOLDER "Internal Libraries") + +disable_project_warnings("irc-reporter") + +set_target_properties("irc-reporter" PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${SM_PROGRAM_DIR}" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${SM_PROGRAM_DIR}" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${SM_PROGRAM_DIR}" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${SM_PROGRAM_DIR}" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${SM_PROGRAM_DIR}" +) + +set_target_properties("irc-reporter" PROPERTIES + OUTPUT_NAME "irc-reporter" + RELEASE_OUTPUT_NAME "irc-reporter" + DEBUG_OUTPUT_NAME "irc-reporter" + MINSIZEREL_OUTPUT_NAME "irc-reporter" + RELWITHDEBINFO_OUTPUT_NAME "irc-reporter" +) \ No newline at end of file diff --git a/src/irc/appveyor.cpp b/src/irc/appveyor.cpp new file mode 100644 index 0000000000..1abbde0344 --- /dev/null +++ b/src/irc/appveyor.cpp @@ -0,0 +1,147 @@ +#include +#include +#pragma comment(lib, "ws2_32.lib") + +#include +#include +#include +#include + +char const *owner = "wolfman2000"; +char const *nick = "appveyor-sm5"; +char const *server = "irc.freenode.net"; +char const *channel = "#stepmania-devs"; + +bool startsWithPing(char const *buffer) +{ + return std::strncmp(buffer, "PING ", 5) == 0; +} + +int main(int argc, char* argv[]) +{ + // The common return value. + int ret; + // Set up the buffer. + char buffer[512]; + + auto *repoName = argv[1]; + auto *repoVersion = argv[2]; + auto *repoHash = argv[3]; + auto *repoAuthor = argv[4]; + auto *repoBuildUrl = argv[5]; + auto *repoSuccess = argv[6]; + + SOCKET sock; + struct WSAData *wd = (struct WSAData*)malloc(sizeof(struct WSAData)); + ret = WSAStartup(MAKEWORD(2, 0), wd); + free(wd); + + if (ret) + { + std::cout << "Error loading Windows Socket API" << std::endl; + return 1; + } + + struct addrinfo hints; + struct addrinfo *ai; + + // Ensure the hints are cleared. + memset(&hints, 0, sizeof(struct addrinfo)); + // IPv4 vs IPv6: should not matter. + hints.ai_family = AF_UNSPEC; + // IRC uses TCP sockets. + hints.ai_socktype = SOCK_STREAM; + // Be explicit about TCP. + hints.ai_protocol = IPPROTO_TCP; + + ret = getaddrinfo(server, "6667", &hints, &ai); + if (ret != 0) + { + std::cout << "Problem with getting the server information!" << std::endl; + return 2; + } + + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock == -1) + { + std::cout << "Problem with setting up the socket!" << std::endl; + return 3; + } + + ret = connect(sock, ai->ai_addr, ai->ai_addrlen); + if (ret == -1) + { + std::cout << "Problem with connecting to the IRC server!" << std::endl; + } + + // The server data is not needed anymore. + freeaddrinfo(ai); + + // Start some of the basic commands. + std::sprintf(buffer, "USER %s 0 * :%s\r\n", nick, owner); + send(sock, buffer, strlen(buffer), 0); + std::sprintf(buffer, "NICK %s\r\n", nick); + send(sock, buffer, strlen(buffer), 0); + + // Respect the continuous loop. + for (;;) + { + // receive the bytes as we get them. + int numBytes = recv(sock, buffer, 511, 0); + if (numBytes <= 0) + { + // We have quit IRC. Get out of here. + break; + } + buffer[numBytes] = '\0'; // Ensure a null terminated string. + if (startsWithPing(buffer)) + { + // We MUST reply to a PING with PONG. Otherwise, we get booted off. + buffer[1] = 'O'; + send(sock, buffer, strlen(buffer), 0); + } + // System commands should have a colon. + if (buffer[0] != ':') + { + continue; + } + + // Only join once we have a 001. This indicates it's safe. + if (std::strncmp(std::strchr(buffer, ' ') + 1, "001", 3)) + { + std::sprintf(buffer, "JOIN %s\r\n", channel); + send(sock, buffer, strlen(buffer), 0); + continue; + } + if (std::strncmp(std::strchr(buffer, ' ') + 1, "366", 3)) + { + if (std::strncmp(repoSuccess, "success", 7) == 0) + { + std::sprintf(buffer, "PRIVMSG %s :AppVeyor build report for fork %s, build %s, commit %s by %s: Compiles on Windows!\r\n", + channel, repoName, repoVersion, repoHash, repoAuthor + ); + } + else + { + std::sprintf(buffer, "PRIVMSG %s :AppVeyor build report for fork %s, build %s, commit %s by %s: Did not compile on Windows!\r\n", + channel, repoName, repoVersion, repoHash, repoAuthor + ); + } + send(sock, buffer, strlen(buffer), 0); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + std::sprintf(buffer, "PRIVMSG %s :Build Log: %s\r\n", channel, repoBuildUrl); + send(sock, buffer, strlen(buffer), 0); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + std::sprintf(buffer, "QUIT Bye bye now.\r\n"); + send(sock, buffer, strlen(buffer), 0); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + } + + closesocket(sock); + WSACleanup(); + + return 0; +}