Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/actions/facetsctl-register/action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: 'FacetsCloud CI Integration'
description: 'Register artifacts in FacetsCloud using facetsctl'
name: 'FacetsCloud CI Integration (deprecated)'
description: 'DEPRECATED - use Facets-cloud/github-actions/raptor-register instead. Registers artifacts using facetsctl v2.'
inputs:
docker_image:
description: 'Docker image URL in the external registry'
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@ in three modes, derived automatically from the triggering event:
This is the action the Facets control plane wires into bootstrapped modules
repositories, and the recommended CI for all modules repos.

## 2. Facets Module Preview & Security Scan (deprecated)
## 2. Facets Artifact Register (current)

Registers an already-pushed image against a Facets **artifact** — by environment, git
ref, or release stream — using the **`raptor`** CLI. It registers; it does not build or
push.

[raptor-register README](./raptor-register/README.md).

This replaces `facetsctl-register` below.

## 3. Facets Module Preview & Security Scan (deprecated)

> ⚠️ **Deprecated.** This action is based on the legacy `ftf` CLI and is kept only for
> existing workflows that still use it. It receives no new features. New repos should
Expand All @@ -39,3 +49,11 @@ security checks for Facets Terraform modules:
- **Terraform Validation**: Verifies the correctness of Terraform configurations.
- **Checkov Security Scanning**: Identifies security vulnerabilities in Terraform code.
- **Facets Module Preview**: Registers a Preview only module with your Facets Control Plane.

## 4. FacetsCloud CI Integration (deprecated)

> ⚠️ **Deprecated, and broken as written.** `facetsctl-register` runs facetsctl **v2**
> in a Docker action, and reads `secrets.*` from inside the action — not a context an
> action can read — so its credentials arrive empty. Use
> [**raptor-register**](./raptor-register/README.md); its README has the input-by-input
> mapping. It lives at `.github/actions/facetsctl-register/`.
118 changes: 118 additions & 0 deletions raptor-register/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Facets Artifact Register

