{"id":24104383,"url":"https://github.com/montana/manifest","last_synced_at":"2025-05-09T00:42:38.345Z","repository":{"id":45680745,"uuid":"321899631","full_name":"Montana/manifest","owner":"Montana","description":"Any registry or runtime that claims to have a certain Docker distribution image specification support will be interacting with the various manifest types to find out various things about the image. For Travis CI + IBM. Authored by Montana Mendy.","archived":false,"fork":false,"pushed_at":"2021-09-17T18:54:12.000Z","size":2783,"stargazers_count":33,"open_issues_count":2,"forks_count":8,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-09T00:42:30.818Z","etag":null,"topics":["architecture","bash","curl","curl-library","docker","docker-command","docker-image","dockerfile","ibm","ibm-power","ibm-z","json","linux","manifest","multiarch","ppc64le","python","quayio","s390x","travis-ci"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Montana.png","metadata":{"files":{"readme":"README.md","changelog":"history.png","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-12-16T07:12:44.000Z","updated_at":"2025-04-17T16:47:14.000Z","dependencies_parsed_at":"2022-09-19T05:20:57.568Z","dependency_job_id":null,"html_url":"https://github.com/Montana/manifest","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Montana%2Fmanifest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Montana%2Fmanifest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Montana%2Fmanifest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Montana%2Fmanifest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Montana","download_url":"https://codeload.github.com/Montana/manifest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253171210,"owners_count":21865280,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["architecture","bash","curl","curl-library","docker","docker-command","docker-image","dockerfile","ibm","ibm-power","ibm-z","json","linux","manifest","multiarch","ppc64le","python","quayio","s390x","travis-ci"],"created_at":"2025-01-10T19:59:53.212Z","updated_at":"2025-05-09T00:42:38.309Z","avatar_url":"https://github.com/Montana.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Header](header.png)\n\n[![Build Status](https://travis-ci.com/Montana/manifest.svg?branch=master)](https://travis-ci.com/Montana/manifest)\n\n# Docker Manifest with Travis CI \n\n_BEFORE_ you start reading, this guide is only for Travis CI and no other CI provider, also there might be some things you may want to check out first:\n\n---\n\n**NB**: All these scripts, mini programs, and obviously the `.travis.yml` file I've authored pass the Travis build individually, so if they break please revert your branch in GitHub by using `git revert HEAD`. You could also use `git reflog` as well, you _might also_ want to check `git log`, and see `travis whatsup`. \n\n---\n\n\n| Language | Filetype                                       | Travis Pass/Fail |\n| ------- | ---------------------------------------------- | ---------------- |\n| Bash    | [travis.sh](travis.sh)                         | Pass             |\n| YAML    | [.travis.yml](.travis.yml)                     | Pass             |\n| Bash    | [annotations.sh](annotations.sh)               | Pass             |\n| Python3 | [charcount.py](charcount.py)                   | Pass             |\n| Bash    | [crontest.sh](crontest.sh)                     | Pass             |\n| Bash    | [cron_table.sh](cron_table.sh)                 | Pass             |\n| Bash    | [ci.sh](ci.sh)                                 | Pass             |\n| Bash    | [caching.sh](caching.sh)                       | Pass             |\n| Bash    | [build.sh](build.sh)                           | Pass\n\n* My custom `travis.sh` bash script to set this env up: [travis.sh](travis.sh) \n* My custom `.travis.yml` for this use case: [.travis.yml](.travis.yml)\n* Heavily edited bash script I customized for this use case entiteld `annotations.sh`: [annotations.sh](annotations.sh)\n* The mini Python app I created to test within Travis in conjunction with `manifest` entitled `charcount.py`: [charcount.py](charcount.py)\n* My script that tests `crons`. The file as you can tell from the extension is in bash:  [crontest.sh](crontest.sh)\n* My cron table I made - if you choose to do randomized `cron` checks, or on different days of the week: [cron_table.sh](cron_table.sh)\n* My quick print in bash of `\"Hello Travis\"`. The file is called `ci.sh`, it's in bash -- if you just need a quick printout: [ci.sh](ci.sh)\n* My bash script on Docker `manifest` caching: [caching.sh](caching.sh)\n\n\u003e This is so you can get a better idea how this works after you setup your `env vars`, and ultimately inject your own 'components' like 'charcount' to have flexibility with the test use case I've setup here. The only script that will be being used (mandatory) is the `travis.sh` under the `script:` hook in the `.travis.yml` file. Everything else is optional. \n\nYou'll want to see this before you can move on which is [travis.sh](travis.sh) getting executed and passing the build:\n\n![Success](success.png)\n\n\u003e My script [travis.sh](travis.sh) passing in the Travis CI build, which means we are off to start seeing `manifests`.\n\n## Branches coming soon\n\nThe current stucture (in theory) will look a bit like this:\n\n```\ndat-manifest/master\n│   README.md\n│       \n│\n└───dat-manifest/main\n│   │   README.md\n│   │   Dockerfile.cross (PR)\n│   │\n│   └───dat-manifest/branch3\n│       │   README.md\n│       │   .travis.yml (PR)\n│       │   ...\n│   \n└───dat-manifest/scripts\n    │   cron_table.sh\n    │   travis.sh (PR)\n```\n\n## Getting started\n\n![Gif](https://github.com/Montana/manifest-unicorn/raw/master/manifest.gif)\n\u003e The first thing to do before looking at `manifests` is loginng into Docker through the Docker CLI.\n\nFirst thing, you'll need to set your `docker env vars`, you can do this once the repo is created, along with your `.travis.yml` and your `Dockerfile` you can login into Travis from the CLI via:\n\n```bash\ntravis login --pro --github-token (your github token) \n```\n\nThen just to make sure you're logged into the proper Travis account run: \n\n```bash\ntravis whatsup\n```\n\nIt's my own personal opinion, when pushing for `manifests` you should always commit with `-asm` so you're signing off on it so, instead of the regular `git commit -m \"whatever\"` you'd run `git commit -asm \"whatever\"`. In this particular example, I grabbed from the `DockerHub` the following packages: \n\n```bash\nlucashalbert/curl\nppc64le/node\ns390x/python  \nibmjava:jre \n```\n\nThese are the perfect packages (`cURL`), (`ppc64le`), (`s390x`), (`ibmjava:jre`), to show a multiarch docker image using `ppc64le`, `s390x`, and it's manifests.\n\n\n## Docker Manifest Layers\n\nDocker images have a bunch of layers. For each command in the `Dockerfile`, Docker generates a layer with all new files, for example `CMD`. So, each layer is a set of differences from the previous one, for example below is a `digest` of an React app I'm currently working on:\n\n![ReactLayers](layers.png)\n\n\u003e Showing the typical Docker `digest` and `sha256` affiliated with the React app in question.\n\n## Docker Manifest Caching \n\nDocker `manifest` caching is possible, you need to fetch the unique values - but the short end of this is the following:\n\n```bash\n#!/bin/bash\ndocker history -q IMAGE_HERE | grep -v missing \u0026\u0026 tar -xOf file.tar manifest.json | tr , '\\n' | grep -o '\"Config\":\".*\"' | awk -F ':' '{print $2}' | awk '{print substr($0,2,12)}'\n\n# Alternatively use, tar -xOf file.tar manifest.json | tr , '\\n' | grep -o '\"Config\":\".*\"' | awk -F ':' '{print $2}' | awk '{print substr($0,2,12)}'\n```\nThis outputs everything, and would suggest if you're into caching. \n\n## Breaking the `digest`\n\nWhen `ls` the folder, this is how the folder is structured, or should be:\n\n```\n├── 1/\n├── 2/\n├── 3/\n├── 4/\n├── config.json\n└── manifest.json\n```\n\nWe want to re-tar and reload `docker load`:\n\n```bash\ntar -cf new-a.tar -C a/ .\ndocker load -i new-a.tar\nLoaded image ID: sha256:24b...975\n```\n\nThe digest equals the `sha256` of the `config.json` file, it runs! \n\n## Check if image:tag combination already exists on DockerHub:\n\nFollow this bash script I edited that will have the image combination search function, either `result` or `null`:\n\n```bash\n#!/bin/bash\n\nfunc docker_image_tag_exists() {\n    EXISTS=$(curl -s https://hub.docker.com/v2/repositories/$1/tags/?page_size=10000 | jq -r \"[.results? | .[]? | .name == \\\"$2\\\"] | any\")\n    test ${EXISTS} = true\n}\n\nif docker_image_tag_exists $1 $2; then\n    echo \"true\" # enforce image tag to exist \nelse\n    echo \"false\" # enforce null fetch if image tag doesn't exist\nfi\n```\nThis will be a quicky way to check if a particularly Docker `image:tag` combination exists on DockerHub, so you can quickly make decisions and possibly `grep` image name combos, and get `manifests` quicker on certain items. \n\n\n### What are Docker Manifests, why do I need to use manifest? \n\nAny registry or runtime that claims to have a certain Docker distribution image specification (this can easily be checked) support will be interacting with the various `manifest` types to find out the following things inside a image:\n\n1. What are the _actual_ filesystem content, (layers) will be needed to build the root filesystem for the container.\n\n2. Any specific image config that is necessary to know how to run a container, some are more niche than others for using certain images. For example, information like what command(s) to run when starting the container (as probably represented in the Dockerfile that was used to build the image).\n\nIn short, The Docker container `manifest` is a file that contains data about a container image. Specifically `digest`, `sha256`, and most importantly `arch`. We can create a `docker manifest` which points to images for different architectures so that when using the image on a particular architecture Docker automatically pulls the desired image. \n\nThe main reason for `manifest` is to cross-build docker images.\n\n\n## Docker configuration files \n\nBy default, the Docker command line stores its configuration files in a directory called `.docker` within your `$HOME` directory, this can obviously be changed, but for now we are talking by standard practices. \n\nDocker manages most of the files in the configuration directory and you should **not modify** them. However, you can modify the `config.json` file to control certain aspects of how the docker command behaves, (e.g. flags, manifest, etc). \n\nYou can modify the docker command behavior using `environment variables`, you'll see me use the term `env vars` (it's the same meaning), or CLI options. You can also use options within `config.json` to modify some of the same exact behavior. If an `env var` and the --config flag are set, the flag takes precedent over the `env var`. CLI options override `env vars` and `env vars` override properties you specify in a `config.json` file. It's a bit of give and take. \n\nOverriding a single value in your `.env` file is reasonably simple: just set an `env var` with the same name in your shell before rerunning docker.\n\n### Change the `.docker` directory\n\nYou can close out docker, reopen terminal and run: \n\n```bash\ndocker --config ~/testconfigs/ ps\n```\n\nThis particular flag only applies to whatever command is being called. For persistent config, you can set the `DOCKER_CONFIG` `env var` in your shell (e.g. ~/.profile or ~/.bashrc)(Between `zsh`, `bashrc`). The example below sets the new directory to be `HOME/newdir/.docker.`:\n\n```bash\necho export DOCKER_CONFIG=$HOME/newdir/.docker \u003e ~/.profile\n```\n\n## Fat Manifest\n\nThe `manifest list` is the “fat manifest” which points to specific image manifests for one or various platforms or architectures. Its use is optional, and relatively few images will use one of these manifests. You can distinguish via the content-Type returned in a `HTTP` response usually.\n\n## Image Digest \n\nWhen inspecting a `digest` in a directory tree, it should look similar to this:\n\n![tree](tree.png)\n\n\u003e Docker's JSON config file describes the environment that built the docker image and its history through `manifest` and a good example is above, and the hash beginning with **5d1**.\n(here, `5d1cdcfd1c744987e4916f7815874391b29bff62e3df2d29885683e1b39e4c0a.json`).\n\n### Why I use cron jobs \n\nFor a build like this, the `cron` and at a services level, it can enable programmers to schedule tasks to run at a specific time in the future. You can think of it as setting a coffee maker. The at service specifies a one-time task that runs at a certain time. The `cron` service can schedule tasks on a repetitive basis and in rapid succession, such as daily, weekly, or monthly tasks that involve almost anything.\n\nYou'll see some of these `dcrons` around, you'll start why I _personally_ use them. They are _NOT_ for everybody. I made a table of the most common scheduler type crons. You can get a better idea on how to write them and implement them by looking at my [cron_table.sh](cron_table.sh): \n\n| Version          | Edit crontab | Remove crontab       | New crontab   | List cron-jobs |\n|------------------|--------------|----------------------|---------------|----------------|\n| dcron            | crontab -e   | crontab -d [user]    | crontab file  | crontab -l     |\n| fcron            | fcrontab -e  | fcrontab -r [user]   | fcrontab file | fcrontab -l    |\n| cronie and bcron | crontab -e   | crontab -r -u [user] | crontab file  | crontab -l     |\n\n\n### Sample docker `config.json` file\n\nFollowing is a sample `config.json` file:\n\n\n```json\n{\n  \"HttpHeaders\": {\n    \"MyHeader\": \"MyValue\"\n  },\n  \"psFormat\": \"table {{.ID}}\\\\t{{.Image}}\\\\t{{.Command}}\\\\t{{.Labels}}\",\n  \"imagesFormat\": \"table {{.ID}}\\\\t{{.Repository}}\\\\t{{.Tag}}\\\\t{{.CreatedAt}}\",\n  \"pluginsFormat\": \"table {{.ID}}\\t{{.Name}}\\t{{.Enabled}}\",\n  \"statsFormat\": \"table {{.Container}}\\t{{.CPUPerc}}\\t{{.MemUsage}}\",\n  \"servicesFormat\": \"table {{.ID}}\\t{{.Name}}\\t{{.Mode}}\",\n  \"secretFormat\": \"table {{.ID}}\\t{{.Name}}\\t{{.CreatedAt}}\\t{{.UpdatedAt}}\",\n  \"configFormat\": \"table {{.ID}}\\t{{.Name}}\\t{{.CreatedAt}}\\t{{.UpdatedAt}}\",\n  \"serviceInspectFormat\": \"pretty\",\n  \"nodesFormat\": \"table {{.ID}}\\t{{.Hostname}}\\t{{.Availability}}\",\n  \"detachKeys\": \"ctrl-e,e\",\n  \"credsStore\": \"secretservice\",\n  \"credHelpers\": {\n    \"awesomereg.example.org\": \"hip-star\",\n    \"unicorn.example.com\": \"vcbait\"\n  },\n  \"stackOrchestrator\": \"kubernetes\",\n  \"plugins\": {\n    \"plugin1\": {\n      \"option\": \"value\"\n    },\n    \"plugin2\": {\n      \"anotheroption\": \"anothervalue\",\n      \"athirdoption\": \"athirdvalue\"\n    }\n  },\n  \"proxies\": {\n    \"default\": {\n      \"httpProxy\":  \"http://user:pass@example.com:3128\",\n      \"httpsProxy\": \"http://user:pass@example.com:3128\",\n      \"noProxy\":    \"http://user:pass@example.com:3128\",\n      \"ftpProxy\":   \"http://user:pass@example.com:3128\"\n    },\n    \"https://manager1.mycorp.example.com:2377\": {\n      \"httpProxy\":  \"http://user:pass@example.com:3128\",\n      \"httpsProxy\": \"http://user:pass@example.com:3128\"\n    },\n  }\n}\n```\n\n### Using experimental CLI features \n\nI will show you other methods in doing this, but if you're going to do it through docker's `config.json`, look for the following:\n\n```json\n{\n  \"experimental\": \"enabled\",\n  \"debug\": true\n}\n```\nThis for example will make `manifestation` possible, when calling `docker manifest`. \n\n## Using manifest wthout installation\n\nIf you want to use `manifest` for simple query operations? For example, maybe you only want to query if a specific image:tag combination is a manifest list entry or not, something I call a 'if or that' query. If so, what platforms are listed in the manifest itself.\n\nYou can consume this feature of `manifest` without installing the binary as long as you are querying public (e.g. not private/authentication-requiring DockerHub registries, and secure ones not insecure ones). This can be done in a different way by another tool called `mquery`. Using `mquery` you can query the Docker image itself using these commands: \n\nYou can use mquery via a multi-platform image currently located on DockerHub as mplatform/mquery:latest. For example, you can query the mquery image itself with the following command:\n\n```bash\ndocker run --rm mplatform/mquery mplatform/mquery\nImage: mplatform/mquery\n * Manifest List: Yes\n * Supported platforms:\n   - linux/ppc64le\n   - linux/s390x\n   ```\n\nFor reference, the  `mquery` program in itself is a small Golang program that queries any functions and calls `manifest` while running via [OpenWhisk](https://openwhisk.apache.org/) from Apache in [IBM Cloud Functions](https://cloud.ibm.com/docs/openwhisk/index.html#getting-started-with-cloud-functions). \n\n## Working with insecure registries\n\nThe `manifest` command interacts solely with a Docker registry, and _solely_ a Docker registry. Thus, it has no way to query the engine for the list of allowed `insecure` registries. To allow the CLI to interact with an `insecure` registry, some `docker manifest` commands have an --insecure flag, and you'll see that we used the `--insecure` flag in our `.travis.yml` file for this long 'how-to'. For each transaction (e.g. create, which queries a registry, the `--insecure` flag must be specified.) If it's not, the latter will take precedent, and your build will error within Travis.\n\nThis flag tells the CLI that this registry call may ignore security concerns like missing or self-signed certificates using a command like:\n\n```bash\nln -s /etc/ssl/certs/ca-certificates.crt /etc/docker/certs.d/mydomain.com:5000/ca-certificates.crt\n```\n\n\u003e The `--insecure` flag is not required to annotate a `manifest` list on some occassions -- specifically when specified somewhere else (`.travis.yml`, `Dockerfile`), since annotations are to a locally-stored copy in general, and of course this is of a `manifest` list. You may also skip the `--insecure` flag if you are performing a `docker manifest` inspect on a locally-stored `manifest list`. Be sure to keep in mind that locally-stored `manifest lists` are never used by the engine on a docker pull. So if you run `docker pull` that `manifest` will not be used. \n\n\nLikewise, on a `docker manifest push` to an `--insecure` registry, the `--insecure` flag must be specified. If not, read what will happen above (the docker protocol heirarchy does its job). If this is not used with an `insecure` registry, the manifest command fails to find a registry that meets the default requirements, in turn will cause your Travis build to fail. \n\n## Manifest parent commands \n\nIn this case the `parent command` is the command that `inits` what you want to do with `docker manifest`. It gets a little more complex when `Dockerfile`. For example, the image’s parent image (in a `Dockerfile`) is the image designated in the `FROM` directive in the image’s Dockerfile. All subsequent commands are based on this parent image. A `Dockerfile` with the `FROM scratch` directive uses no parent image, as it creates mostly a base docker image, and example of this would be:\n\n```Dockerfile\nFROM scratch\nWORKDIR /root/\n# Copy the file from the build image\nCOPY --from=build /go/src/app .\nCMD [\"./app\"]\n```\nA table made, to graphically show you the definition of `parent command` for all you visual learners out there, I know I am:\n\n| Command | Description                          |\n|---------|--------------------------------------|\n| docker  | The base command for the Docker CLI. |\n\nThe `docker` command, will directly lead us into the next section on the proper usage of `docker manifest`. \n\n## Child commands in Docker as it pertains to `manifest` \n\nSometimes it's good to regroup, and put all command in one compact table, we can get lost when we are running `docker manifest` in variant. These tables will be great to go back and look at if you have questions about what a command does later. I've made one for the `child` commands for docker `manifest`:\n\n| Child commands           | Description                                                           |\n|--------------------------|-----------------------------------------------------------------------|\n| docker manifest annotate | Add additional information to a local image manifest                  |\n| docker manifest create   | Create a local manifest list for annotating and pushing to a registry |\n| docker manifest inspect  | Display an image manifest, or manifest list                           |\n| docker manifest push     | Push a manifest list to a repository                                  |\n\n## Test a docker registry for \"manifest list\" support\n\nThis script expects and _requires_ `env vars`. Please run the following: \n\n```bash\n./test-registry.sh r.myprivreg.com/somerepo\n```\nThis will give you the binary answer of yes/no if there is `manifest` support for that DockerHub repo.\n\n\n![Container Manifest](container-manifest.png) \n\u003e Flow chart explaining how my `.travis.yml` works.\n\n## Using Travis to display the Manifests \n\nThe following is the [.travis.yml](.travis.yml) file in the repo which also uses [travis.sh](travis.sh) bash script I wrote to complete some of the `env var` tasks, while pushing to the custom `.travis.yml` file I made for this use case.\n\n```yaml\n---\nlanguage: shell\nsudo: required\ndist: xenial\nos: linux\n\nservices:\n  - docker\n\naddons:\n  apt:\n    packages:\n      - docker-ce\n\nenv:\n  - DEPLOY=false repo=ibmjava:jre docker_archs=\"amd64 ppc64le s390x\"\n\ninstall:\n  - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes\n\nbefore_script:\n  - export ver=$(curl -s \"https://pkgs.alpinelinux.org/package/edge/main/x86_64/curl\" | grep -A3 Version | grep href | sed 's/\u003c[^\u003e]*\u003e//g' | tr -d \" \")\n  - export build_date=$(date -u +\"%Y-%m-%dT%H:%M:%SZ\")\n  - export vcs_ref=$(git rev-parse --short HEAD)\n\n  # Montana's crucial workaround\n  \nscript:\n  - chmod u+x ./travis.sh\n  - chmod u+x /build.sh\n  - export DOCKER_CLI_EXPERIMENTAL=enabled # crucial to use manifest\n\nafter_success:\n  - docker images\n  - docker manifest inspect --verbose lucashalbert/curl # multiarch build\n  - docker manifest inspect --insecure lucashalbert/curl # multiarch build \n  - docker manifest inspect --verbose ppc64le/node # IBM power build \n  - docker manifest inspect --insecure ppc64le/node # IBM power build \n  - docker manifest inspect --verbose s390x/python # IBM Z build \n  - docker manifest inspect --insecure s390x/python # IBM z build\n  - docker manifest inspect --verbose ibmjava:jre # official Docker IBM Java (Multiarch) build\n  - docker manifest inspect --insecure ibmjava:jre # official Docker IBM Java (Multiarch) build\n\nbranches:\n  only:\n    - master\n  except:\n    - /^*-v[0-9]/\n    - /^v\\d.*$/\n    \n    # .travis.yml created by Montana Mendy for Travis CI \u0026 IBM\n```\n\n## Setting up `env vars`\n\nYou can use your GitHub auth token, or just username/password, once logged in set the `env vars`: \n\n```bash\ntravis env set DOCKER_USERNAME username\ntravis env set DOCKER_PASSWORD pwd\n```\nYou should see this in your build at some point, this is reassurance your `env vars` got saved.\n\n![envvars](dockervars.png)\n\n\u003e Travis CI confirming that our `env vars` are safe and secure while it builds. \n\nFor some setting the `env vars` in the CLI is the best option, but for others using the Travis CI user interface is easier, and quicker. Click Settings -\u003e Environment Variables then add your `DOCKER` env vars. \n\n![UI](envvarui.png)\n\n\u003e We are using the UI to enter the Travis CI `env vars` (rather than the CLI). \n\nFor both cases, you'll also want to make sure you're logged into Docker, you can do this via: \n\n```bash\ndocker login \n``` \n![login](login.png)\n\n\u003e Login succeeded! This is crucial to have everything working properly, specifically getting the `docker manifests`. \n\nThat will now set both your Docker username/password as `env vars`. \n\n## Manifest\n\nOnce you've added `manifest`, it's crucial to add to your `.travis.yml`: \n\n```yaml\nscript: export DOCKER_CLI_EXPERIMENTAL=enabled\n```\nAlternatively you can run this in your project directory tree via: \n\n```bash\nexport DOCKER_CLI_EXPERIMENTAL=enabled\n```\n\nThe lines critical in the `.travis.yml` for the manifests to `print` are the following:\n\n```yaml\nafter_success:\n  - docker images\n  - docker manifest inspect --verbose lucashalbert/curl\n  - docker manifest inspect --insecure lucashalbert/curl\n  - docker manifest inspect --verbose ppc64le/node\n  - docker manifest inspect --insecure ppc64le/node\n  - docker manifest inspect --verbose s390x/\n  - docker manifest inspect --insecure s390x/python\n  - docker manifest inspect --verbose ibmjava:jre \n  - docker manifest inspect --insecure ibmjava:jre \n  ```\nYou want to use the `--verbose` and `--insecure` flags, to get as much `manifest` information as possible. This is true with any build. \n\nIn theory, this doesn't have to be `after_success:` but we want to make the most sense of the `.travis.yml` logs.  Let's see if in particular for example, we can't find the `s390x` manifest: \n\n![s390x](s390x.png)\n\n\u003e We are looking at the `s390x manifest`.\n\nOn the flip side, we can easily scroll through the `travis logs` and lookout for the `manifest` of `ppc64le`: \n\n![ppc64le](ppc64le.png) \n\n\u003e When the exact same `Dockerfile` is built again, a new `digest` in the `manifest` is created for that build, irrespective of the fact, that the same file is being built again and in the same environment. The only difference that makes these two different digests is the `container.key` and the `creation-timestamp`.\n\n## Create and push a `manifest list`\n\nIn order to spawn a `manifest list`, you first create the manifest list locally (localhost) by specifying the constituent images, (you can check them using `docker -ps -a`) if you would like to have included in your `manifest list`. Keep in mind that this is pushed to a docker registry, so if you want to push to a registry other than the docker registry, you need to create your manifest list with the registry name or IP and port. This is similar to tagging an image and pushing it to a foreign registry.\n\nAfter you have created your local copy of the `manifest list`, you may optionally annotate it. Annotations allowed are the architecture and operating system (e.g. `ppc64le`, `linux`) (overriding the image’s current values, and again this is from the heirarchy of docker protocols), `os features`, and an the `architecture variant` you're wanting to use:\n\n```bash\ndocker manifest create 0.0.0.0:5000/cool-ibm-test:v1 \\\n```\nThen you should see: \n\n```bash\n  00.00.00.00:5000/cool-ibm-test-ppc64le-linux:v1 \\\n  0.00.00.00:5000/cool-ibm-test-s390x-linux:v1 \\\n  0.00.00.00:5000/cool-ibm-test-amd64-linux:v1 \\\n  ```\nLastly, you need to `push` your `docker manifest` list to the desired registry. Below are descriptions of these three commands, and an example putting them all together:\n\n```bash\ndocker manifest annotate 00.00.00.00:5000/cool-ibm-test-linux:v1 \\ 00.00.00.00:5000/cool-ibm-test-linux:v1 \\ --arch ppc64le\n```\n \nThat's really it for `pushing` a `manifest`. There's also arbitrary flags, and the docker heirarch protocol, which are well explained in this document.\n\n![Manifest Ubuntu](ppc64leubuntu.png)\n\n\u003e Each layer of the `manifest` is comprised of a JSON file (which looks like the `.config` file we talked about earlier), a `VERSION` file with the string 1.0, and a `layer.tar` file containing the images files. In this particular case above, we are going to inspect `ppc64le/node` from DockerHub in my Ubuntu VM, using VMWare.\n\nYou might need to resolve some dependencies if doing `manifest` on Ubuntu, this is fairly easiy:\n\n```bash\nsudo apt install ruby-dev libffi-dev make gcc\nsudo gem install travis\n```\nThen make sure Travis is installed:\n\n```bash\nwhich travis\n``` \n\nThen you're good to go, now lets move on to pushing a manifest:\n\n```json\n\"schemaVersion\": 2,\n   \"mediaType\": \"application/vnd.docker.distribution.manifest.list.v2+json\",\n   \"manifests\": [\n      {\n         \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n         \"size\": 945,\n         \"digest\": \"sha256:2ab48cb5665bebc392e27628bb49397853ecb1472ecd5ee8151d5ff7ab86e68d\",\n         \"platform\": {\n            \"architecture\": \"amd64\",\n            \"os\": \"linux\"\n         }\n      },\n      {\n         \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n         \"size\": 1363,\n         \"digest\": \"sha256:956f5cf1146bb6bb33d047e1111c8e887d707dde373c9a650b308a8ea7b40fa7\",\n         \"platform\": {\n            \"architecture\": \"arm\",\n            \"os\": \"linux\",\n            \"variant\": \"v6\"\n         }\n      },\n      {\n         \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n         \"size\": 1363,\n         \"digest\": \"sha256:c6cc369f9824b7f6a19cca9d7f1789836528dd7096cdb4d1fc0922fd43af9d79\",\n         \"platform\": {\n            \"architecture\": \"arm\",\n            \"os\": \"linux\",\n            \"variant\": \"v7\"\n         }\n      },\n      {\n         \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n         \"size\": 1363,\n         \"digest\": \"sha256:b9ae5a5f88f9e4f35c5ad8f83fbb0705cf4a38208a4e40c932d7abd2e7b7c40b\",\n         \"platform\": {\n            \"architecture\": \"arm64\",\n            \"os\": \"linux\",\n            \"variant\": \"v8\"\n         }\n      },\n      {\n         \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n         \"size\": 1363,\n         \"digest\": \"sha256:4eca7b4f398526c8bf84be21f6c2c218119ed90a0ffa980dd4ba31ab50ca8cc5\",\n         \"platform\": {\n            \"architecture\": \"386\",\n            \"os\": \"linux\"\n         }\n      },\n      {\n         \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n         \"size\": 1363,\n         \"digest\": \"sha256:2239e5d3ee0e032514fe9c227c90cc5a1980a4c12602f683f4d0a647fb092797\",\n         \"platform\": {\n            \"architecture\": \"ppc64le\",\n            \"os\": \"linux\"\n         }\n      },\n      {\n         \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n         \"size\": 1363,\n         \"digest\": \"sha256:57523d3964bc9ee43ea5f644ad821838abd4ad1617eed34152ee361d538bfa3a\",\n         \"platform\": {\n            \"architecture\": \"s390x\",\n            \"os\": \"linux\"\n         }\n      }\n   ]\n}\nDone. Your build exited with 0.\n```\n\n## The `build.sh` script \n\n```bash\n#!/bin/bash -x\n\nfor docker_arch in ${docker_archs}; do\n    case ${docker_arch} in\n        ppc64le ) qemu_arch=\"ppc64le\" image_arch=\"ppc64le\" variant=\"\"   ;;\n        s390x   ) qemu_arch=\"s390x\"   image_arch=\"s390x\"   variant=\"\"   ;;\n    esac\n    cp Dockerfile.cross Dockerfile.${docker_arch}\n    sed -i \"s|__BASEIMAGE_ARCH__|${docker_arch}|g\" Dockerfile.${docker_arch}\n    sed -i \"s|__QEMU_ARCH__|${qemu_arch}|g\" Dockerfile.${docker_arch}\n    sed -i \"s|__CURL_VER__|${ver}|g\" Dockerfile.${docker_arch}\n    sed -i \"s|__BUILD_DATE__|${build_date}|g\" Dockerfile.${docker_arch}\n    sed -i \"s|__VCS_REF__|${vcs_ref}|g\" Dockerfile.${docker_arch}\n    if [ ${docker_arch} == 'amd64' ]; then\n        sed -i \"/__CROSS__/d\" Dockerfile.${docker_arch}\n        cp Dockerfile.${docker_arch} Dockerfile\n    else\n        sed -i \"s/__CROSS__//g\" Dockerfile.${docker_arch}\n    fi\n\n\n    # Check for qemu static bins\n    if [[ ! -f qemu-${qemu_arch}-static ]]; then\n        echo \"Downloading the qemu static binaries for ${docker_arch}\"\n        wget -q -N https://github.com/multiarch/qemu-user-static/releases/download/v4.0.0-4/x86_64_qemu-${qemu_arch}-static.tar.gz\n        tar -xvf x86_64_qemu-${qemu_arch}-static.tar.gz\n        rm x86_64_qemu-${qemu_arch}-static.tar.gz\n    fi\n\n    # Build image\n    docker build -f Dockerfile.${docker_arch} -t ${repo}:${docker_arch}-${ver} .\n   \n    # Check if build should be deployed\n    if [ \"$TRAVIS_BRANCH\" = \"master\" ] \u0026\u0026 [ \"${TRAVIS_PULL_REQUEST}\" = \"false\" ]; then \n        # Push image\n        docker push ${repo}:${docker_arch}-${ver}\n\n        # Create arch/ver docker manifest\n        DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create ${repo}:${docker_arch}-${ver} ${repo}:${docker_arch}-${ver}\n        \n        # Annotate arch/ver docker manifest\n        if [ ! -z \"${variant}\" ]; then\n            DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate ${repo}:${docker_arch}-${ver} ${repo}:${docker_arch}-${ver} --os linux --arch ${image_arch} --variant ${variant}\n        else\n            DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate ${repo}:${docker_arch}-${ver} ${repo}:${docker_arch}-${ver} --os linux --arch ${image_arch}\n        fi\n\n        # Generate Dynamic Manifest Image List\n        if [ -z \"${manifest_images}\" ]; then\n            manifest_images=\"${repo}:${docker_arch}-${ver}\"\n        else\n            manifest_images=\"${manifest_images} ${repo}:${docker_arch}-${ver}\"\n        fi\n    elif [ \"$DEPLOY\" = true ]; then\n        # Tag image with travis branch\n        docker tag ${repo}:${docker_arch}-${ver} ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH}\n\n        # Push tagged image\n        docker push ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH}\n\n        # Create tagged arch/ver docker manifest\n        DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH} ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH}\n        \n        # Annotate tagged arch/ver docker manifest\n        if [ ! -z \"${variant}\" ]; then\n            DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH} ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH} --os linux --arch ${image_arch} --variant ${variant}\n        else\n            DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH} ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH} --os linux --arch ${image_arch}\n        fi\n\n        # Generate Dynamic Manifest Image List\n        if [ -z \"${manifest_images}\" ]; then\n            manifest_images=\"${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH}\"\n        else\n            manifest_images=\"${manifest_images} ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH}\"\n        fi\n    else\n        echo \"Skipping image deployment... Not configured to deploy images/manifests to DockerHub\"\n    fi\ndone\n\n# Check if build should be deployed\nif [ \"$TRAVIS_BRANCH\" = \"master\" ] \u0026\u0026 [ \"${TRAVIS_PULL_REQUEST}\" = \"false\" ]; then \n    # Create version specific docker manifest\n    DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create ${repo}:${ver} ${manifest_images}\n\n    # Create latest version docker manifest\n    DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create ${repo}:latest ${manifest_images}\n\nelif [ \"$DEPLOY\" = true ]; then\n    # Create version specific docker manifest\n    DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create ${repo}:${ver}-${TRAVIS_BRANCH} ${manifest_images}\n\n    # Create latest version docker manifest\n    DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create ${repo}:latest-${TRAVIS_BRANCH} ${manifest_images}\n\nelse\n    echo \"Skipping version specific and latest tag docker manifest creation... Not configured to deploy images/manifests to DockerHub\"\nfi\n\nfor docker_arch in ${docker_archs}; do\n    case ${docker_arch} in\n        ppc64le ) qemu_arch=\"ppc64le\" image_arch=\"ppc64le\" variant=\"\"   ;;\n        s390x   ) qemu_arch=\"s390x\"   image_arch=\"s390x\"   variant=\"\"   ;;\n    esac\n\n    # Check if build should be deployed\n    if [ \"$TRAVIS_BRANCH\" = \"master\" ] \u0026\u0026 [ \"${TRAVIS_PULL_REQUEST}\" = \"false\" ]; then \n        # Annotate arch/ver docker manifest\n        if [ ! -z \"${variant}\" ]; then\n            # Annotate version specific docker manifest\n            DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate ${repo}:${ver} ${repo}:${docker_arch}-${ver} --os linux --arch ${image_arch} --variant ${variant}\n\n            # Annotate latest docker manifest\n            DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate ${repo}:latest ${repo}:${docker_arch}-${ver} --os linux --arch ${image_arch} --variant ${variant}\n\n        else\n            # Annotate version specific docker manifest\n            DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate ${repo}:${ver} ${repo}:${docker_arch}-${ver} --os linux --arch ${image_arch}\n\n            # Annotate latest docker manifest\n            DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate ${repo}:latest ${repo}:${docker_arch}-${ver} --os linux --arch ${image_arch}\n        fi\n\n        # Push arch/ver docker manifest\n        DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push ${repo}:${docker_arch}-${ver}\n    elif [ \"$DEPLOY\" = true ]; then\n        # Annotate arch/ver docker manifest\n        if [ ! -z \"${variant}\" ]; then\n            # Annotate version specific docker manifest\n            DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate ${repo}:${ver}-${TRAVIS_BRANCH} ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH} --os linux --arch ${image_arch} --variant ${variant}\n\n            # Annotate latest docker manifest\n            DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate ${repo}:latest-${TRAVIS_BRANCH} ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH} --os linux --arch ${image_arch} --variant ${variant}\n\n        else\n            # Annotate version specific docker manifest\n            DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate ${repo}:${ver}-${TRAVIS_BRANCH} ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH} --os linux --arch ${image_arch}\n\n            # Annotate latest docker manifest\n            DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate ${repo}:latest-${TRAVIS_BRANCH} ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH} --os linux --arch ${image_arch}\n        fi\n\n        # Push tagged arch/ver docker manifest\n        DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push ${repo}:${docker_arch}-${ver}-${TRAVIS_BRANCH}\n    else\n        echo \"Skipping version specific and latest tag docker manifest annotation... Not configured to deploy images/manifests to DockerHub\"\n    fi\ndone\n\n# Check if build should be deployed\nif [ \"$TRAVIS_BRANCH\" = \"master\" ] \u0026\u0026 [ \"${TRAVIS_PULL_REQUEST}\" = \"false\" ]; then\n    # Push version specific docker manifest\n    DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push ${repo}:${ver}\n\n    # Push latest docker manifest\n    DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push ${repo}:latest\nelif [ \"$DEPLOY\" = true ]; then\n    # Push version specific docker manifest\n    DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push ${repo}:${ver}-${TRAVIS_BRANCH}\n\n    # Push latest docker manifest\n    DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push ${repo}:latest-${TRAVIS_BRANCH}\nelse\n    echo \"Skipping version specific and latest tag docker manifest push... Not configured to deploy images/manifests to DockerHub\"\nfi\n```\n\n## Docker Manifest \n\nThe `docker manifest` command by itself performs no action, in theory it's `null`. In order to operate on a manifest or manifest list, one of the subcommands must be used.\n\nA single manifest is information about an image, such as `layers`, `size`, and `digest`. The docker manifest command also gives users additional information such as the `OS` and `arch` an image was built for:\n\n![Manifest](manifest.png)\n\n\u003e When I ran `docker manifest inspect`, usage manuals can be key -- but in this lengthy \"how-to\", there's no need for them. \n\n## Docker manifest inspect (verbose)\n\nWhen setting `docker manifest inspect` with a `verbose` flag, it's going to be showing you a bit more information. So I've displayed this in the following screenshot with the `manifests` highlighted. \n\n![Verbose](manifest_build.png)\n\n\u003e We are pulling from `lucashalbert/curl` for multiarch goodness. \n\nAlternatively you can `grep` the `manifests`. As you can see though, they are in the logs, with a passing build. Just to see how other images work, we will show you the `ppc64le/node` image `manifest`: \n\n![ppc64lenode](ppc64lenode.png)\n\n\u003e We are pulling from ppc64le/node in DockerHub.\n\nIn both cases, you get reliable results, pulling from various images you can expect the same thing. \n\n### Bash script using more of the manifest ecosystem\n\nThe below commands will come in more handy when you're writing a `bash` script. The following is a bash script I edited, so you can make sense of the ecosystem of other `docker manifest` commands:\n\n```bash\n#!/bin/bash\n\nset -o errexit\n\nmain() {\n  # arg 1 holds switch string\n  # arg 2 holds node version\n  # arg 3 holds tag suffix\n\n  case $1 in\n  \"prepare\")\n    docker_prepare\n    ;;\n  \"build\")\n    docker_build\n    ;;\n  \"test\")\n    docker_test\n    ;;\n  \"tag\")\n    docker_tag\n    ;;\n  \"push\")\n    docker_push\n    ;;\n  \"manifest-list-version\")\n    docker_manifest_list_version \"$2\" \"$3\"\n    ;;\n  \"manifest-list-test-beta-latest\")\n    docker_manifest_list_test_beta_latest \"$2\" \"$3\"\n    ;;\n  *)\n    echo \"none of above!\"\n    ;;\n  esac\n}\n\nfunction docker_prepare() {\n  # Prepare the machine before any code installation scripts\n  setup_dependencies\n\n  # Update docker configuration to enable docker manifest command\n  update_docker_configuration\n\n  # Prepare qemu to build images other then x86_64 on travis\n  prepare_qemu\n}\n\nfunction docker_build() {\n  # Build Docker image\n  echo \"DOCKER BUILD: Build Docker image.\"\n  echo \"DOCKER BUILD: arch - ${ARCH}.\"\n  echo \"DOCKER BUILD: build version -\u003e ${BUILD_VERSION}.\"\n  echo \"DOCKER BUILD: webtrees version -\u003e ${WT_VERSION}.\"\n  echo \"DOCKER BUILD: qemu arch - ${QEMU_ARCH}.\"\n  echo \"DOCKER BUILD: docker file - ${DOCKER_FILE}.\"\n\n  docker build --no-cache \\\n    --build-arg ARCH=${ARCH} \\\n    --build-arg BUILD_DATE=$(date +\"%Y-%m-%dT%H:%M:%SZ\") \\\n    --build-arg BUILD_VERSION=${BUILD_VERSION} \\\n    --build-arg BUILD_REF=${TRAVIS_COMMIT} \\\n    --build-arg WT_VERSION=${WT_VERSION} \\\n    --build-arg QEMU_ARCH=${QEMU_ARCH} \\\n    --file ./${DOCKER_FILE} \\\n    --tag ${TARGET}:build .\n}\n\nfunction docker_test() {\n  echo \"DOCKER TEST: Test Docker image.\"\n  echo \"DOCKER TEST: testing image -\u003e ${TARGET}:build\"\n\n  docker run -d --rm --name=testing ${TARGET}:build\n  if [ $? -ne 0 ]; then\n    echo \"DOCKER TEST: FAILED - Docker container testing failed to start.\"\n    exit 1\n  else\n    echo \"DOCKER TEST: PASSED - Docker container testing succeeded to start.\"\n  fi\n}\n\nfunction docker_tag() {\n  echo \"DOCKER TAG: Tag Docker image.\"\n\n  echo \"DOCKER TAG: tagging image - ${TARGET}:${BUILD_VERSION}-${ARCH}\"\n  docker tag ${TARGET}:build ${TARGET}:${BUILD_VERSION}-${ARCH}\n}\n\nfunction docker_push() {\n  echo \"DOCKER PUSH: Push Docker image.\"\n\n  echo \"DOCKER TAG: pushing image - ${TARGET}:${BUILD_VERSION}-${ARCH}\"\n  docker push ${TARGET}:${BUILD_VERSION}-${ARCH}\n}\n\nfunction docker_manifest_list_version() {\n\n  echo \"DOCKER MANIFEST: Create and Push docker manifest list - ${TARGET}:${BUILD_VERSION}.\"\n\n  docker manifest create ${TARGET}:${BUILD_VERSION} \\\n    ${TARGET}:${BUILD_VERSION}-amd64 \\\n    ${TARGET}:${BUILD_VERSION}-arm32v7 \\\n    ${TARGET}:${BUILD_VERSION}-arm64v8 \\\n    ${TARGET}:${BUILD_VERSION}-ppc64le \\\n    ${TARGET}:${BUILD_VERSION}-s390x\n\n  docker manifest annotate ${TARGET}:${BUILD_VERSION} ${TARGET}:${BUILD_VERSION}-arm32v7 --os=linux --arch=arm --variant=v7\n  docker manifest annotate ${TARGET}:${BUILD_VERSION} ${TARGET}:${BUILD_VERSION}-arm64v8 --os=linux --arch=arm64 --variant=v8\n  docker manifest annotate ${TARGET}:${BUILD_VERSION} ${TARGET}:${BUILD_VERSION}-ppc64le --os=linux --arch=ppc64le\n  docker manifest annotate ${TARGET}:${BUILD_VERSION} ${TARGET}:${BUILD_VERSION}-s390x --os=linux --arch=s390x\n  \n  docker manifest push ${TARGET}:${BUILD_VERSION}\n  \n  docker run --rm mplatform/mquery ${TARGET}:${BUILD_VERSION}\n}\n\nfunction docker_manifest_list_test_beta_latest() {\n\n  if [[ ${BUILD_VERSION} == *\"test\"* ]]; then\n    export TAG_PREFIX=\"test\";\n  elif [[ ${BUILD_VERSION} == *\"beta\"* ]]; then\n    export TAG_PREFIX=\"beta\";\n  else\n    export TAG_PREFIX=\"latest\";\n  fi\n\n  echo \"DOCKER MANIFEST: Create and Push docker manifest list - ${TARGET}:${TAG_PREFIX}.\"\n\n  docker manifest create ${TARGET}:${TAG_PREFIX} \\\n    ${TARGET}:${BUILD_VERSION}-amd64 \\\n    ${TARGET}:${BUILD_VERSION}-arm32v7 \\\n    ${TARGET}:${BUILD_VERSION}-arm64v8 \\\n    ${TARGET}:${BUILD_VERSION}-ppc64le \\\n    ${TARGET}:${BUILD_VERSION}-s390x\n\n  docker manifest annotate ${TARGET}:${TAG_PREFIX} ${TARGET}:${BUILD_VERSION}-arm32v7 --os=linux --arch=arm --variant=v7\n  docker manifest annotate ${TARGET}:${TAG_PREFIX} ${TARGET}:${BUILD_VERSION}-arm64v8 --os=linux --arch=arm64 --variant=v8\n  docker manifest annotate ${TARGET}:${TAG_PREFIX} ${TARGET}:${BUILD_VERSION}-ppc64le --os=linux --arch=ppc64le\n  docker manifest annotate ${TARGET}:${TAG_PREFIX} ${TARGET}:${BUILD_VERSION}-s390x --os=linux --arch=s390x\n\n  docker manifest push ${TARGET}:${TAG_PREFIX}\n  \n  docker run --rm mplatform/mquery ${TARGET}:${TAG_PREFIX}\n}\n\nfunction setup_dependencies() {\n  echo \"PREPARE: Setting up dependencies.\"\n  sudo apt update -y\n  sudo apt install --only-upgrade docker-ce -y\n}\n\nfunction update_docker_configuration() {\n  echo \"PREPARE: Updating docker configuration\"\n\n  mkdir $HOME/.docker\n\n  # enable experimental to use docker manifest command\n  echo '{\n    \"experimental\": \"enabled\"\n  }' | tee $HOME/.docker/config.json\n\n  # enable experimental\n  echo '{\n    \"experimental\": true,\n    \"storage-driver\": \"overlay2\",\n    \"max-concurrent-downloads\": 50,\n    \"max-concurrent-uploads\": 50\n  }' | sudo tee /etc/docker/daemon.json\n\n  sudo service docker restart\n}\n\nfunction prepare_qemu() {\n  echo \"PREPARE: Qemu\"\n  # Prepare qemu to build non amd64 / x86_64 images\n  docker run --rm --privileged multiarch/qemu-user-static:register --reset\n  mkdir tmp\n  pushd tmp \u0026\u0026\n    curl -L -o qemu-x86_64-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/$QEMU_VERSION/qemu-x86_64-static.tar.gz \u0026\u0026 tar xzf qemu-x86_64-static.tar.gz \u0026\u0026\n    curl -L -o qemu-arm-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/$QEMU_VERSION/qemu-arm-static.tar.gz \u0026\u0026 tar xzf qemu-arm-static.tar.gz \u0026\u0026\n    curl -L -o qemu-ppc64le-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/$QEMU_VERSION/qemu-ppc64le-static.tar.gz \u0026\u0026 tar xzf qemu-ppc64le-static.tar.gz \u0026\u0026\n    curl -L -o qemu-s390x-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/$QEMU_VERSION/qemu-s390x-static.tar.gz \u0026\u0026 tar xzf qemu-s390x-static.tar.gz \u0026\u0026\n    curl -L -o qemu-aarch64-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/$QEMU_VERSION/qemu-aarch64-static.tar.gz \u0026\u0026 tar xzf qemu-aarch64-static.tar.gz \u0026\u0026\n    popd\n}\n\nmain \"$1\" \"$2\" \"$3\"\n```\n\u003e Bash script I edited heavily that shows off qemu, more `docker manifest` options and along with multiarch builds. \n\n### Other commands\n\n| Command                  | Description                                                           |\n|--------------------------|-----------------------------------------------------------------------|\n| docker manifest annotate | Add additional information to a local image manifest                  |\n| docker manifest create   | Create a local manifest list for annotating and pushing to a registry |\n| docker manifest inspect  | Display an image manifest, or manifest list                           |\n| docker manifest push     | Push a manifest list to a repository                                  |\n\n\nReminder the commands on the left are all `parent commands` and have `flags` that can be attached to them for different behaviors/functions.\n\n## Annotations \n\nAnnotations are allowed in docker for the reason of defining architecture and operating system (overriding the image’s current values), os features, and an architecture variant, this takes precedent over `env vars` in the docker protocol heirarchy. An example of using `annotation` would look something like:\n\n```bash\ndocker manifest annotate 00.00.00.000:5000/cool-ibm-test:v1 00.00.00.00:5000/cool-ibm-test --arch ppc64le, s390x\n```\n\nIn this example, the only `archs` that are going to be building is: \n\n```bash\nppc64le\ns390x\n```\nSo for example, you'd run:\n\n```bash\ndocker manifest inspect cool-ibm-test:v1\n ```\nThen this is what the output you'd expect to see, `s390x` along with `ppc64le` for architectures. You can also see `os`, `digest` and of course the `sha256` hash.\n\n## Inspect a manifest list (`cool-ibm-test`) \n\n```json\n{\n         \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n         \"size\": 425,\n         \"digest\": \"sha256:df436846483aff62bad830b730a0d3b77731bcf98ba5e470a8bbb8e9e346e4e8\",\n         \"platform\": {\n            \"architecture\": \"ppc64le\",\n            \"os\": \"linux\"\n         }\n      },\n      {\n         \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n         \"size\": 425,\n         \"digest\": \"sha256:5bb8e50aa2edd408bdf3ddf61efb7338ff34a07b762992c9432f1c02fc0e5e62\",\n         \"platform\": {\n            \"architecture\": \"s390x\",\n            \"os\": \"linux\"\n         }\n      }\n   ]\n}\n```\n\nPhew, that was a lot! This should now give you the opportunity to mix and match `docker manifest` with Travis. I haven't seen much on this on GitHub or really anywhere. So it's really my pleasure to share my custom `.travis.yml` file, and show you what works, and I left all my history open -- so you can see where things didn't go so smoothly! I hope you enjoyed the read.\n\nThe Docker container `manifest` is a file that contains data about a container image. Specifically `digest`, `sha256`, and of course `arch`. We can create a `manifest` which points to images for different architectures so that when using the image on a particular architecture Docker automatically pulls the desired image. The reason for `manifest` is to mainly cross-build docker images.\n\n```export DOCKER_CLI_EXPERIMENTAL=enabled # crucial for manifest to work \n\ndocker manifest create IBM/ibm-image:latest \\\n            IBM/ibm-image:latest-${PLATFORM_1} \\ # arch s390x (one can assume)\n            IBM/ibm-image:latest-${PLATFORM_2} \\ # arch ppc64le (one can assume)\n            \ndocker manifest annotate IBM/ibm-image:latest IBM/ibm-image:latest-${PLATFORM_1} --arch ${PLATFORM_1} # arch s390x (one can assume) \ndocker manifest annotate IBM/ibm-image:latest IBM/ibm-image:latest-${PLATFORM_2} --arch ${PLATFORM_2} # arch ppc64le (one can assume)\n\ndocker manifest push IBM/ibm-image:latest\n```\n\u003e How `manifest` is used. You first enable experimental CLI functions, create your image, push, then as you can see two archs - `s390x`, and `ppc64le`. \n\n\n![History](history.png)\n\n### Author(s)\nMontana Mendy - [Montana](https://github.com/Montana) (Rails Engineer/DevRel @ Travis CI) \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmontana%2Fmanifest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmontana%2Fmanifest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmontana%2Fmanifest/lists"}