| /*
* Sai push definitions src/push/pu-private.h
*
* Copyright (C) 2026 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "../common/include/private.h"
/*
* How long we ask sai-web to hold a request for (it caps it at 600 anyway)
*/
#define SAIP_WAIT_S 600
/*
* sai-web sends a keepalive newline every 10s on a held request, so this long
* without anything arriving means the connection is dead
*/
#define SAIP_RX_TIMEOUT_S 45
/* don't retry pushing the same hash to the same branch sooner than this */
#define SAIP_RETRY_S 600
/* longest one git step may take before it's killed */
#define SAIP_GIT_TIMEOUT_S 600
/*
* One promotion rule: a feed branch matching "match" (an lws_strcmp_wildcard
* pattern, default "*") and ending in branch_suffix is promoted to the
* branch named without the suffix, with a force push if force. The first
* matching rule in the watch decides.
*/
typedef struct saip_rule {
lws_dll2_t list;
const char *match;
const char *branch_suffix;
int force;
} saip_rule_t;
/*
* A mirror of the watched repo: whatever we push to the primary remote, we
* also push to the same branch here. The project name is appended to url.
* If token_file is given, it holds a token git gives as the password when
* the (https) remote asks, eg, a github fine-grained access token.
*/
typedef struct saip_mirror {
lws_dll2_t list;
const char *url;
const char *token_file;
} saip_mirror_t;
/*
* What we know about promoting to one target branch of a watch
*/
typedef struct saip_target {
lws_dll2_t list;
char branch[65];
char pushed[65]; /* hash we last pushed everywhere */
char tried[65]; /* hash we last tried */
lws_usec_t tried_at;
/*
* The last event promoted to this branch on the primary, kept in the
* state file across restarts: an event whose notification arrived
* before this one's is never promoted over it
*/
uint64_t promoted_received;
char promoted_hash[65];
char promoted_uuid[65];
} saip_target_t;
typedef struct saip_watch {
lws_dll2_t list;
/* from the conf */
const char *feed; /* sai rss.xml or rss.json url */
const char *fetchurl; /* events must have this */
const char *remote; /* project name is appended */
lws_dll2_owner_t rules; /* saip_rule_t */
lws_dll2_owner_t mirrors; /* saip_mirror_t */
/* runtime */
char host[128];
char path[256]; /* rss.json path, from '/' */
int port;
int tls;
struct lws *wsi;
lws_sorted_usec_list_t sul; /* next feed request */
uint16_t retry_count;
struct lejp_ctx ctx;
lws_struct_args_t a; /* the feed being parsed */
int http_status;
char index[33]; /* from the last feed */
lws_dll2_owner_t targets; /* saip_target_t */
uint8_t parse_done:1;
uint8_t parse_failed:1;
uint8_t handled:1;
} saip_watch_t;
enum {
SAIP_STEP_INIT, /* git init --bare, idempotent */
SAIP_STEP_FETCH, /* fetch the primary remote's branches */
SAIP_STEP_ON_SRC, /* the hash must be on the feed branch */
/* then these for the primary, and each mirror in turn */
SAIP_STEP_R_FETCH_DST, /* fetch the remote's target branch */
SAIP_STEP_R_IN_DST, /* if the remote's target has it, skip it */
SAIP_STEP_R_PUSH, /* push the hash to the remote's target */
SAIP_STEP_DONE
};
/*
* One promotion, run as a series of git commands, one at a time. Remote 0
* is the watch's primary remote, 1.. are its mirrors in order.
*/
typedef struct saip_job {
lws_dll2_t list;
saip_watch_t *w;
saip_target_t *t;
char project[65];
char src[65]; /* feed branch, eg main-dev */
char dst[65]; /* target branch, eg main */
char hash[65];
char uuid[65]; /* the event */
uint64_t received; /* the event's notification */
char repo[512]; /* bare repo in the cache */
int force;
int step;
int remote; /* 0 primary, 1.. mirrors */
uint8_t dst_fetched:1; /* remote's target is fetched */
uint8_t failed:1; /* a mirror failed */
char line[256]; /* partial output line */
size_t line_len;
} saip_job_t;
/* the conf, as parsed into its lwsac */
typedef struct saip_conf {
const char *user;
const char *repo_cache;
lws_dll2_owner_t watches; /* saip_watch_t */
} saip_conf_t;
typedef struct saip {
struct lws_context *cx;
struct lws_vhost *vh;
struct lwsac *ac_conf;
saip_conf_t *conf;
char home[256]; /* for the git child env */
char self[256]; /* our own path, for GIT_ASKPASS */
lws_dll2_owner_t jobs; /* saip_job_t, head is running */
struct lws_spawn_piped *lsp;
lws_sorted_usec_list_t sul_job;
} saip_t;
extern saip_t saip;
extern const struct lws_protocols protocol_saip_feed, protocol_saip_git;
int
saip_conf_load(const char *path);
void
saip_feed_start(saip_watch_t *w);
void
saip_feed_process(saip_watch_t *w, sai_feed_t *f);
void
saip_job_queue(saip_watch_t *w, saip_target_t *t, const sai_feed_item_t *it,
int force);
saip_target_t *
saip_target_get(saip_watch_t *w, const char *branch);
int
saip_state_load(void);
void
saip_state_save(void);
int
saip_askpass(const char *prompt);
|