commit 9d88153b9f990652b2dd274efb7b561fc1446708 from: Stefan Sperling date: Mon Mar 11 09:49:10 2024 UTC WIP: generate notification content in repo_write commit - 4ddc9d7454d1838afd08981593a8b32332f3725f commit + 9d88153b9f990652b2dd274efb7b561fc1446708 blob - ed6de8db29817b25f285cd13c8395a03bfa2e2aa blob + 6e68ff331dae7881c9b5fdd240f869c658aedce6 --- gotd/repo_write.c +++ gotd/repo_write.c @@ -1627,8 +1627,203 @@ receive_pack_idx(struct imsg *imsg, struct gotd_imsgev return got_error(GOT_ERR_PRIVSEP_NO_FD); return NULL; +} + +static char * +get_datestr(time_t *time, char *datebuf) +{ + struct tm mytm, *tm; + char *p, *s; + + tm = gmtime_r(time, &mytm); + if (tm == NULL) + return NULL; + s = asctime_r(tm, datebuf); + if (s == NULL) + return NULL; + p = strchr(s, '\n'); + if (p) + *p = '\0'; + return s; +} + +static const struct got_error * +print_commit_or_tag(int fd, struct got_object_id *id) +{ + const struct got_error *err; + struct got_object_id *commit_id; + struct got_commit_object *commit = NULL; + struct got_tag_object *tag = NULL; + char *commit_id_str = NULL; + char *logmsg = NULL, *datestr; + const char *author = NULL, *committer = NULL; + char datebuf[26]; + time_t time; + int obj_type; + + err = got_object_open_as_commit(&commit, repo_write.repo, id); + if (err) { + if (err->code != GOT_ERR_OBJ_TYPE) + return err; + + err = got_object_open_as_tag(&tag, repo_write.repo, id); + if (err) + return err; + + dprintf(fd, "Tag: %s\n", got_object_tag_get_name(tag)); + dprintf(fd, "Tagger: %s\n", got_object_tag_get_tagger(tag)); + + obj_type = got_object_tag_get_object_type(tag); + if (obj_type != GOT_OBJ_TYPE_COMMIT) + goto done; + + commit_id = got_object_tag_get_object_id(tag); + err = got_object_open_as_commit(&commit, repo_write.repo, + commit_id); + if (err) + goto done; + } else + commit_id = id; + + err = got_object_id_str(&commit_id_str, commit_id); + if (err) + goto done; + + err = got_object_commit_get_logmsg(&logmsg, commit); + if (err) + goto done; + + committer = got_object_commit_get_committer(commit); + author = got_object_commit_get_author(commit); + dprintf(fd, "Commit: %s\n", commit_id_str); + dprintf(fd, "Committer: %s\n", committer); + datestr = get_datestr(&time, datebuf); + dprintf(fd, "Date: %s\n", datestr); + if (strcmp(author, committer) != 0) + dprintf(fd, "Author: %s\n", author); + dprintf(fd, "Log message:\n%s\n", logmsg); +done: + if (commit) + got_object_commit_close(commit); + if (tag) + got_object_tag_close(tag); + free(commit_id_str); + free(logmsg); + return err; +} + +static const struct got_error * +notify_created_ref(const char *refname, uint8_t *sha1, + struct gotd_imsgev *iev, int fd) +{ + const struct got_error *err; + struct got_object_id id; + char *id_str; + + memset(&id, 0, sizeof(id)); + memcpy(id.sha1, sha1, sizeof(id.sha1)); + + err = got_object_id_str(&id_str, &id); + if (err) + return err; + + dprintf(fd, "Created %s: %s\n", refname, id_str); + err = print_commit_or_tag(fd, &id); + free(id_str); + return err; } +static const struct got_error * +notify_removed_ref(const char *refname, uint8_t *sha1, + struct gotd_imsgev *iev, int fd) +{ + const struct got_error *err; + struct got_object_id id; + char *id_str; + + memset(&id, 0, sizeof(id)); + memcpy(id.sha1, sha1, sizeof(id.sha1)); + + err = got_object_id_str(&id_str, &id); + if (err) + return err; + + dprintf(fd, "Removed %s: %s\n", refname, id_str); + free(id_str); + return err; +} + +static const struct got_error * +notify_changed_ref(const char *refname, uint8_t *old_sha1, + uint8_t *new_sha1, struct gotd_imsgev *iev, int fd) +{ + const struct got_error *err; + struct got_object_id old_id, new_id; + char *old_id_str = NULL, *new_id_str = NULL; + + memset(&old_id, 0, sizeof(old_id)); + memcpy(old_id.sha1, old_sha1, sizeof(old_id.sha1)); + memset(&new_id, 0, sizeof(new_id)); + memcpy(new_id.sha1, new_sha1, sizeof(new_id.sha1)); + + err = got_object_id_str(&old_id_str, &old_id); + if (err) + return err; + err = got_object_id_str(&new_id_str, &new_id); + if (err) + goto done; + + dprintf(fd, "Changed %s: %s\n", refname, new_id_str); + dprintf(fd, "(formerly %s)\n", old_id_str); + + /* TODO: Walk commits and determine the new set... */ +done: + free(old_id_str); + free(new_id_str); + return err; +} + +static const struct got_error * +render_notification(struct imsg *imsg, struct gotd_imsgev *iev) +{ + const struct got_error *err = NULL; + struct gotd_imsg_notification_content ireq; + size_t datalen; + char *refname; + + if (imsg->fd == -1) + return got_error(GOT_ERR_PRIVSEP_NO_FD); + + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + if (datalen < sizeof(ireq)) + return got_error(GOT_ERR_PRIVSEP_LEN); + + memcpy(&ireq, imsg->data, sizeof(ireq)); + + if (datalen != sizeof(ireq) + ireq.refname_len) + return got_error(GOT_ERR_PRIVSEP_LEN); + + refname = strndup(imsg->data + sizeof(ireq), ireq.refname_len); + if (refname == NULL) + return got_error_from_errno("strndup"); + + switch (ireq.action) { + case GOTD_NOTIF_ACTION_CREATED: + err = notify_created_ref(refname, ireq.new_id, iev, imsg->fd); + break; + case GOTD_NOTIF_ACTION_REMOVED: + err = notify_removed_ref(refname, ireq.old_id, iev, imsg->fd); + break; + case GOTD_NOTIF_ACTION_CHANGED: + err = notify_changed_ref(refname, ireq.old_id, ireq.new_id, + iev, imsg->fd); + break; + } + + free(refname); + return err; +} + static void repo_write_dispatch_session(int fd, short event, void *arg) { @@ -1721,6 +1916,11 @@ repo_write_dispatch_session(int fd, short event, void log_warnx("update refs: %s", err->msg); } break; + case GOTD_IMSG_NOTIFY: + err = render_notification(&imsg, iev); + if (err) + log_warnx("render notification: %s", err->msg); + break; default: log_debug("unexpected imsg %d", imsg.hdr.type); break;