---
version: '3'

vars:
  REPOSITORY_LIST: .cache/repository-list.txt

tasks:
  access-token:
    deps:
      - :install:software:glab
      - :install:software:jq
      - create
    vars:
      REPO_ID:
        sh: glab api projects/:fullpath | jq '.id'
      REQUEST_DATA: '{ "name":"PROJECT_CI_ACCESS_TOKEN", "scopes":["write_repository", "read_repository"] }'
      TOKEN_STATUS:
        sh: glab api projects/:fullpath/access_tokens | jq -r '.[] | select(.name=="PROJECT_CI_ACCESS_TOKENX") | .id'
    run: once
    log:
      error: Failed to configure project-specific access token
      start: Configuring project-specific access token
      success: Configured project-specific access token
    cmds:
      - |
        curl -sSL --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" --header \
        'Content-Type:application/json' --data '{{.REQUEST_DATA}}' \
        'https://gitlab.com/api/v4/projects/{{.REPO_ID}}/access_tokens' > /dev/null
    status:
      - '[ -n "{{.TOKEN_STATUS}}" ]'

  branches:
    deps:
      - :install:software:glab
      - create
    vars:
      REPO_ID:
        sh: glab api projects/:fullpath | jq '.id'
    run: once
    log:
      error: Error encountered while setting up protected branches
      start: Setting up protected branches
      success: Set up protected branches
    cmds:
      - |
        glab api projects/:fullpath/protected_branches -X POST -f name=master -f code_owner_approval_required=true &> /dev/null &
        glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=main -f ref=master &> /dev/null &
        glab api projects/:fullpath/protected_branches -X POST -f name=main -f code_owner_approval_required=true &> /dev/null &
        glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=next -f ref=master &> /dev/null &
        glab api projects/:fullpath/protected_branches -X POST -f name=next -f code_owner_approval_required=true &> /dev/null &
        glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=alpha -f ref=master &> /dev/null &
        glab api projects/:fullpath/protected_branches -X POST -f name=alpha -f code_owner_approval_required=true &> /dev/null &
        glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=beta -f ref=master &> /dev/null &
        glab api projects/:fullpath/protected_branches -X POST -f name=beta -f code_owner_approval_required=true &> /dev/null &
        glab api projects/:fullpath/protected_branches -X POST -f name=protected/* -f code_owner_approval_required=true &> /dev/null &
        glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=synchronize -f ref=master &> /dev/null &
        glab api projects/:fullpath/protected_branches -X POST -f name=synchronize -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
        if [[ '{{.REPOSITORY_TYPE}}' == 'ansible' ]]; then
          glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=test/linux -f ref=master &> /dev/null &
          glab api projects/:fullpath/protected_branches -X POST -f name=test/linux \
            -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
          glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=test/darwin -f ref=master &> /dev/null &
          glab api projects/:fullpath/protected_branches -X POST -f name=test/darwin \
            -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
          glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=test/windows -f ref=master &> /dev/null &
          glab api projects/:fullpath/protected_branches -X POST -f name=test/windows \
            -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
        fi
        wait

  create:
    deps:
      - :install:software:glab
      - :install:software:jq
      - :git:remotes
    vars:
      DESCRIPTION:
        sh: jq -r '.description' package.json
      GITLAB_GROUP:
        sh: jq -r '.blueprint.repository.gitlab' package.json | sed 's/https:\/\/gitlab.com\///' | sed 's!/[^/]*$!!'
      GITLAB_PATH:
        sh: jq -r '.blueprint.repository.gitlab' package.json | sed 's/https:\/\/gitlab.com\///'
      NAME:
        sh: jq -r '.blueprint.name' package.json
      PROJECT_TYPE:
        sh: if [[ $(jq -r '.private' package.json) == 'true' ]]; then echo '--private'; else echo '--public'; fi
      TMP:
        sh: mktemp
    run: once
    log:
      error: Encountered error while creating GitLab repository
      start: Ensuring GitLab repository has been created
      success: Ensured GitLab repository exists
    cmds:
      - cmd: |
          NO_PROMPT=1 glab repo create '{{.GITLAB_PATH}}' --group '{{.GITLAB_GROUP}}' \
          --description '{{.EMOJI_START}}{{.DESCRIPTION}}{{.EMOJI_END}}' --name '{{.NAME}}' \
          {{.PROJECT_TYPE}} --tag "$(jq -r '.keywords | tostring' package.json | sed 's/\[//' | sed 's/\]//')"
          git commit --quiet -m "🎂 Birth" -n
          git add --all
          git push --quiet -u --no-progress gitlab master
        ignore_error: true
    status:
      - '[ -z "$GITLAB_TOKEN" ] || (test -d .git && glab repo view "{{.GITLAB_PATH}}" > /dev/null)'
    preconditions:
      - sh: '[ "{{.DESCRIPTION}}" != "null" ]'
        msg: The `.description` in `package.json` must be set.
      - sh: '[ "{{.NAME}}" != "null" ]'
        msg: The `.blueprint.name` variable in `package.json` must be set.

  group:exec:
    cmds:
      - task: group:exec:{{if .CLI_ARGS}}cli{{else}}prompt{{end}}

  group:exec:cli:
    log:
      error: Failed to run group:exec logic
      start: Running group:exec logic
      success: Successfully ran group:exec logic
    cmds:
      - task git:gitlab:group:repositories -- {{index (splitList " ---- " .CLI_ARGS) 0}}
      - |
        BASE_PWD="$PWD"
        function execRepo() {
          REPO_DETAILS="$1"
          REPO_URL="$(echo "$REPO_DETAILS" | sed 's/   .*$//')"
          REPO_PATH="$(echo "$REPO_DETAILS" | sed 's/^.*   //')"
          DIR_NAME="$(dirname "$REPO_PATH")"
          mkdir -p "$DIR_NAME"
          if [ -d "$REPO_PATH" ]; then
            cd "$REPO_PATH"
            git pull origin master || continue
            cd "$BASE_PWD"
          else
            git clone "$REPO_URL" "$REPO_PATH"
          fi
          .config/log info "Running bash command on $REPO_PATH"
          (
            cd "$REPO_PATH"
            {{trimPrefix "'" (trimSuffix "'" (index (splitList " ---- " .CLI_ARGS) 1))}}
            cd "$BASE_PWD"
          )
          .config/log success "Finished running bash command on $REPO_PATH"
        }
        cat {{.REPOSITORY_LIST}} | (while IFS= read -r REPO_DETAILS; do
          .config/log info "Executing logic on $REPO_DETAILS"
          execRepo "$REPO_DETAILS"{{if eq .GROUP_EXEC_ASYNC "true"}} &{{end}}
        done{{if eq .GROUP_EXEC_ASYNC "true"}}
        wait){{else}}){{end}}

  group:exec:prompt:
    interactive: true
    cmds:
      - mkdir -p .cache
      - node .config/scripts/prompts/gitlab-group.js

  group:repositories:
    deps:
      - :install:software:glab
      - :install:software:jq
    summary: |
      # Return repositories belonging to group and sub-groups

      Given a GitLab group path (which can include subgroups as well), this task will cycle through
      the group and its' subgroups and generate a list of repositories. The repositories are
      saved to a file located at `{{.REPOSITORY_LIST}}`.

      **Example specifying a group and subgroup:**
      `task {{.TASK}} -- megabyte-labs/ansible-roles`
    log:
      error: Encountered error while generating a list of repositories
      start: Generating list of repositories
      success: List of repositories generated
    cmds:
      - mkdir -p .cache
      - rm -f {{.REPOSITORY_LIST}}
      - task: group:repositories:loop

  group:repositories:loop:
    cmds:
      - glab api "groups/{{urlquery .CLI_ARGS}}" | jq -r '.projects[] | .ssh_url_to_repo + "   " + .path_with_namespace' >> {{.REPOSITORY_LIST}}
      - |
        .config/log info 'Recursively acquiring repository details from subgroups'
        SUBGROUPS="$(glab api groups/{{urlquery .CLI_ARGS}}/subgroups | jq -r '.[] | .full_path')"
        if [ -n "$SUBGROUPS" ]; then
          echo "$SUBGROUPS" | (while IFS= read -r SUBGROUP; do
            task {{.TASK}} -- "$SUBGROUP" &
          done
          wait)
        fi

  ids:
    deps:
      - :install:software:glab
      - :install:software:jq
      - create
    log:
      error: Error acquiring GitLab project and group IDs
      start: Acquiring GitLab project and group IDs
      success: Saved GitLab project and group IDs to package.json
    cmds:
      - |
        API_RES="$(glab api projects/:fullpath)"
        PROJECT_ID="$(echo "$API_RES" | jq '.id')"
        TMP="$(mktemp)" && jq --arg projectId "$PROJECT_ID" '.blueprint.gitlab_project_id = $projectId' package.json > "$TMP"
        mv "$TMP" package.json
        GROUP_ID="$(echo "$API_RES" | jq '.namespace.id')"
        TMP_GROUP="$(mktemp)" && jq --arg groupId "$GROUP_ID" '.blueprint.gitlab_group_id = $groupId' package.json > "$TMP_GROUP"
        mv "$TMP_GROUP" package.json

  integrations:
    deps:
      - create
    run: once
    cmds:
      - task: integrations:github

  integrations:github:
    deps:
      - :install:software:glab
      - :install:software:jq
    log:
      error: Error enabling GitLab's GitHub integration
      start: Ensuring GitLab's GitHub integration is enabled
      success: GitLab's GitHub integration is enabled
    cmds:
      - glab api projects/:fullpath/integrations/github -X PUT -f token="$GITHUB_TOKEN"
        -f repository_url="$(jq -r '.blueprint.repository.github' package.json)" --silent
    status:
      - '[ -z "$GITLAB_TOKEN" ] || [ -z $GITHUB_TOKEN ]'

  mirror:
    deps:
      - :install:software:glab
      - :install:software:jq
      - :git:github:create
      - create
    vars:
      GITHUB_SLUG:
        sh: jq -r '.blueprint.repository.github' package.json | sed 's/.*\///'
      GITLAB_REPO_ID:
        sh: glab api projects/:fullpath | jq -r '.id'
      PUSH_MIRROR_COUNT:
        sh: glab api projects/:fullpath/remote_mirrors | jq '. | length'
    log:
      error: Error ensuring push/pull mirroring is enabled between GitLab and GitHub
      start: Ensuring push/pull mirroring from GitLab to GitHub is set up
      success: Push and pull mirroring from GitLab to GitHub are enabled
    cmds:
      - |
        if [[ "{{.PUSH_MIRROR_COUNT}}" == '0' ]]; then
          glab api projects/:fullpath/remote_mirrors --method POST --header "Content-Type: application/json" \
            -f "url=https://{{.GITHUB_USER}}:$GITHUB_TOKEN@github.com/{{.GITHUB_ORG}}/{{.GITHUB_SLUG}}.git" \
            -f 'enabled=true' > /dev/null
          .config/log success 'Successfully set up push mirroring from GitLab to GitHub'
        fi
      - >
        curl -s -H 'Content-Type: application/json' -H "Authorization: Bearer $GITLAB_TOKEN" -X PUT --data
        "{\"mirror\": true, \"import_url\": \"https://{{.GITHUB_USER}}:$GITHUB_TOKEN@github.com/{{.GITHUB_ORG}}/{{.GITHUB_SLUG}}.git\"}"
        'https://gitlab.com/api/v4/projects/{{.GITLAB_REPO_ID}}' > /dev/null
    status:
      - '[ -z "$GITLAB_TOKEN ] || [ -z "GITHUB_TOKEN ]'

  pipelines:
    deps:
      - :install:software:glab
      - :install:software:jq
      - create
    vars:
      PIPELINE_COUNT:
        sh: jq -r '.gitlab_pipelines | length' .variables.json
    log:
      error: Error setting up GitLab pipelines
      start: Ensuring GitLab pipelines are set up according to the configuration
      success: GitLab pipelines are set up
    cmds:
      - |
        PIPELINES="$(jq -r '.gitlab_pipelines' .variables.json)"
        PIPELINE_RES="$(glab api projects/:fullpath/pipeline_schedules)"
        for INDEX in {1..{{.PIPELINE_COUNT}}}; do
          PIPELINE_INDEX="$((INDEX - 1))"
          ACTIVE="$(echo "$PIPELINES" | jq -r --arg i "$PIPELINE_INDEX" '.[$i | tonumber].active')"
          CRON="$(echo "$PIPELINES" | jq -r --arg i "$PIPELINE_INDEX" '.[$i | tonumber].cron' | sed 's/"//g')"
          DESC="$(echo "$PIPELINES" | jq -r --arg i "$PIPELINE_INDEX" '.[$i | tonumber].description')"
          REF="$(echo "$PIPELINES" | jq -r --arg i "$PIPELINE_INDEX" '.[$i | tonumber].ref')"
          if (! echo "$PIPELINE_RES" | grep "$DESC") > /dev/null; then
            glab api projects/:fullpath/pipeline_schedules -X POST -f active="$ACTIVE" -f description="$DESC" -f ref="$REF" \
              -f cron="$CRON" -f cron_timezone='{{.TIMEZONE}}' --silent
            if [ "$DESC" != 'null' ]; then .config/log success "Pipeline with description of '${DESC}' successfully added"; fi
          else
            if [ "$DESC" != 'null' ]; then .config/log info "Pipeline with description of '${DESC}' already added"; fi
          fi
        done
    status:
      - '[ -z "$GITLAB_TOKEN" ]'

  pipelines:clear:
    deps:
      - create
    log:
      error: Error clearing GitLab pipelines
      start: Clearing GitLab pipelines
      success: Cleared GitLab pipelines
    cmds:
      - |
        TMP="$(mktemp)"
        glab api projects/:id/pipeline_schedules -X GET > "$TMP"
        for PIPELINE_ID in $(jq -r '.[].id' "$TMP"); do
          glab api projects/:fullpath/pipeline_schedules/"$PIPELINE_ID" -X DELETE
        done
    preconditions:
      - sh: '[ ! -z "$GITLAB_TOKEN" ]'
        msg: The `GITLAB_TOKEN` environment variable must be set to run this task

  update:
    deps:
      - access-token
      - branches
      - mirror
      - integrations
      - ids
      - pipelines
      - update:meta
      - wiki

  update:meta:
    deps:
      - :install:software:glab
      - :install:software:jq
      - create
    vars:
      DESCRIPTION:
        sh: jq -r '.description' package.json
      ISSUES_TEMPLATE: '## Summary\r\n\r\n(Summarize the bug encountered concisely)\r\n\r\n##
        Steps to reproduce\r\n\r\n(How one can reproduce the issue - this is very important)\r\n\r\n##
        Example Project\r\n\r\n(If possible, please create an example project here on GitLab.com that
        exhibits the problematic\r\nbehavior, and link to it here in the bug report.\r\nIf you are using
        an older version of GitLab, this will also determine whether the bug has been fixed\r\nin a more
        recent version)\r\n\r\n## What is the current bug behavior?\r\n\r\n(What actually happens)\r\n\r\n##
        What is the expected correct behavior?\r\n\r\n(What you should see instead)\r\n\r\n## Relevant
        logs and/or screenshots\r\n\r\n(Paste any relevant logs - please use code blocks (```) to format
        console output, logs, and code, as\r\nit is very hard to read otherwise.)\r\n\r\n## Possible
        fixes\r\n\r\n(If you can, link to the line of code that might be responsible for the problem)'
      NAME:
        sh: jq -r '.blueprint.name' package.json
      PROJECT_TYPE:
        sh: if [[ $(jq -r '.private' package.json) == 'true' ]]; then echo 'private'; else echo 'public'; fi
      TEST_COVERAGE_REGEX:
        sh: jq -r '.build_coverage_regex' .variables.json | sed 's/^null$//'
    log:
      error: Error ensuring GitLab metadata is up-to-date
      start: Ensuring GitLab metadata is up-to-date
      success: GitLab metadata is up-to-date
    cmds:
      - |
        KEYWORDS="$(jq -r '.keywords | tostring' package.json | sed 's/\[//' | sed 's/\]//' | sed 's/"//g')"
        PROJECT_ID="$(glab api projects/:fullpath -X PUT -f build_coverage_regex="{{.TEST_COVERAGE_REGEX}}" \
        -f wiki_enabled={{.GITLAB_WIKI}} -f visibility="{{.PROJECT_TYPE}}" -f topics="$KEYWORDS" | jq '.id')"
        curl -s -H 'Content-Type: application/json' -H "Authorization: Bearer $GITLAB_TOKEN" -X PUT --data \
        '{"description": "{{.EMOJI_START}}{{.DESCRIPTION}}{{.EMOJI_END}}", "issues_template": "{{.ISSUES_TEMPLATE}}", "name": "{{.NAME}}"}' \
        "https://gitlab.com/api/v4/projects/$PROJECT_ID" > /dev/null
    status:
      - '[ -z "$GITLAB_TOKEN" ]'
    preconditions:
      - sh: '[ "{{.DESCRIPTION}}" != "null" ]'
        msg: The `.description` in `package.json` must be set.
      - sh: '[ "{{.NAME}}" != "null" ]'
        msg: The `.blueprint.name` variable in `package.json` must be set.

  wiki:
    deps:
      - :common:update:variables
      - :install:software:glab
      - :install:software:jq
      - create
    vars:
      DOCS_URL:
        sh: jq -r '.docs.link' .variables.json
    log:
      error: Failed to update GitLab wiki settings
      start: Setting GitLab wiki settings
      success: GitLab wiki settings are up-to-date
    cmds:
      - glab api projects/:fullpath/services/external-wiki -X PUT -f external_wiki_url="{{.DOCS_URL}}" --silent
    status:
      - '[ -z "$GITLAB_TOKEN" ]'
    preconditions:
      - sh: '[ "{{.DOCS_URL}}" != "null" ]'
        msg: The `.docs.link` variable in `.variables.json` must be set.
      - sh: '[ "{{.GROUP}}" != "null" ]'
        msg: The `.group` variable in `.variables.json` must be set.
