import itertools import json import logging import pprint import re from collections import defaultdict from concurrent.futures import ThreadPoolExecutor from urllib.parse import quote_plus import boto3 import httpx import urllib.parse import subprocess import typing def list_repo_tags(*, repository_name: str, region: str, username: str, password: str) -> typing.List[dict]: """ Fetches the list of tags in a Codecommit repo using git ls-remote command :returns: List of {tag, commit} items """ url = "https://{username}:{password}@git-codecommit.{region}.amazonaws.com/v1/repos/{repository_name}".format( username=quote_plus(username), password=quote_plus(password), # passwords are base64-encoded and might contain "/" region=region, repository_name=repository_name, ) result = subprocess.run(["git", "ls-remote", "--refs", "--tags", url], text=True, stdout=subprocess.PIPE) tags = [] for line in result.stdout.splitlines(keepends=False): # lines are formatted as: # 070161940636bab5d934af2702f4248e5a13eb29 refs/tags/ui_sandbox_acc104 # and columns are delimited with "\t" if "refs/tags/" in line: commit_hash, tag_ref = line.split("\t", maxsplit=1) tag = tag_ref.replace("refs/tags/", "") tags.append({"commit": commit_hash, "tag": tag}) # natural-sort by tag name # flora_prod_1 < ... < flora_prod_9 > flora_prod_10 # vs lexical sort would give: # flora_prod_1 < flora_prod_10 < ... < flora_prod_9 tags = sorted(tags, key=lambda t: [int(it) if it.isdigit() else it for it in re.split(r"(\d+)", t["tag"])]) return tags def chunk(it, size): it = iter(it) sentinel = () return iter(lambda: tuple(itertools.islice(it, size)), sentinel) def summarize_tags(tags: list[dict], take: int = 10) -> list[dict]: re_prefix = re.compile(r"^(\D*)") re_digits = re.compile(r"(\d+)") grouped = defaultdict(list) for t in tags: tag_name = t["tag"] if m := re_prefix.search(tag_name): prefix = m.group(1) grouped[prefix].append(t) else: print("no match", m) flattened = [*grouped.pop("", [])] for g, items in grouped.items(): sorted_items = sorted( items, key=lambda t: [int(it) if it.isdigit() else it for it in re_digits.split(t["tag"])], reverse=True ) flattened.extend(sorted_items[:take]) return flattened abdus_creds = { "username": "coder+1-at-400344683105", "password": "4xdnGOtghKiT0M5CjulWf5raWwBSqT0Df2YkIY9Ws68=", } def get_commit_content(): client = boto3.client("codecommit") res = client.get_file( repositoryName="testing", filePath="content.txt1", commitSpecifier="vnext", ) print(res) def main(): import logging from botocore.endpoint import Endpoint logging.basicConfig(level=logging.INFO) raw_make_request = Endpoint.make_request def intercepted_make_request(*args, **kwargs): aws_response, parsed_response = raw_make_request(*args, **kwargs) logging.info('AWS response: %s', parsed_response) return aws_response, parsed_response Endpoint.make_request = intercepted_make_request session = boto3.Session( aws_access_key_id="AKIAQKZTNEVLBHDG5Q3X", aws_secret_access_key="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", region_name="eu-central-1", ) cc = session.client("rds") _ = cc.describe_db_clusters() if __name__ == "__main__": main() exit() # get_commit_content() # flora b1cd47a221b84b2884b3d24198085a9f-f0dc2e747ff241649715c20d48232d8e # oms 36363cc2f43b4cf5b1965af9f8925a4d-6deff091ce2d4c2483d8834c532a6f36 # omnitron 36363cc2f43b4cf5b1965af9f8925a4d-97400227a37940539c55489e1354ffc2 all_tags = list_repo_tags( repository_name="b1cd47a221b84b2884b3d24198085a9f-f0dc2e747ff241649715c20d48232d8e", username="sandbox-acc-api-user-at-023193265494", password="sYPWKdhSx4JliXNI4ffRb4qWYBHLxA54nczSJ6CmYCY=", region="eu-central-1", ) print(all_tags) exit(0) all_tags.append({"tag": "1asd", "commit": "asdf"}) summarized = summarize_tags(all_tags, take=5) commits = {t["commit"] for t in summarized} session = boto3.Session( aws_access_key_id="AKIAQKZTNEVLBHDG5Q3X", aws_secret_access_key="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", region_name="eu-central-1", ) client = session.client("codecommit") def get_commit_infos(commits: list[str]): res = client.batch_get_commits( repositoryName="36363cc2f43b4cf5b1965af9f8925a4d-97400227a37940539c55489e1354ffc2", commitIds=commits, ) return res["commits"] with ThreadPoolExecutor() as pool: results = list(pool.map(get_commit_infos, chunk(commits, 100))) clone_ssh_url = repo["repositoryMetadata"]["cloneUrlSsh"] print(repo) exit(0) # exit() from botocore.awsrequest import AWSRequest from botocore.signers import RequestSigner # AWS Version 4 signing example # DynamoDB API (CreateTable) # See: http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html # This version makes a POST request and passes request parameters # in the body (payload) of the request. Auth information is passed in # an Authorization header. import sys, os, base64, datetime, hashlib, hmac import requests # pip install requests # ************* REQUEST VALUES ************* method = "POST" service = "codecommit" host = "codecommit.eu-central-1.amazonaws.com" region = "eu-central-1" endpoint = "https://codecommit.eu-central-1.amazonaws.com/" # POST requests use a content type header. For DynamoDB, # the content is JSON. content_type = "application/x-amz-json-1.1" # DynamoDB requires an x-amz-target header that has this format: # DynamoDB_. amz_target = "CodeCommit_20150413.GetReferences" # Request parameters for CreateTable--passed in a JSON block. request_body = {"repositoryName": "36363cc2f43b4cf5b1965af9f8925a4d-6deff091ce2d4c2483d8834c532a6f36"} request_parameters = json.dumps(request_body) # Key derivation functions. See: # http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python def sign(key, msg): return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest() def getSignatureKey(key, date_stamp, regionName, serviceName): kDate = sign(("AWS4" + key).encode("utf-8"), date_stamp) kRegion = sign(kDate, regionName) kService = sign(kRegion, serviceName) kSigning = sign(kService, "aws4_request") return kSigning # Read AWS access key from env. variables or configuration file. Best practice is NOT # to embed credentials in code. access_key = os.environ.get("AWS_ACCESS_KEY_ID", "AKIAQKZTNEVLBHDG5Q3X") secret_key = os.environ.get("AWS_SECRET_ACCESS_KEY", "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") if access_key is None or secret_key is None: print("No access key is available.") sys.exit() # Create a date for headers and the credential string t = datetime.datetime.utcnow() amz_date = t.strftime("%Y%m%dT%H%M%SZ") date_stamp = t.strftime("%Y%m%d") # Date w/o time, used in credential scope # ************* TASK 1: CREATE A CANONICAL REQUEST ************* # http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html # Step 1 is to define the verb (GET, POST, etc.)--already done. # Step 2: Create canonical URI--the part of the URI from domain to query # string (use '/' if no path) canonical_uri = "/" ## Step 3: Create the canonical query string. In this example, request # parameters are passed in the body of the request and the query string # is blank. canonical_querystring = "" # Step 4: Create the canonical headers. Header names must be trimmed # and lowercase, and sorted in code point order from low to high. # Note that there is a trailing \n. canonical_headers = ( "content-type:" + content_type + "\n" + "host:" + host + "\n" + "x-amz-date:" + amz_date + "\n" + "x-amz-target:" + amz_target + "\n" ) # Step 5: Create the list of signed headers. This lists the headers # in the canonical_headers list, delimited with ";" and in alpha order. # Note: The request can include any headers; canonical_headers and # signed_headers include those that you want to be included in the # hash of the request. "Host" and "x-amz-date" are always required. # For DynamoDB, content-type and x-amz-target are also required. signed_headers = "content-type;host;x-amz-date;x-amz-target" # Step 6: Create payload hash. In this example, the payload (body of # the request) contains the request parameters. payload_hash = hashlib.sha256(request_parameters.encode("utf-8")).hexdigest() # Step 7: Combine elements to create canonical request canonical_request = ( method + "\n" + canonical_uri + "\n" + canonical_querystring + "\n" + canonical_headers + "\n" + signed_headers + "\n" + payload_hash ) # ************* TASK 2: CREATE THE STRING TO SIGN************* # Match the algorithm to the hashing algorithm you use, either SHA-1 or # SHA-256 (recommended) algorithm = "AWS4-HMAC-SHA256" credential_scope = date_stamp + "/" + region + "/" + service + "/" + "aws4_request" string_to_sign = ( algorithm + "\n" + amz_date + "\n" + credential_scope + "\n" + hashlib.sha256(canonical_request.encode("utf-8")).hexdigest() ) # ************* TASK 3: CALCULATE THE SIGNATURE ************* # Create the signing key using the function defined above. signing_key = getSignatureKey(secret_key, date_stamp, region, service) # Sign the string_to_sign using the signing_key signature = hmac.new(signing_key, (string_to_sign).encode("utf-8"), hashlib.sha256).hexdigest() # ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST ************* # Put the signature information in a header named Authorization. authorization_header = ( algorithm + " " + "Credential=" + access_key + "/" + credential_scope + ", " + "SignedHeaders=" + signed_headers + ", " + "Signature=" + signature ) # For DynamoDB, the request can include any headers, but MUST include "host", "x-amz-date", # "x-amz-target", "content-type", and "Authorization". Except for the authorization # header, the headers must be included in the canonical_headers and signed_headers values, as # noted earlier. Order here is not significant. # # Python note: The 'host' header is added automatically by the Python 'requests' library. headers = { "User-Agent": "aws-sdk-js/2.627.0 promise", "Content-Type": content_type, "X-Amz-Date": amz_date, "X-Amz-Target": amz_target, "Authorization": authorization_header, } # ************* SEND THE REQUEST ************* print("\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++") print("Request URL = " + endpoint) r = httpx.post(endpoint, json=request_body, headers=headers) print("\nRESPONSE++++++++++++++++++++++++++++++++++++") print("Response code: %d\n" % r.status_code) print(r.text)