Compare commits
4 commits
cc4423b604
...
e0a81b0b26
Author | SHA1 | Date | |
---|---|---|---|
e0a81b0b26 | |||
1103d6b4ab | |||
7b9be1d1b3 | |||
3c94e2d45f |
7 changed files with 389 additions and 188 deletions
64
apps/ci-os/packages/cache/default.nix
vendored
Normal file
64
apps/ci-os/packages/cache/default.nix
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
{ pkgs, lib, ... }:
|
||||||
|
|
||||||
|
with pkgs;
|
||||||
|
|
||||||
|
writeShellApplication rec {
|
||||||
|
name = "attic-action";
|
||||||
|
|
||||||
|
runtimeInputs = [ attic-client ];
|
||||||
|
|
||||||
|
# Define the shell script
|
||||||
|
text = ''
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
ATTIC_ENDPOINT="${ATTIC_ENDPOINT:-https://cache.252.no}"
|
||||||
|
ATTIC_CACHE="${ATTIC_CACHE:-nix-gitops}"
|
||||||
|
ATTIC_TOKEN="${ATTIC_TOKEN:-}"
|
||||||
|
SKIP_PUSH="${SKIP_PUSH:-false}"
|
||||||
|
SKIP_USE="${SKIP_USE:-false}"
|
||||||
|
|
||||||
|
# Ensure required inputs are provided
|
||||||
|
if [[ -z "$ATTIC_ENDPOINT" || -z "$ATTIC_CACHE" ]]; then
|
||||||
|
echo "Error: Both ATTIC_ENDPOINT and ATTIC_CACHE are required."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$ATTIC_TOKEN" ]]; then
|
||||||
|
echo "Warning: ATTIC_TOKEN is not set. Operations requiring authentication might fail."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add the cache as a substituter unless SKIP_USE is true
|
||||||
|
if [[ "$SKIP_USE" != "true" ]]; then
|
||||||
|
echo "Adding $ATTIC_ENDPOINT as a substituter..."
|
||||||
|
attic use --endpoint "$ATTIC_ENDPOINT" "$ATTIC_CACHE" || {
|
||||||
|
echo "Error: Failed to add substituter."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo "Skipping use of cache as substituter."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Push to the cache unless SKIP_PUSH is true
|
||||||
|
if [[ "$SKIP_PUSH" != "true" ]]; then
|
||||||
|
echo "Pushing results to $ATTIC_ENDPOINT/$ATTIC_CACHE..."
|
||||||
|
attic push --endpoint "$ATTIC_ENDPOINT" "$ATTIC_CACHE" || {
|
||||||
|
echo "Error: Failed to push to cache."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo "Skipping cache push."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Attic action completed successfully."
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Metadata for the package
|
||||||
|
meta = with lib; {
|
||||||
|
homepage = "https://code.252.no/tommy/containers";
|
||||||
|
description = "Binary cache server action script for CI pipelines";
|
||||||
|
license = licenses.mit;
|
||||||
|
maintainers = with maintainers; [ "tommy-skaug" ];
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
{ pkgs, lib, gitSvPkg, ... }:
|
{ pkgs, lib, gitSvPkg, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
fluxLocal = import ./flux-local { inherit pkgs lib; };
|
fluxLocal = import ./flux-local { inherit lib pkgs; };
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
flux-local = fluxLocal;
|
flux-local = fluxLocal;
|
||||||
flux-diff = import ./flux-diff { inherit pkgs lib fluxLocal; };
|
flux-diff = import ./flux-diff { inherit lib pkgs fluxLocal; };
|
||||||
merge-diff = import ./merge-diff { inherit pkgs lib gitSvPkg; };
|
merge-diff = import ./merge-diff { inherit lib pkgs gitSvPkg; };
|
||||||
forgejo-label = import ./forgejo-label { inherit lib pkgs; };
|
forgejo-label = import ./forgejo-label { inherit lib pkgs; };
|
||||||
forgejo-comment = import ./forgejo-comment { inherit lib pkgs; };
|
forgejo-comment = import ./forgejo-comment { inherit lib pkgs; };
|
||||||
forgejo-release = import ./forgejo-release { inherit lib pkgs; };
|
forgejo-release = import ./forgejo-release { inherit lib pkgs; };
|
||||||
|
|
|
@ -11,80 +11,112 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func getEnv(key, defaultVal string) string {
|
||||||
|
if value, exists := os.LookupEnv(key); exists {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return defaultVal
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Define command-line flags
|
// Define command-line flags with environment variable defaults
|
||||||
var forgejoAPIURL string
|
var forgejoAPIURL string
|
||||||
|
flag.StringVar(&forgejoAPIURL, "forgejo-api-url", getEnv("FORGEJO_API_URL", "https://code.252.no/api/v1"), "Forgejo API URL (default: https://code.252.no/api/v1 or $FORGEJO_API_URL)")
|
||||||
|
|
||||||
var token string
|
var token string
|
||||||
|
flag.StringVar(&token, "token", os.Getenv("FORGEJO_TOKEN"), "Forgejo API token (required, or set $FORGEJO_TOKEN)")
|
||||||
|
|
||||||
var repoOwner string
|
var repoOwner string
|
||||||
|
flag.StringVar(&repoOwner, "repo-owner", os.Getenv("REPO_OWNER"), "Repository owner (required, or set $REPO_OWNER)")
|
||||||
|
|
||||||
var repoName string
|
var repoName string
|
||||||
|
flag.StringVar(&repoName, "repo-name", os.Getenv("REPO_NAME"), "Repository name (required, or set $REPO_NAME)")
|
||||||
|
|
||||||
var issueId int
|
var issueId int
|
||||||
|
issueIdStr := os.Getenv("ISSUE_ID")
|
||||||
flag.StringVar(&forgejoAPIURL, "forgejo-api-url", "https://code.252.no/api/v1", "Forgejo API URL")
|
if issueIdStr != "" {
|
||||||
flag.StringVar(&token, "token", "", "Forgejo API token")
|
|
||||||
flag.StringVar(&repoOwner, "repo-owner", "", "Repository owner")
|
|
||||||
flag.StringVar(&repoName, "repo-name", "", "Repository name")
|
|
||||||
flag.IntVar(&issueId, "issue-id", 0, "Issue id")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
// Get the diff file from the positional arguments
|
|
||||||
args := flag.Args()
|
|
||||||
if len(args) < 1 {
|
|
||||||
fmt.Println("Usage: forgejo-comment <diff-file>")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
diffFile := args[0]
|
|
||||||
|
|
||||||
// Read environment variables if flags are not set
|
|
||||||
if forgejoAPIURL == "" {
|
|
||||||
forgejoAPIURL = os.Getenv("FORGEJO_API_URL")
|
|
||||||
if forgejoAPIURL == "" {
|
|
||||||
fmt.Println("Error: FORGEJO_API_URL is not set")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if token == "" {
|
|
||||||
token = os.Getenv("FORGEJO_TOKEN")
|
|
||||||
if token == "" {
|
|
||||||
fmt.Println("Error: FORGEJO_TOKEN is not set")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if repoOwner == "" {
|
|
||||||
repoOwner = os.Getenv("REPO_OWNER")
|
|
||||||
if repoOwner == "" {
|
|
||||||
fmt.Println("Error: REPO_OWNER is not set")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if repoName == "" {
|
|
||||||
repoName = os.Getenv("REPO_NAME")
|
|
||||||
if repoName == "" {
|
|
||||||
fmt.Println("Error: REPO_NAME is not set")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if issueId == 0 {
|
|
||||||
issueIdEnv := os.Getenv("ISSUE_ID")
|
|
||||||
if issueIdEnv == "" {
|
|
||||||
fmt.Println("Error: ISSUE_ID is not set")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
var err error
|
var err error
|
||||||
issueId, err = strconv.Atoi(issueIdEnv)
|
issueId, err = strconv.Atoi(issueIdStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error: Invalid ISSUE_ID")
|
fmt.Println("Error: Invalid ISSUE_ID")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
flag.IntVar(&issueId, "issue-id", issueId, "Issue ID to comment on (cannot be used with --pull-id, or set $ISSUE_ID)")
|
||||||
|
|
||||||
|
var pullId int
|
||||||
|
pullIdStr := os.Getenv("PULL_ID")
|
||||||
|
if pullIdStr != "" {
|
||||||
|
var err error
|
||||||
|
pullId, err = strconv.Atoi(pullIdStr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error: Invalid PULL_ID")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flag.IntVar(&pullId, "pull-id", pullId, "Pull Request ID to comment on (cannot be used with --issue-id, or set $PULL_ID)")
|
||||||
|
|
||||||
|
// Override the default usage message to be more helpful
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] <diff-file>\n", os.Args[0])
|
||||||
|
fmt.Fprintln(flag.CommandLine.Output(), "\nOptions:")
|
||||||
|
flag.PrintDefaults()
|
||||||
|
fmt.Fprintln(flag.CommandLine.Output(), "\nExample:")
|
||||||
|
fmt.Fprintf(flag.CommandLine.Output(), " %s --pull-id=123 --token=YOUR_TOKEN --repo-owner=owner --repo-name=repo diff.txt\n", os.Args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Get the diff file from the positional arguments
|
||||||
|
args := flag.Args()
|
||||||
|
if len(args) < 1 {
|
||||||
|
fmt.Println("Error: Missing diff file.")
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
diffFile := args[0]
|
||||||
|
|
||||||
|
// Check for required parameters
|
||||||
|
if token == "" {
|
||||||
|
fmt.Println("Error: Forgejo API token is not set. Use the --token flag or set FORGEJO_TOKEN environment variable.")
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if repoOwner == "" {
|
||||||
|
fmt.Println("Error: Repository owner is not set. Use the --repo-owner flag or set REPO_OWNER environment variable.")
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if repoName == "" {
|
||||||
|
fmt.Println("Error: Repository name is not set. Use the --repo-name flag or set REPO_NAME environment variable.")
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that only one of issueId or pullId is set
|
||||||
|
if (issueId == 0 && pullId == 0) || (issueId != 0 && pullId != 0) {
|
||||||
|
fmt.Println("Error: You must specify either --issue-id or --pull-id, but not both.")
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine which ID to use
|
||||||
|
var itemId int
|
||||||
|
var itemType string
|
||||||
|
if issueId != 0 {
|
||||||
|
itemId = issueId
|
||||||
|
itemType = "issue"
|
||||||
|
} else {
|
||||||
|
itemId = pullId
|
||||||
|
itemType = "pull"
|
||||||
|
}
|
||||||
|
|
||||||
// Read the diff file
|
// Read the diff file
|
||||||
diffContentBytes, err := os.ReadFile(diffFile)
|
diffContentBytes, err := os.ReadFile(diffFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error reading diff file: %v\n", err)
|
fmt.Printf("Error reading diff file '%s': %v\n", diffFile, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +135,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the API URL
|
// Construct the API URL
|
||||||
apiURL := fmt.Sprintf("%s/repos/%s/%s/issues/%d/comments", forgejoAPIURL, repoOwner, repoName, issueId)
|
apiURL := fmt.Sprintf("%s/repos/%s/%s/issues/%d/comments", forgejoAPIURL, repoOwner, repoName, itemId)
|
||||||
|
|
||||||
// Create the HTTP request
|
// Create the HTTP request
|
||||||
req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(payloadBytes))
|
req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(payloadBytes))
|
||||||
|
@ -137,6 +169,6 @@ func main() {
|
||||||
fmt.Printf("Response body: %s\n", string(respBody))
|
fmt.Printf("Response body: %s\n", string(respBody))
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Posted diff to Forgejo issue #%d\n", issueId)
|
fmt.Printf("Posted diff to Forgejo %s #%d\n", itemType, itemId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,131 +1,15 @@
|
||||||
{ pkgs, lib, gitSvPkg, ... }:
|
{ lib, pkgs, gitSvPkg }:
|
||||||
|
|
||||||
with pkgs;
|
pkgs.buildGoModule rec {
|
||||||
|
pname = "merge-diff";
|
||||||
|
version = "2.0.0";
|
||||||
|
|
||||||
writeShellApplication rec {
|
buildInputs = with pkgs; [ gitFull jq coreutils gnugrep gawk gitSvPkg ];
|
||||||
name = "merge-diff";
|
|
||||||
|
|
||||||
runtimeInputs = [ gitFull jq coreutils gnugrep gawk gitSvPkg ];
|
src = ./src;
|
||||||
|
|
||||||
# Define the shell script
|
# Vendor dependencies for reproducibility
|
||||||
text = ''
|
vendorHash = null;
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Function to display usage
|
subPackages = [ "." ];
|
||||||
usage() {
|
|
||||||
echo "Usage: $0 -p <path_pattern> -d <directory depth>"
|
|
||||||
echo " -p, --pattern Path pattern to filter changed files (can be specified multiple times)"
|
|
||||||
echo " -d, --depth Depth of changeset (default is 2)"
|
|
||||||
echo " -h, --help Display this help message"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Parse command-line arguments
|
|
||||||
PATTERNS=()
|
|
||||||
DEPTH=2
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case "$1" in
|
|
||||||
-p|--pattern)
|
|
||||||
PATTERNS+=("$2")
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-d|--depth)
|
|
||||||
DEPTH="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-h|--help)
|
|
||||||
usage
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unknown option: $1"
|
|
||||||
usage
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ ''${#PATTERNS[@]} -eq 0 ]; then
|
|
||||||
echo "Error: At least one path pattern must be specified."
|
|
||||||
usage
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Get the current commit SHA
|
|
||||||
CURRENT_SHA=$(git rev-parse HEAD)
|
|
||||||
|
|
||||||
# Get parent commits
|
|
||||||
PARENT_COUNT=$(git rev-list --parents -n 1 "$CURRENT_SHA" | wc -w)
|
|
||||||
|
|
||||||
if [ "$PARENT_COUNT" -gt 2 ]; then
|
|
||||||
echo "This is a merge commit with $((PARENT_COUNT - 1)) parents."
|
|
||||||
# For simplicity, comparing with the first parent
|
|
||||||
PARENT_SHA=$(git rev-parse HEAD^1)
|
|
||||||
elif [ "$PARENT_COUNT" -eq 2 ]; then
|
|
||||||
# Regular commit with one parent
|
|
||||||
PARENT_SHA=$(git rev-parse HEAD^1)
|
|
||||||
else
|
|
||||||
echo "Single commit with no parents. Listing all changes."
|
|
||||||
PARENT_SHA=HEAD~1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# List changed files between parent and current commit
|
|
||||||
CHANGED_FILES=$(git diff --name-only "$PARENT_SHA" "$CURRENT_SHA")
|
|
||||||
|
|
||||||
#echo "Changed files:"
|
|
||||||
#echo "$CHANGED_FILES"
|
|
||||||
|
|
||||||
# Initialize an empty array for filtered changes
|
|
||||||
FILTERED_CHANGES=()
|
|
||||||
|
|
||||||
# Iterate over each pattern and filter changed files
|
|
||||||
for pattern in "''${PATTERNS[@]}"; do
|
|
||||||
while IFS= read -r file; do
|
|
||||||
if [[ "$file" =~ $pattern ]]; then
|
|
||||||
file=$(echo "$file"| cut -d/ -f1-"$DEPTH")
|
|
||||||
FILTERED_CHANGES+=("$file")
|
|
||||||
fi
|
|
||||||
done <<< "$CHANGED_FILES"
|
|
||||||
done
|
|
||||||
|
|
||||||
# Remove duplicates
|
|
||||||
UNIQUE_FILTERED_CHANGES=$(printf "%s\n" "''${FILTERED_CHANGES[@]}" | sort -u)
|
|
||||||
|
|
||||||
#echo "Filtered changed files:"
|
|
||||||
#echo "$UNIQUE_FILTERED_CHANGES"
|
|
||||||
|
|
||||||
|
|
||||||
# Determine semantic version bump using git-sv
|
|
||||||
# Ensure that git-sv is available
|
|
||||||
if ! command -v git-sv &> /dev/null; then
|
|
||||||
echo "Error: git-sv is not installed or not in PATH."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Get the semantic version bump
|
|
||||||
VERSION_BUMP=$(git-sv next-version)
|
|
||||||
|
|
||||||
# Validate the version bump
|
|
||||||
#if [[ "$VERSION_BUMP" =~ ^(patch|minor|major)$ ]]; then
|
|
||||||
# echo "Version bump: $VERSION_BUMP"
|
|
||||||
#else
|
|
||||||
# echo "Error: Unexpected version bump value: $VERSION_BUMP"
|
|
||||||
# exit 1
|
|
||||||
#fi
|
|
||||||
|
|
||||||
# Convert to JSON
|
|
||||||
RESULTS=$(jq -n \
|
|
||||||
--argjson filteredChanges "$(printf '%s\n' "$UNIQUE_FILTERED_CHANGES" | jq -R -s -c 'split("\n") | map(select(length > 0))')" \
|
|
||||||
--arg versionBump "$VERSION_BUMP" \
|
|
||||||
'{filteredChanges: $filteredChanges, versionBump: $versionBump}')
|
|
||||||
|
|
||||||
# Output results
|
|
||||||
echo "$RESULTS"
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Metadata for the package
|
|
||||||
meta = with lib; {
|
|
||||||
homepage = "https://code.252.no/tommy/containers";
|
|
||||||
description = "Identify Merge Commit and List Changed Files with Flexible Filtering and Semantic Versioning";
|
|
||||||
license = licenses.mit;
|
|
||||||
maintainers = with maintainers; [ "tommy-skaug" ];
|
|
||||||
};
|
|
||||||
}
|
}
|
3
apps/ci-os/packages/merge-diff/src/go.mod
Normal file
3
apps/ci-os/packages/merge-diff/src/go.mod
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module code.252.no/tommy/merge-diff
|
||||||
|
|
||||||
|
go 1.22.6
|
179
apps/ci-os/packages/merge-diff/src/main.go
Normal file
179
apps/ci-os/packages/merge-diff/src/main.go
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
FilteredChanges []string `json:"filteredChanges"`
|
||||||
|
NextVersion string `json:"nextVersion"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Parse command-line arguments
|
||||||
|
var patterns patternList
|
||||||
|
var depth int
|
||||||
|
|
||||||
|
flag.Var(&patterns, "p", "Path pattern to filter changed files (can be specified multiple times)")
|
||||||
|
flag.Var(&patterns, "pattern", "Path pattern to filter changed files (can be specified multiple times)")
|
||||||
|
flag.IntVar(&depth, "d", 2, "Depth of changeset (default is 2)")
|
||||||
|
flag.IntVar(&depth, "depth", 2, "Depth of changeset (default is 2)")
|
||||||
|
help := flag.Bool("h", false, "Display this help message")
|
||||||
|
helpLong := flag.Bool("help", false, "Display this help message")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *help || *helpLong {
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(patterns) == 0 {
|
||||||
|
fmt.Println("Error: At least one path pattern must be specified.")
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current commit SHA
|
||||||
|
currentSHA := getCurrentSHA()
|
||||||
|
|
||||||
|
// Get parent commits
|
||||||
|
parentSHAs := getParentSHAs(currentSHA)
|
||||||
|
|
||||||
|
if len(parentSHAs) == 0 {
|
||||||
|
fmt.Println("Single commit with no parents. Listing all changes.")
|
||||||
|
parentSHAs = append(parentSHAs, currentSHA+"~1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all changed files from all parents
|
||||||
|
changedFiles := make(map[string]struct{})
|
||||||
|
for _, parentSHA := range parentSHAs {
|
||||||
|
files := getChangedFiles(parentSHA, currentSHA)
|
||||||
|
for _, file := range files {
|
||||||
|
changedFiles[file] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter and process changed files
|
||||||
|
filteredChanges := filterAndProcessFiles(changedFiles, patterns, depth)
|
||||||
|
|
||||||
|
// Get the next semantic version using git-sv
|
||||||
|
nextVersion := getNextVersion()
|
||||||
|
|
||||||
|
// Prepare the result
|
||||||
|
result := Result{
|
||||||
|
FilteredChanges: uniqueStrings(filteredChanges),
|
||||||
|
NextVersion: nextVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output results as JSON
|
||||||
|
output, err := json.Marshal(result)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error marshaling JSON: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom flag type for multiple patterns
|
||||||
|
type patternList []string
|
||||||
|
|
||||||
|
func (p *patternList) String() string {
|
||||||
|
return strings.Join(*p, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *patternList) Set(value string) error {
|
||||||
|
*p = append(*p, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
func getCurrentSHA() string {
|
||||||
|
output, err := exec.Command("git", "rev-parse", "HEAD").Output()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error getting current SHA: %v", err)
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getParentSHAs(commitSHA string) []string {
|
||||||
|
output, err := exec.Command("git", "rev-list", "--parents", "-n", "1", commitSHA).Output()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error getting parent SHAs: %v", err)
|
||||||
|
}
|
||||||
|
parts := strings.Fields(string(output))
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1:] // Exclude the current commit SHA
|
||||||
|
}
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getChangedFiles(parentSHA, currentSHA string) []string {
|
||||||
|
output, err := exec.Command("git", "diff", "--name-only", parentSHA, currentSHA).Output()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error getting changed files: %v", err)
|
||||||
|
}
|
||||||
|
lines := strings.Split(strings.TrimSpace(string(output)), "\n")
|
||||||
|
if len(lines) == 1 && lines[0] == "" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterAndProcessFiles(changedFiles map[string]struct{}, patterns []string, depth int) []string {
|
||||||
|
var filtered []string
|
||||||
|
for file := range changedFiles {
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
matched, err := regexp.MatchString(pattern, file)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Invalid regex pattern: %v", err)
|
||||||
|
}
|
||||||
|
if matched {
|
||||||
|
processedFile := processFilePath(file, depth)
|
||||||
|
filtered = append(filtered, processedFile)
|
||||||
|
break // Stop checking other patterns for this file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
func processFilePath(filePath string, depth int) string {
|
||||||
|
parts := strings.Split(filePath, string(os.PathSeparator))
|
||||||
|
if len(parts) > depth {
|
||||||
|
parts = parts[:depth]
|
||||||
|
}
|
||||||
|
return filepath.Join(parts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func uniqueStrings(input []string) []string {
|
||||||
|
uniqueMap := make(map[string]struct{})
|
||||||
|
for _, item := range input {
|
||||||
|
uniqueMap[item] = struct{}{}
|
||||||
|
}
|
||||||
|
var result []string
|
||||||
|
for item := range uniqueMap {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNextVersion() string {
|
||||||
|
// Ensure that git-sv is available
|
||||||
|
if _, err := exec.LookPath("git-sv"); err != nil {
|
||||||
|
log.Fatalf("Error: git-sv is not installed or not in PATH.")
|
||||||
|
}
|
||||||
|
output, err := exec.Command("git-sv", "next-version").Output()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error getting next version: %v", err)
|
||||||
|
}
|
||||||
|
nextVersion := strings.TrimSpace(string(output))
|
||||||
|
return nextVersion
|
||||||
|
}
|
39
renovate.json5
Normal file
39
renovate.json5
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"config:base"
|
||||||
|
],
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"matchManagers": ["nix"],
|
||||||
|
"matchPaths": ["**/flake.nix", "**/default.nix"],
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchManagers": ["gomod"],
|
||||||
|
"matchPaths": ["**/go.mod"],
|
||||||
|
"enabled": true,
|
||||||
|
"additionalBranchPrefix": "gomod-",
|
||||||
|
"schedule": ["at any time"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchManagers": ["github-actions"],
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchManagers": ["dockerfile"],
|
||||||
|
"matchPaths": ["**/Dockerfile"],
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchManagers": ["json"],
|
||||||
|
"matchPaths": ["**/*.json", "**/*.json5"],
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timezone": "UTC",
|
||||||
|
"schedule": ["before 6am on Monday"],
|
||||||
|
"dependencyDashboard": true,
|
||||||
|
"labels": ["dependencies"],
|
||||||
|
"branchConcurrentLimit": 5,
|
||||||
|
"prConcurrentLimit": 5
|
||||||
|
}
|
Reference in a new issue