Commit 92b82078 authored by anarcat's avatar anarcat 💥
Browse files

Merge branch 'i17-yaml-validation' into 'main'

Use the project-namespaced CI lint API endpoint

Closes #17

See merge request !41
parents dcc81713 dbb0448e
Loading
Loading
Loading
Loading
+39 −8
Original line number Diff line number Diff line
@@ -66,6 +66,12 @@ def main():
        help="""GitLab API URL endpoint,
        defaults to $CI_API_V4_URL environment or %(default)s if undefined""",
    )
    parser.add_argument(
        "--project-id",
        default=os.environ.get("CI_PROJECT_ID"),
        help="""GitLab CI project namespace to validate the YAML against,
        defaults to $CI_PROJECT_ID environment variable""",
    )
    parser.add_argument(
        "path",
        nargs="+",
@@ -73,29 +79,54 @@ def main():
        help="GitLab CI YAML files to validate",
    )
    args = parser.parse_args()
    if args.project_id is None:
        parser.error("missing CI_PROJECT_ID environment, use --project-id to specify")

    token = os.environ.get("GITLAB_PRIVATE_TOKEN")
    if token is None:
        logging.error("missing GITLAB_PRIVATE_TOKEN environment, aborting")
        sys.exit(1)
    headers = {
        "Content-Type": "application/json",
        "PRIVATE-TOKEN": token,
    }
    # XXX: this doesn't actually work. The CI_JOB_TOKEN variable
    # *does* exist, but it doesn't have the privileges necessary to
    # run the API. This is just too bad, and should probably be
    # reported upstream so that we don't have to go through the hoops
    # of creating the project-level access token once a year. see:
    # https://docs.gitlab.com/ee/api/rest/#job-tokens
    ci_job_token = os.environ.get("CI_JOB_TOKEN")
    if ci_job_token:
        headers['JOB-TOKEN'] = ci_job_token

    gitlab_private_token = os.environ.get("GITLAB_PRIVATE_TOKEN")
    if gitlab_private_token:
        headers['PRIVATE-TOKEN'] = gitlab_private_token

    if gitlab_private_token is None and ci_job_token is None:
        parser.error("missing GITLAB_PRIVATE_TOKEN or CI_JOB_TOKEN environment")

    success = True
    for path in args.path:
        logging.info("linting GitLab CI YAML file: %s", path.name)
        payload = {
            "content": path.read(),
            "dry_run": True,
        }
        logging.debug("sending paylod: %r", payload)
        r = requests.post(
            args.api_url + "/ci/lint",
            args.api_url + f"/projects/{args.project_id}/ci/lint",
            headers=headers,
            json=payload,
        )
        # XXX: a bit rough, we should handle errors here better
        try:
            r.raise_for_status()
        except requests.RequestException as e:
            # if this happens, you need to recreate the token here:
            # https://gitlab.torproject.org/tpo/tpa/ci-templates/-/settings/access_tokens
            # with "maintainer" role and "api" scope then copy it over
            # to the project's CI/CD variable as GITLAB_PRIVATE_TOKEN
            #
            # see also https://gitlab.torproject.org/tpo/tpa/ci-templates/-/issues/17
            logging.error("failed to call API endpoint: %s, is the token valid?", e)
            sys.exit(3)

        j = r.json()
        logging.debug("response: %r", j)
        if j["valid"]: