Add example of push and sign OCI artifacts

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
This commit is contained in:
Stefan Prodan
2023-01-31 13:38:30 +02:00
parent 56807fddf6
commit 6681cd05a9
2 changed files with 40 additions and 31 deletions

View File

@ -134,18 +134,25 @@ jobs:
flux tag artifact $OCI_REPO:$(git rev-parse --short HEAD) --tag staging flux tag artifact $OCI_REPO:$(git rev-parse --short HEAD) --tag staging
``` ```
Example workflow for publishing Kubernetes manifests bundled as OCI artifacts to Docker Hub: ### Push and sign Kubernetes manifests to container registries
Example workflow for publishing Kubernetes manifests bundled as OCI artifacts
which are signed with Cosign and GitHub OIDC:
```yaml ```yaml
name: push-artifact-production name: push-sign-artifact
on: on:
push: push:
tags: branches:
- '*' - 'main'
permissions:
packages: write # needed for ghcr.io access
id-token: write # needed for keyless signing
env: env:
OCI_REPO: "oci://docker.io/my-org/app-config" OCI_REPO: "oci://ghcr.io/my-org/manifests/${{ github.event.repository.name }}"
jobs: jobs:
kubernetes: kubernetes:
@ -155,23 +162,24 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Setup Flux CLI - name: Setup Flux CLI
uses: fluxcd/flux2/action@main uses: fluxcd/flux2/action@main
- name: Login to Docker Hub - name: Setup Cosign
uses: sigstore/cosign-installer@main
- name: Login to GHCR
uses: docker/login-action@v2 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKER_USERNAME }} registry: ghcr.io
password: ${{ secrets.DOCKER_PASSWORD }} username: ${{ github.actor }}
- name: Generate manifests password: ${{ secrets.GITHUB_TOKEN }}
- name: Push and sign manifests
run: | run: |
kustomize build ./manifests/production > ./deploy/app.yaml digest_url=$(flux push artifact \
- name: Push manifests $OCI_REPO:$(git rev-parse --short HEAD) \
run: | --path="./manifests" \
flux push artifact $OCI_REPO:$(git tag --points-at HEAD) \ --source="$(git config --get remote.origin.url)" \
--path="./deploy" \ --revision="$(git branch --show-current)/$(git rev-parse HEAD)" |\
--source="$(git config --get remote.origin.url)" \ jq -r '. | .repository + "@" + .digest')
--revision="$(git tag --points-at HEAD)/$(git rev-parse HEAD)"
- name: Deploy manifests to production cosign sign $digest_url
run: |
flux tag artifact $OCI_REPO:$(git tag --points-at HEAD) --tag production
``` ```
### End-to-end testing ### End-to-end testing

View File

@ -43,13 +43,14 @@ The command can read the credentials from '~/.docker/config.json' but they can a
--source="$(git config --get remote.origin.url)" \ --source="$(git config --get remote.origin.url)" \
--revision="$(git branch --show-current)/$(git rev-parse HEAD)" --revision="$(git branch --show-current)/$(git rev-parse HEAD)"
# Push and sign artifact with cosgin # Push and sign artifact with cosign
digest_url = $(flux push artifact \ digest_url = $(flux push artifact \
oci://ghcr.io/org/config/app:$(git rev-parse --short HEAD) \ oci://ghcr.io/org/config/app:$(git rev-parse --short HEAD) \
--source="$(git config --get remote.origin.url)" \ --source="$(git config --get remote.origin.url)" \
--revision="$(git branch --show-current)/$(git rev-parse HEAD)" \ --revision="$(git branch --show-current)/$(git rev-parse HEAD)" \
--path="./path/to/local/manifest.yaml" | \ --path="./path/to/local/manifest.yaml" \
jq -r '. | .image + "@" + .digest') --output json | \
jq -r '. | .repository + "@" + .digest')
cosign sign $digest_url cosign sign $digest_url
# Push manifests passed into stdin to GHCR # Push manifests passed into stdin to GHCR
@ -116,7 +117,7 @@ func init() {
pushArtifactCmd.Flags().Var(&pushArtifactArgs.provider, "provider", pushArtifactArgs.provider.Description()) pushArtifactCmd.Flags().Var(&pushArtifactArgs.provider, "provider", pushArtifactArgs.provider.Description())
pushArtifactCmd.Flags().StringSliceVar(&pushArtifactArgs.ignorePaths, "ignore-paths", excludeOCI, "set paths to ignore in .gitignore format") pushArtifactCmd.Flags().StringSliceVar(&pushArtifactArgs.ignorePaths, "ignore-paths", excludeOCI, "set paths to ignore in .gitignore format")
pushArtifactCmd.Flags().StringVarP(&pushArtifactArgs.output, "output", "o", "", pushArtifactCmd.Flags().StringVarP(&pushArtifactArgs.output, "output", "o", "",
"the format in which the artifact digest should be printed. can be 'json' or 'yaml'") "the format in which the artifact digest should be printed, can be 'json' or 'yaml'")
pushCmd.AddCommand(pushArtifactCmd) pushCmd.AddCommand(pushArtifactCmd)
} }
@ -207,15 +208,15 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error {
} }
info := struct { info := struct {
URL string `json:"url"` URL string `json:"url"`
Image string `json:"image"` Repository string `json:"repository"`
Tag string `json:"tag"` Tag string `json:"tag"`
Digest string `json:"digest"` Digest string `json:"digest"`
}{ }{
URL: fmt.Sprintf("oci://%s", digestURL), URL: fmt.Sprintf("oci://%s", digestURL),
Image: fmt.Sprintf("%s/%s", digest.RegistryStr(), digest.RepositoryStr()), Repository: digest.Repository.Name(),
Tag: tag.TagStr(), Tag: tag.TagStr(),
Digest: digest.DigestStr(), Digest: digest.DigestStr(),
} }
switch pushArtifactArgs.output { switch pushArtifactArgs.output {