mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
feat(ci): pin releases to an immutable image set (#2327)
This commit is contained in:
@@ -190,17 +190,12 @@ jobs:
|
|||||||
|
|
||||||
- name: Advance moving image tags
|
- name: Advance moving image tags
|
||||||
env:
|
env:
|
||||||
IMAGE: ghcr.io/${{ env.GHCR_OWNER }}/${{ inputs.image }}
|
|
||||||
VERSION: ${{ needs.meta.outputs.build_version }}
|
|
||||||
MOVING_TAGS: ${{ inputs.moving-tags }}
|
MOVING_TAGS: ${{ inputs.moving-tags }}
|
||||||
run: |
|
VERSION: ${{ needs.meta.outputs.build_version }}
|
||||||
set -euo pipefail
|
run: >-
|
||||||
tag_args=()
|
tools/ci/run.sh image-set
|
||||||
IFS=',' read -ra moving <<< "${MOVING_TAGS}"
|
promote
|
||||||
for raw in "${moving[@]}"; do
|
--component "${{ inputs.image }}"
|
||||||
tag="$(echo "$raw" | xargs)"
|
--build-version "${VERSION}"
|
||||||
[ -n "$tag" ] && tag_args+=( "-t" "${IMAGE}:${tag}" )
|
--registry "ghcr.io/${{ env.GHCR_OWNER }}"
|
||||||
done
|
--moving-tags "${MOVING_TAGS}"
|
||||||
if (( ${#tag_args[@]} > 0 )); then
|
|
||||||
docker buildx imagetools create "${tag_args[@]}" "${IMAGE}:${VERSION}"
|
|
||||||
fi
|
|
||||||
|
|||||||
@@ -231,10 +231,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Advance moving image tags
|
- name: Advance moving image tags
|
||||||
env:
|
env:
|
||||||
IMAGE: ghcr.io/${{ env.GHCR_OWNER }}/fluxer-app-proxy-self-hosted
|
|
||||||
VERSION: ${{ needs.meta.outputs.build_version }}
|
VERSION: ${{ needs.meta.outputs.build_version }}
|
||||||
run: >-
|
run: >-
|
||||||
docker buildx imagetools create
|
tools/ci/run.sh image-set
|
||||||
-t "${IMAGE}:v1"
|
promote
|
||||||
-t "${IMAGE}:latest"
|
--component fluxer-app-proxy-self-hosted
|
||||||
"${IMAGE}:${VERSION}"
|
--build-version "${VERSION}"
|
||||||
|
--registry "ghcr.io/${{ env.GHCR_OWNER }}"
|
||||||
|
--moving-tags v1,latest
|
||||||
|
|||||||
@@ -280,10 +280,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Advance moving image tags
|
- name: Advance moving image tags
|
||||||
env:
|
env:
|
||||||
IMAGE: ghcr.io/${{ env.GHCR_OWNER }}/fluxer-app-proxy
|
|
||||||
VERSION: ${{ needs.meta.outputs.build_version }}
|
VERSION: ${{ needs.meta.outputs.build_version }}
|
||||||
run: >-
|
run: >-
|
||||||
docker buildx imagetools create
|
tools/ci/run.sh image-set
|
||||||
-t "${IMAGE}:v1"
|
promote
|
||||||
-t "${IMAGE}:latest"
|
--component fluxer-app-proxy
|
||||||
"${IMAGE}:${VERSION}"
|
--build-version "${VERSION}"
|
||||||
|
--registry "ghcr.io/${{ env.GHCR_OWNER }}"
|
||||||
|
--moving-tags v1,latest
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
name: release image set
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
build-version:
|
||||||
|
description: "Explicit Fluxer CalVer build version (YYYY.MDD.MICRO, UTC HHMMSS without leading zeroes) to use instead of automatic UTC clock allocation"
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
from-tag:
|
||||||
|
description: "Image tag every component is read from (v1 snapshots today's moving tags, a CalVer pins a coordinated build)"
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
default: "v1"
|
||||||
|
component-versions:
|
||||||
|
description: "Per-component overrides, one <image>=<version> entry per line (for example fluxer-api=2026.830.191141)"
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: write
|
||||||
|
packages: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: release-image-set
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
env:
|
||||||
|
GHCR_OWNER: ${{ github.repository_owner }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
approve:
|
||||||
|
name: approve image set release
|
||||||
|
permissions: {}
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
environment: builds
|
||||||
|
timeout-minutes: 5
|
||||||
|
steps:
|
||||||
|
- name: approved
|
||||||
|
run: echo "Image set release approved."
|
||||||
|
|
||||||
|
manifest:
|
||||||
|
name: resolve and publish the image set
|
||||||
|
needs: approve
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 20
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
|
||||||
|
env:
|
||||||
|
GIT_CONFIG_GLOBAL: ${{ runner.temp }}/gitconfig
|
||||||
|
- name: Set up Rust toolchain (CI helpers)
|
||||||
|
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9
|
||||||
|
with:
|
||||||
|
toolchain: "1.93.0"
|
||||||
|
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5
|
||||||
|
- uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ github.token }}
|
||||||
|
- name: Create token
|
||||||
|
id: create-token
|
||||||
|
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
|
||||||
|
with:
|
||||||
|
client-id: ${{ vars.FLUXER_CI_APP_ID }}
|
||||||
|
private-key: ${{ secrets.FLUXER_CI_APP_KEY }}
|
||||||
|
owner: fluxerapp
|
||||||
|
repositories: fluxer
|
||||||
|
permission-contents: write
|
||||||
|
permission-packages: read
|
||||||
|
|
||||||
|
- name: set variables
|
||||||
|
id: vars
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ steps.create-token.outputs.token }}
|
||||||
|
FLUXER_BUILD_VERSION: ${{ inputs['build-version'] }}
|
||||||
|
run: >-
|
||||||
|
tools/ci/run.sh resolve-calver
|
||||||
|
--github-output
|
||||||
|
|
||||||
|
- name: resolve release image set
|
||||||
|
id: resolve
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ steps.create-token.outputs.token }}
|
||||||
|
VERSION: ${{ steps.vars.outputs.build_version }}
|
||||||
|
FROM_TAG: ${{ inputs['from-tag'] }}
|
||||||
|
COMPONENT_VERSIONS: ${{ inputs['component-versions'] }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
args=(
|
||||||
|
image-set resolve
|
||||||
|
--version "${VERSION}"
|
||||||
|
--registry "ghcr.io/${GHCR_OWNER}"
|
||||||
|
--from-tag "${FROM_TAG}"
|
||||||
|
--out-dir release-out
|
||||||
|
--github-output
|
||||||
|
)
|
||||||
|
while IFS= read -r entry; do
|
||||||
|
entry="$(echo "$entry" | xargs)"
|
||||||
|
if [ -n "$entry" ]; then
|
||||||
|
args+=( --component-version "$entry" )
|
||||||
|
fi
|
||||||
|
done <<< "${COMPONENT_VERSIONS}"
|
||||||
|
tools/ci/run.sh "${args[@]}"
|
||||||
|
|
||||||
|
- name: verify release image set
|
||||||
|
env:
|
||||||
|
VERSION: ${{ steps.vars.outputs.build_version }}
|
||||||
|
run: >-
|
||||||
|
tools/ci/run.sh image-set verify
|
||||||
|
--manifest "release-out/fluxer-release-${VERSION}.json"
|
||||||
|
|
||||||
|
- name: Publish GitHub release
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ steps.create-token.outputs.token }}
|
||||||
|
VERSION: ${{ steps.vars.outputs.build_version }}
|
||||||
|
BUNDLE_COMMIT: ${{ steps.resolve.outputs.bundle_commit }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [ -z "${BUNDLE_COMMIT}" ]; then
|
||||||
|
echo "image-set resolve reported no bundle commit" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
gh release create "fluxer-release@${VERSION}" \
|
||||||
|
--repo fluxerapp/fluxer \
|
||||||
|
--target "${BUNDLE_COMMIT}" \
|
||||||
|
--title "fluxer-release ${VERSION}" \
|
||||||
|
--latest=true \
|
||||||
|
--notes "Immutable image set for ${VERSION}. Every image in the set contains ${BUNDLE_COMMIT}, the commit this tag points at, so the bundle here is never newer than the images. Pin with: docker compose -f docker-compose.yml -f fluxer-release-${VERSION}.yml up -d" \
|
||||||
|
"release-out/fluxer-release-${VERSION}.json" \
|
||||||
|
"release-out/fluxer-release-${VERSION}.yml"
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@ mod desktop;
|
|||||||
mod desktop_native;
|
mod desktop_native;
|
||||||
mod functions;
|
mod functions;
|
||||||
mod gateway;
|
mod gateway;
|
||||||
|
mod image_set;
|
||||||
mod release;
|
mod release;
|
||||||
mod schema;
|
mod schema;
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@ enum Command {
|
|||||||
Ci(ci_workflow::CiArgs),
|
Ci(ci_workflow::CiArgs),
|
||||||
CleanSchemaGeneratedFiles(schema::CleanSchemaGeneratedFilesArgs),
|
CleanSchemaGeneratedFiles(schema::CleanSchemaGeneratedFilesArgs),
|
||||||
Gateway(gateway::GatewayArgs),
|
Gateway(gateway::GatewayArgs),
|
||||||
|
ImageSet(image_set::ImageSetArgs),
|
||||||
Release(release::ReleaseArgs),
|
Release(release::ReleaseArgs),
|
||||||
ResolveCalver(calver::ResolveCalverArgs),
|
ResolveCalver(calver::ResolveCalverArgs),
|
||||||
}
|
}
|
||||||
@@ -54,6 +56,7 @@ pub async fn run() -> Result<()> {
|
|||||||
Command::Ci(args) => ci_workflow::run_ci(args).await,
|
Command::Ci(args) => ci_workflow::run_ci(args).await,
|
||||||
Command::CleanSchemaGeneratedFiles(args) => schema::run_clean_generated_files(args),
|
Command::CleanSchemaGeneratedFiles(args) => schema::run_clean_generated_files(args),
|
||||||
Command::Gateway(args) => gateway::run_gateway(args),
|
Command::Gateway(args) => gateway::run_gateway(args),
|
||||||
|
Command::ImageSet(args) => image_set::run(args),
|
||||||
Command::Release(args) => release::run(args).await,
|
Command::Release(args) => release::run(args).await,
|
||||||
Command::ResolveCalver(args) => calver::run(args),
|
Command::ResolveCalver(args) => calver::run(args),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use std::fs::{self, File};
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
const RELEASE_REPOSITORY: &str = "fluxerapp/fluxer";
|
pub(crate) const RELEASE_REPOSITORY: &str = "fluxerapp/fluxer";
|
||||||
const RELEASE_COMPARE_URL: &str = "https://github.com/fluxerapp/fluxer/compare";
|
const RELEASE_COMPARE_URL: &str = "https://github.com/fluxerapp/fluxer/compare";
|
||||||
pub(crate) const DESKTOP_RELEASE_DESCRIPTOR_SCHEMA_VERSION: u8 = 1;
|
pub(crate) const DESKTOP_RELEASE_DESCRIPTOR_SCHEMA_VERSION: u8 = 1;
|
||||||
pub(crate) const DESKTOP_RELEASE_ROUTE_COUNT: usize = 28;
|
pub(crate) const DESKTOP_RELEASE_ROUTE_COUNT: usize = 28;
|
||||||
@@ -502,7 +502,7 @@ fn validate_component(component: &str) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_full_sha(label: &str, value: &str) -> Result<String> {
|
pub(crate) fn validate_full_sha(label: &str, value: &str) -> Result<String> {
|
||||||
let value = value.trim();
|
let value = value.trim();
|
||||||
ensure!(
|
ensure!(
|
||||||
value.len() == 40 && value.bytes().all(|byte| byte.is_ascii_hexdigit()),
|
value.len() == 40 && value.bytes().all(|byte| byte.is_ascii_hexdigit()),
|
||||||
@@ -559,7 +559,7 @@ fn qualified_releases(
|
|||||||
Ok(qualified)
|
Ok(qualified)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_commit_sha(reference: &str) -> Result<String> {
|
pub(crate) fn resolve_commit_sha(reference: &str) -> Result<String> {
|
||||||
let sha = output_text(
|
let sha = output_text(
|
||||||
CommandSpec::new("gh")
|
CommandSpec::new("gh")
|
||||||
.arg("api")
|
.arg("api")
|
||||||
@@ -991,7 +991,7 @@ fn verify_release_assets(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn release_tag(component: &str, version: &str) -> String {
|
pub(crate) fn release_tag(component: &str, version: &str) -> String {
|
||||||
format!("{component}@{version}")
|
format!("{component}@{version}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user