1+ #!/usr/bin/env python
2+
3+ import os
4+ import kinto_http
5+ import hashlib
6+ import pathlib
7+ import uuid
8+
9+ from settings import config
10+
11+ from constants import WEBKIT_LISTS_DIR
12+
13+ def get_config_if_env (env_var , config_section , config_option , fallback = "" ):
14+ """
15+ Return the config value if the environment variable exists; otherwise, return the fallback.
16+ """
17+ if env_var in os .environ :
18+ return os .environ [env_var ]
19+ return config .get (config_section , config_option , fallback = fallback )
20+
21+ def get_file_hash (list_path ):
22+ with open (list_path , "rb" ) as f :
23+ return hashlib .sha256 (f .read ()).hexdigest ()
24+
25+ SERVER = get_config_if_env ("SERVER" , "main" , "remote_settings_url" )
26+ BUCKET = "main-workspace"
27+ COLLECTION = "tracking-protection-lists-ios"
28+ AUTHORIZATION = get_config_if_env ("AUTHORIZATION" , "main" , "remote_settings_authorization" )
29+ ENVIRONMENT = os .getenv ("ENVIRONMENT" , "local" ).lower ()
30+ DRY_RUN = os .getenv ("DRY_RUN" , "0" ) in "1yY"
31+
32+ def publish2rs ():
33+ client = kinto_http .Client (
34+ server_url = SERVER ,
35+ auth = AUTHORIZATION ,
36+ bucket = BUCKET ,
37+ collection = COLLECTION ,
38+ dry_mode = DRY_RUN
39+ )
40+
41+ remote_attachments = {
42+ r ["name" ]: {"id" : r ["id" ], "hash" : r ["attachment" ]["hash" ]}
43+ for r in client .get_records ()
44+ }
45+
46+ local_attachments = {
47+ file .stem : get_file_hash (file )
48+ for file in pathlib .Path (WEBKIT_LISTS_DIR ).iterdir ()
49+ }
50+
51+ # Determine records to create, update, or delete
52+ # by comparing the attachment sha256 hashes.
53+ to_create = []
54+ to_update = []
55+ for name , hash in local_attachments .items ():
56+ remote_attachment = remote_attachments .pop (name , None )
57+ if remote_attachment is None :
58+ to_create .append ({"name" : name })
59+ elif remote_attachment ["hash" ] != hash :
60+ to_update .append ({"id" : remote_attachment ["id" ], "name" : name })
61+ # Remaining records in `remote_attachments` are to be deleted.
62+ to_delete = [{"id" : record ["id" ]} for _ , record in remote_attachments .items ()]
63+
64+ # Print changes
65+ print ("Changes to apply:" )
66+ print (f"To create: { to_create } " )
67+ print (f"To update: { to_update } " )
68+ print (f"To delete: { to_delete } " )
69+
70+ has_pending_changes = (len (to_create ) + len (to_update ) + len (to_delete )) > 0
71+ if not has_pending_changes :
72+ print ("Records are in sync. Nothing to do ✅." )
73+ return os .EX_OK
74+
75+ # Batch delete operations.
76+ # NOTE: Attachment deletion is implicit when deleting a record.
77+ with client .batch () as batch :
78+ for record in to_delete :
79+ batch .delete_record (id = record ["id" ])
80+
81+ # Adding, updating attachments on client
82+ # since batch operations are not supported.
83+ for record in to_create :
84+ id = str (uuid .uuid4 ())
85+ filepath = f"{ WEBKIT_LISTS_DIR } /{ record ['name' ]} .json"
86+ client .add_attachment (id = id , data = {"name" : record ["name" ]}, filepath = filepath )
87+ for record in to_update :
88+ filepath = f"{ WEBKIT_LISTS_DIR } /{ record ['name' ]} .json"
89+ client .add_attachment (id = record ["id" ], filepath = filepath )
90+
91+
92+ if ENVIRONMENT == "dev" :
93+ # Self approve changes on DEV.
94+ # message arg is not supported as of now.
95+ client .approve_changes ()
96+ print ("Changes applied to dev server ✅" )
97+ else :
98+ # Request review.
99+ print ("Request review..." , end = "" )
100+ client .request_review (message = "r?" )
101+ print ("✅" )
0 commit comments