Registers an already-pushed image against a Facets **artifact**, using the
[`raptor`](https://github.com/Facets-cloud/raptor-releases/releases) CLI.

It registers; it does not build or push. Build and push with whatever your pipeline
already uses, then hand the resulting URI to this action.

```yaml
- uses: Facets-cloud/github-actions/raptor-register@v1
with:
control_plane_url: ${{ secrets.FACETS_CP_URL }}
username: ${{ secrets.FACETS_USERNAME }}
token: ${{ secrets.FACETS_TOKEN }}
project: my-project
artifact: my-project-api
image: my.registry.example.com/my-project/api:${{ github.sha }}
git_ref: ${{ github.ref_name }}
registry: my-ecr
```

## Where the build lands

Exactly one of three inputs, and the choice is the whole behaviour:

| Input | What happens |
|---|---|
| `git_ref` | The branch is matched against the project's routing rules, and **they** decide the target. A branch that matches nothing leaves the build registered but unrouted. |
| `environment` | Registered straight to that environment. No rules consulted. |
| `release_stream` | Registered straight to that stream. No rules consulted. |

An artifact takes environments or release streams, never both — it is fixed when the
artifact is created. `raptor get artifacts -p <project>` shows which.

To see what a project's rules actually say, and where builds landed:

```bash
raptor get ci-cd -p <project> -a <artifact> # the branch mapping and promotion ladder
raptor get builds <artifact> -p <project> # what is registered; flags unrouted builds
```

## `registry` decides whether the resource can find the build

A resource asks for its image in one of two shapes. Read which one with
`raptor get resources -p PROJECT RESOURCE -o json`:

| shape | how it resolves | `registry` |
|---|---|---|
| `spec.release.image: ${blueprint.self.artifacts.NAME}` | by artifact name | not needed |
| `spec.release.build: {name: NAME, artifactory: REG}` | by registry, then name | **required, and it must be REG** |

The module resolves the second shape as `all_artifactories[REG][NAME]`. A build stored
under another registry is absent from that map, so the module falls back to the literal
string `NOT_FOUND` and deploys **that**. The pod then sits in `InvalidImageName`, and
nothing failed earlier to warn you. Read what a build carries with
`raptor get builds ARTIFACT -p PROJECT -o wide`.

## Which artifact?

Not always the resource name — the artifact is the CI integration a resource deploys
from, and one artifact is often shared by several resources across several projects.
Read it off the resource listing rather than guessing:

```bash
raptor get resources -p <project> -o wide # the ARTIFACT column
```

## Inputs

| Input | Required | Default | Description |
|---|---|---|---|
| `control_plane_url` | yes | | Control Plane URL, e.g. `https://your-org.console.facets.cloud` |
| `username` | yes | | Facets username |
| `token` | yes | | Facets API token — pass a secret, never a literal |
| `project` | yes | | Project (blueprint) the artifact belongs to |
| `artifact` | yes | | Artifact to register against |
| `image` | yes | | Full image URI including the tag; must already be pushed |
| `git_ref` | one of three | `""` | Register against this branch and let the rules place it |
| `environment` | one of three | `""` | Register directly against this environment |
| `release_stream` | one of three | `""` | Register directly against this release stream |
| `registry` | see above | `""` | Registry the image lives in |
| `external_id` | no | this run's id | CI reference recorded on the build |
| `raptor_version` | no | `latest` | `latest` or an exact tag, e.g. `v0.1.98`. `--registry` needs v0.1.98 or later |
| `raptor-download-url` | no | `""` | Exact binary URL; overrides `raptor_version` |

## Notes

- **The token is scoped to one step.** Auth is set as a step-level `env:` inside the
action, so it is not exported into the rest of the caller's job.
- **Pushing to a Facets registry?** Get the credentials and the repository to tag
against from raptor, in the step before this one — the control plane owns the
repository name, so it is not something to assemble by hand:

```bash
raptor get registry-credentials <registry> -p <project> -a <artifact> # docker login
REPO=$(raptor get registry-credentials <registry> -p <project> -a <artifact> --repository-uri)
```

- **Zip bundles** are not this action: `raptor set artifact-zip` uploads and registers
in one command, with no registry involved.

## Replaces `facetsctl-register`

[`facetsctl-register`](../.github/actions/facetsctl-register/action.yml) is deprecated. It runs
facetsctl **v2** in a Docker action and reads `secrets.*` from inside the action, which
is not a context an action can read — so its credentials arrive empty. Migrating:

| facetsctl-register | raptor-register |
|---|---|
| `docker_image` | `image` |
| `service` | `artifact` (read it from the ARTIFACT column, see above) |
| `blueprint_name` | `project` (now required) |
| `git_ref` | `git_ref` |
| `external_id` | `external_id` (optional; defaults to the run id) |
| `registration_type` | drop it — the target input you pass says which mode you are in |
| `registry` | `registry` — keep it. It is not decoration: see the note above |
| `description` | drop it — not carried on a build registration |
| implicit `secrets.FACETS_*` | explicit `control_plane_url` / `username` / `token` inputs |
121 changes: 121 additions & 0 deletions raptor-register/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: "Facets Artifact Register"
description: "Register a built image against a Facets artifact using the raptor CLI — by environment, git ref, or release stream."
author: "Facets"

inputs:
control_plane_url:
description: "Facets Control Plane URL, e.g. https://your-org.console.facets.cloud."
required: true
username:
description: "Facets username."
required: true
token:
description: "Facets API token. Pass a secret — never a literal."
required: true
project:
description: "Project (blueprint) the artifact belongs to."
required: true
artifact:
description: "Artifact to register the build against. This is the CI integration's name, which is NOT always the resource name — read it from the ARTIFACT column of 'raptor get resources -p <project> -o wide'."
required: true
image:
description: "Full image URI to register, including the tag. The image must already be pushed; this action registers it, it does not build or push."
required: true
git_ref:
description: "Register against this branch and let the project's routing rules decide the target. Exactly one of git_ref, environment or release_stream is required."
required: false
default: ""
environment:
description: "Register directly against this environment. Exactly one of git_ref, environment or release_stream is required."
required: false
default: ""
release_stream:
description: "Register directly against this release stream. Exactly one of git_ref, environment or release_stream is required."
required: false
default: ""
registry:
description: "Registry the image lives in. Required when the resource asks for its image as spec.release.build {name, artifactory}: the module resolves that shape as all_artifactories[registry][name], so a build registered without it lands under 'provided', the lookup misses, and the release deploys the literal string 'NOT_FOUND'. Not needed when the resource uses spec.release.image with an artifact expression."
required: false
default: ""
external_id:
description: "CI run reference recorded on the build — the only link from a deployed image back to the pipeline that produced it. Defaults to this workflow run's id."
required: false
default: ""
raptor_version:
description: "raptor CLI version to install: 'latest' or an exact release tag (e.g. v0.1.97). Ignored when raptor-download-url is set."
required: false
default: "latest"
raptor-download-url:
description: "Exact URL to download the raptor linux-amd64 binary from, bypassing the default Facets-cloud/raptor-releases location. When set, raptor_version is ignored. Escape hatch for testing/pre-release builds and enterprise mirrors."
required: false
default: ""

runs:
using: "composite"
steps:
- name: Install raptor CLI
shell: bash
run: |
set -euo pipefail
VERSION="${{ inputs.raptor_version }}"
DOWNLOAD_URL="${{ inputs.raptor-download-url }}"
if [ -n "$DOWNLOAD_URL" ]; then
# Explicit override: fetch the binary from exactly this URL (raptor_version ignored).
URL="$DOWNLOAD_URL"
elif [ -z "$VERSION" ] || [ "$VERSION" = "latest" ]; then
URL="https://github.com/Facets-cloud/raptor-releases/releases/latest/download/raptor-linux-amd64"
else
URL="https://github.com/Facets-cloud/raptor-releases/releases/download/${VERSION}/raptor-linux-amd64"
fi
echo "Installing raptor from ${URL}"
curl -fsSL -o raptor "$URL"
chmod +x raptor
sudo mv raptor /usr/local/bin/raptor

- name: Register the build
shell: bash
# raptor auth is set here, not exported job-wide, so the token stays out of
# every other step in the caller's job.
env:
CONTROL_PLANE_URL: ${{ inputs.control_plane_url }}
FACETS_USERNAME: ${{ inputs.username }}
FACETS_TOKEN: ${{ inputs.token }}
RAPTOR_NO_UPDATE_CHECK: "1"
PROJECT: ${{ inputs.project }}
ARTIFACT: ${{ inputs.artifact }}
IMAGE: ${{ inputs.image }}
GIT_REF: ${{ inputs.git_ref }}
ENVIRONMENT: ${{ inputs.environment }}
RELEASE_STREAM: ${{ inputs.release_stream }}
EXTERNAL_ID: ${{ inputs.external_id }}
REGISTRY: ${{ inputs.registry }}
RUN_ID: ${{ github.run_id }}
run: |
set -euo pipefail

# Exactly one target. Registering against two would be two different builds,
# and against none would be a build nothing can deploy.
TARGETS=0
[ -n "$GIT_REF" ] && TARGETS=$((TARGETS + 1)) || true
[ -n "$ENVIRONMENT" ] && TARGETS=$((TARGETS + 1)) || true
[ -n "$RELEASE_STREAM" ] && TARGETS=$((TARGETS + 1)) || true
if [ "$TARGETS" -ne 1 ]; then
echo "::error::Set exactly one of git_ref, environment or release_stream (got ${TARGETS})."
exit 1
fi

if [ -n "$GIT_REF" ]; then
set -- --git-ref "$GIT_REF"
elif [ -n "$ENVIRONMENT" ]; then
set -- -e "$ENVIRONMENT"
else
set -- --release-stream "$RELEASE_STREAM"
fi

if [ -n "$REGISTRY" ]; then
set -- "$@" --registry "$REGISTRY"
fi

raptor set artifact-uri -p "$PROJECT" "$@" "$ARTIFACT" \
--uri "$IMAGE" \
--external-id "${EXTERNAL_ID:-$RUN_ID}"