diff options
Diffstat (limited to 'doc/ci/quick_start/tutorial.md')
-rw-r--r-- | doc/ci/quick_start/tutorial.md | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/doc/ci/quick_start/tutorial.md b/doc/ci/quick_start/tutorial.md new file mode 100644 index 00000000000..88d35bf56b0 --- /dev/null +++ b/doc/ci/quick_start/tutorial.md @@ -0,0 +1,504 @@ +--- +stage: Verify +group: Pipeline Authoring +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# Tutorial: Create a complex pipeline + +This tutorial walks you through configuring a progressively more complex CI/CD pipeline +through small, iterative steps. The pipeline is always fully functional, +but it gains more functionality with each step. + +When you finish this tutorial, you will have a new project on GitLab.com and a working documentation site on +[Docusaurus](https://docusaurus.io/). + +To complete this tutorial, you will: + +1. Create a project to hold the Docusaurus files +1. Create the initial pipeline configuration file +1. Add a job to build the site +1. Add a job to deploy the site +1. Add test jobs +1. Start using merge request pipelines +1. Reduce duplicated configuration + +## Prerequisites + +- You need an account on GitLab.com. +- You should be familiar with Git. +- Node.js must be installed on your local machine. For example, on macOS you can + [install node](https://formulae.brew.sh/formula/node) with `brew install node`. + +## Create a project to hold the Docusaurus files + +Before adding the pipeline configuration, you must first set up a Docusaurus project +on GitLab.com: + +1. Create a new project under your username (not a group): + 1. On the top bar, select **Main menu > Projects > View all projects**. + 1. On the right of the page, select **New project**. + 1. Select **Create blank project**. + 1. Enter the project details: + - In the **Project name** field, enter the name of your project, for example `My Pipeline Tutorial Project`. + - Select **Initialize repository with a README**. + 1. Select **Create project**. +1. On the right of the **Project Overview** page for your project, select **Clone** + to find the clone paths for your project. Copy the SSH or HTTP path and use the path + to clone the project locally. + + For example, to clone with SSH into a `pipeline-tutorial` directory on your computer: + + ```shell + git clone git@gitlab.com:my-username/my-pipeline-tutorial-project.git pipeline-tutorial + ``` + +1. Change to the project's directory, then generate a new Docusaurus site: + + ```shell + cd pipeline-tutorial + npm init docusaurus + ``` + + The Docusaurus initialization wizard prompts you with questions about the site. + Use all the default options. + +1. The initialization wizard sets up the site in `website/`, but the site should be in + the root of the project. Move the files up to the root and delete the old directory: + + ```shell + mv website/* . + rm -r website + ``` + +1. Update the Docusaurus configuration file with the details of your GitLab project. + In `docusaurus.config.js`: + + - Set `url:` to a path with this format: `https://<my-username>.gitlab.io/`. + - Set `baseUrl:` to your project name, like `/my-pipeline-tutorial-project/`. + +1. Commit the changes, and push them to GitLab: + + ```shell + git add . + git commit -m "Add simple generated Docusaurus site" + git push origin + ``` + +## Create the initial CI/CD configuration file + +Start with the simplest possible pipeline configuration file to ensure CI/CD is enabled +in the project and runners are available to run jobs. + +This step introduces: + +- [Jobs](../jobs/index.md): These are self-contained parts of a pipeline that run your commands. + Jobs run on [runners](../runners/index.md), separate from the GitLab instance. +- [`script`](../yaml/index.md#script): This section of a job's configuration is + where you define the commands for jobs. If there are multiple commands (in an array), + they run in order. Each command executes as if it was run as a CLI command. + By default, if a command fails or returns an error, the job is flagged as failed + and no more commands run. + +In this step, create a `.gitlab-ci.yml` file in the root of the project with this configuration: + +```yaml +test-job: + script: + - echo "This is my first job!" + - date +``` + +Commit and push this change to GitLab, then: + +1. Go to **Build > Pipelines** and make sure a pipeline runs in GitLab with this single job. +1. Select the pipeline, then select the job to view the job's log and see the `This is my first job!` message + followed by the date. + +Now that you have a `.gitlab-ci.yml` file in your project, you can make all future changes +to pipeline configuration with the [pipeline editor](../pipeline_editor/index.md). + +## Add a job to build the site + +A common task for a CI/CD pipeline is to build the code in the project then deploy it. +Start by adding a job that builds the site. + +This step introduces: + +- [`image`](../yaml/index.md#image): Tell the runner which Docker + container to use to run the job in. The runner: + 1. Downloads the container image and starts it. + 1. Clones your GitLab project into the running container. + 1. Runs the `script` commands, one at a time. +- [`artifacts`](../yaml/index.md#artifacts): Jobs are self-contained and do not share + resources with each other. If you want files generated in one job to be used in + another job, you must save them as artifacts first. Then later jobs can retrieve the + artifacts and use the generated files. + +In this step, replace `test-job` with `build-job`: + +- Use `image` to configure the job to run with the latest `node` image. Docusaurus + is a Node.js project and the `node` image has the needed `npm` commands built in. +- Run `npm install` to install Docusaurus into the running `node` container, then run + `npm run build` to build the site. +- Docusaurus saves the built site in `build/`, so save these files with `artifacts`. + +```yaml +build-job: + image: node + script: + - npm install + - npm run build + artifacts: + paths: + - "build/" +``` + +Use the pipeline editor to commit this pipeline configuration to the default branch, +and check the job log. You can: + +- See the `npm` commands run and build the site. +- Verify that the artifacts are saved at the end. +- Browse the contents of the artifacts file by selecting **Browse** to the right of the job log + after the job completes. + +## Add a job to deploy the site + +After verifying the Docusaurus site builds in `build-job`, you can add a job that deploys it. + +This step introduces: + +- [`stage`](../yaml/index.md#stage) and [`stages`](../yaml/index.md#stage): The most common + pipeline configurations group jobs into stages. Jobs in the same stage can run in parallel, + while jobs in later stages wait for jobs in earlier stages to complete. If a job fails, + the whole stage is considered failed and jobs in later stages do not start running. +- [GitLab Pages](../../user/project/pages/index.md): To host your static site, you + will use GitLab Pages. + +In this step: + +- Add a job that fetches the built site and deploys it. When using GitLab Pages, + the job is always named `pages`. The artifacts from the `build-job` are fetched automatically + and extracted into the job. Pages looks for the site in the `public/` directory though, + so add a `script` command to move the site to that directory. +- Add a `stages` section, and define the stages for each job. `build-job` runs first + in the `build` stage, and `pages` runs after in the `deploy` stage. + +```yaml +stages: # List of stages for jobs and their order of execution + - build + - deploy + +build-job: + stage: build # Set this job to run in the `build` stage + image: node + script: + - npm install + - npm run build + artifacts: + paths: + - "build/" + +pages: + stage: deploy # Set this new job to run in the `deploy` stage + script: + - mv build/ public/ + artifacts: + paths: + - "public/" +``` + +Use the pipeline editor to commit this pipeline configuration to the default branch, +and view the pipeline details from the **Pipelines** list. Verify that: + +- The two jobs run in different stages, `build` and `deploy`. +- After the `pages` job completes a `pages-deploy` job appears, which is the GitLab process + that deploys the Pages site. When that job completes, you can visit your new Docusaurus + site. The Pages documentation explains [the URL formatting](../../user/project/pages/getting_started_part_one.md#gitlab-pages-default-domain-names), + which should be similar to `https://<my-username>.gitlab.io/<my-pipeline-tutorial-project>/`. + +## Add test jobs + +Now that the site builds and deploys as expected, you can add tests and linting. +For example, a Ruby project might run RSpec test jobs. Docusaurus is a static site +that uses Markdown and generated HTML, so this tutorial adds jobs to test the Markdown and HTML. + +This step introduces: + +- [`allow_failure`](../yaml/index.md#allow_failure): Jobs that fail intermittently, + or are expected to fail, can slow down productivity or be difficult to troubleshoot. + Use `allow_failure` to let jobs fail without halting pipeline execution. +- [`dependencies`](../yaml/index.md#dependencies): Use `dependencies` to control + artifact downloads in individual jobs by listing which jobs to fetch artifacts from. + +In this step: + +- Add a new `test` stage that runs between `build` and `deploy`. These three stages + are the default stages when `stages` is undefined in the configuration. +- Add a `lint-markdown` job to run [markdownlint](https://github.com/DavidAnson/markdownlint) + and check the Markdown in your project. markdownlint is a static analysis tool that + checks that your Markdown files follow formatting standards. + - The sample Markdown files Docusaurus generates are in `blog/` and `docs/`. + - This tool scans the original Markdown files only, and does not need the generated HTML + saved in the `build-job` artifacts. Speed up the job with `dependencies: []` + so that it fetches no artifacts. + - A few of the sample Markdown files violate default markdownlint rules, so add + `allow_failure: true` to let the pipeline continue despite the rule violations. +- Add a `test-html` job to run [HTMLHint](https://htmlhint.com/) and check the generated HTML. + HTMLHint is a static analysis tool that scans generated HTML for known issues. +- Both `test-html` and `pages` need the generated HTML found in the `build-job` artifacts. + Jobs fetch artifacts from all jobs in earlier stages by default, but add `dependencies:` + to make sure the jobs don't accidentally download other artifacts after future pipeline changes. + +```yaml +stages: + - build + - test # Add a `test` stage for the test jobs + - deploy + +build-job: + stage: build + image: node + script: + - npm install + - npm run build + artifacts: + paths: + - "build/" + +lint-markdown: + stage: test + image: node + dependencies: [] # Don't fetch any artifacts + script: + - npm install markdownlint-cli2 --global # Install markdownlint into the container + - markdownlint-cli2 -v # Verify the version, useful for troubleshooting + - markdownlint-cli2 "blog/**/*.md" "docs/**/*.md" # Lint all markdown files in blog/ and docs/ + allow_failure: true # This job fails right now, but don't let it stop the pipeline. + +test-html: + stage: test + image: node + dependencies: + - build-job # Only fetch artifacts from `build-job` + script: + - npm install --save-dev htmlhint # Install HTMLHint into the container + - npx htmlhint --version # Verify the version, useful for troubleshooting + - npx htmlhint build/ # Lint all markdown files in blog/ and docs/ + +pages: + stage: deploy + dependencies: + - build-job # Only fetch artifacts from `build-job` + script: + - mv build/ public/ + artifacts: + paths: + - "public/" +``` + +Commit this pipeline configuration to the default branch, and view the pipeline details. + +- The `test-markdown` job fails because the sample Markdown violates the default + markdownlint rules, but is allowed to fail. You can: + - Ignore the violations for now. They do not need to be fixed as part of the tutorial. + - Fix the Markdown file violations. Then you can change `allow_failure` to `false`, + or remove `allow_failure` completely because `allow_failure: false` is the default behavior + when not defined. + - Add a markdownlint configuration file to limit which rule violations to alert on. +- You can also make changes to the Markdown file content and see the changes on the site + after the next deployment. + +## Start using merge request pipelines + +With the pipeline configurations above, the site deploys every time a pipeline completes +successfully, but this is not an ideal development workflow. It's better to work from +feature branches and merge requests, and only deploy the site when changes merge +to the default branch. + +This step introduces: + +- [`rules`](../yaml/index.md#rules): Add rules to each job to configure in which + pipelines they run. You can configure jobs to run in [merge request pipelines](../pipelines/merge_request_pipelines.md), + [scheduled pipelines](../pipelines/schedules.md), or other specific situations. + Rules are evaluated from top to bottom, and if a rule matches, the job is + added to the pipeline. +- [CI/CD variables](../variables/index.md): use these environment variables + to configure job behavior in the configuration file and in script commands. + [Predefined CI/CD variables](../variables/predefined_variables.md) are variables + that you do not need to manually define. They are automatically injected into pipelines + so you can use them to configure your pipeline. Variables are usually formatted as `$VARIABLE_NAME`. + and predefined variables are usually prefixed with `$CI_`. + +In this step: + +- Create a new feature branch and make the changes in the branch instead of the default branch. +- Add `rules` to each job: + - The site should only deploy for changes to the default branch. + - The other jobs should run for all changes in merge requests or the default branch. +- With this pipeline configuration, you can work from a feature branch without running any jobs, + which saves resources. When you are ready to validate your changes, create a merge request + and a pipeline runs with the jobs configured to run in merge requests. +- When your merge request is accepted and the changes merge to the default branch, + a new pipeline runs which also contains the `pages` deployment job. The site deploys + if no jobs fail. + +```yaml +stages: + - build + - test + - deploy + +build-job: + stage: build + image: node + script: + - npm install + - npm run build + artifacts: + paths: + - "build/" + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' # Run for all changes to a merge request's source branch + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run for all changes to the default branch + +lint-markdown: + stage: test + image: node + dependencies: [] + script: + - npm install markdownlint-cli2 --global + - markdownlint-cli2 -v + - markdownlint-cli2 "blog/**/*.md" "docs/**/*.md" + allow_failure: true + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' # Run for all changes to a merge request's source branch + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run for all changes to the default branch + +test-html: + stage: test + image: node + dependencies: + - build-job + script: + - npm install --save-dev htmlhint + - npx htmlhint --version + - npx htmlhint build/ + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' # Run for all changes to a merge request's source branch + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run for all changes to the default branch + +pages: + stage: deploy + dependencies: + - build-job + script: + - mv build/ public/ + artifacts: + paths: + - "public/" + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run for all changes to the default branch only +``` + +Merge the changes in your merge request. This action updates the default branch. Verify that +the new pipeline contains the `pages` job that deploys the site. + +Be sure to use feature branches and merge requests for all future changes to pipeline configuration. +Other project changes, like creating a Git tag or adding a pipeline schedule, do not +trigger pipelines unless you add rules for those cases too. + +## Reduce duplicated configuration + +The pipeline now contains three jobs that all have identical `rules` and `image` +configuration. Instead of repeating these rules, use `extends` and `default` to create +single sources of truth. + +This step introduces: + +- [Hidden jobs](../jobs/index.md#hide-jobs): Jobs that start with `.` are never + added to a pipeline. Use them to hold configuration you want to reuse. +- [`extends`](../yaml/index.md#extends): Use extends to repeat configuration in + multiple places, often from hidden jobs. If you update the hidden job's configuration, + all jobs extending the hidden job use the updated configuration. +- [`default`](../yaml/index.md#default): Set keyword defaults that apply to all jobs + when not defined. +- YAML overriding: When reusing configuration with `extends` or `default`, you can explicitly + define a keyword in the job to override the `extends` or `default` configuration. + +In this step: + +- Add a `.standard-rules` hidden job to hold the rules that are repeated in `build-job`, + `lint-markdown`, and `test-html`. +- Use `extends` to reuse the `.standard-rules` configuration in the three jobs. +- Add a `default` section to define the `image` default as `node`. +- The `pages` deployment job does not need the default `node` image, so explicitly use + [`busybox`](https://hub.docker.com/_/busybox), an extremely tiny and fast image. + +```yaml +stages: + - build + - test + - deploy + +default: # Add a default section to define the `image` keyword's default value + image: node + +.standard-rules: # Make a hidden job to hold the common rules + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + +build-job: + extends: + - .standard-rules # Reuse the configuration in `.standard-rules` here + stage: build + script: + - npm install + - npm run build + artifacts: + paths: + - "build/" + +lint-markdown: + stage: test + extends: + - .standard-rules # Reuse the configuration in `.standard-rules` here + dependencies: [] + script: + - npm install markdownlint-cli2 --global + - markdownlint-cli2 -v + - markdownlint-cli2 "blog/**/*.md" "docs/**/*.md" + allow_failure: true + +test-html: + stage: test + extends: + - .standard-rules # Reuse the configuration in `.standard-rules` here + dependencies: + - build-job + script: + - npm install --save-dev htmlhint + - npx htmlhint --version + - npx htmlhint build/ + +pages: + stage: deploy + image: busybox # Override the default `image` value with `busybox` + dependencies: + - build-job + script: + - mv build/ public/ + artifacts: + paths: + - "public/" + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH +``` + +Use a merge request to commit this pipeline configuration to the default branch. +The file is simpler, but it should have the same behavior as the previous step. + +You've just created a full pipeline and streamlined it to be more efficient. Nice work! +Now you can take this knowledge, learn about [the rest of the `.gitlab-ci.yml` keywords](../yaml/index.md), +and build your own pipelines. |