diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 325fc59e0..b2ac6aedb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ jobs: if: ${{ github.event_name == 'release' || (github.event_name == 'pull_request' && github.event.label.name == 'build-release-container') }} steps: - name: Setup BuildX - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: Checkout uses: actions/checkout@v3 with: @@ -40,6 +40,7 @@ jobs: cache-to: type=gha,mode=max build-contexts: | git=.git + docker=docker target: run push: ${{ github.event_name == 'release' && 'true' || 'false' }} tags: ${{ steps.meta.outputs.tags }} diff --git a/Dockerfile b/Dockerfile index bb4586ce2..5bbaa585d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,31 @@ # syntax=docker/dockerfile:1.4-labs +ARG ALPINE_VERSION=3.17 + +FROM alpine:${ALPINE_VERSION} as rust-base + +RUN apk add --no-cache ca-certificates gcc musl-dev + +ENV RUSTUP_HOME=/usr/local/rustup +ENV CARGO_HOME=/usr/local/cargo +ENV PATH=/usr/local/cargo/bin:${PATH} + +ARG RUSTUP_VERSION=1.25.1 ARG RUST_VERSION=1.61.0 +ARG RUST_ARCH=x86_64-unknown-linux-musl + +# https://github.com/sfackler/rust-openssl/issues/1462 +ENV RUSTFLAGS="-Ctarget-feature=-crt-static" -FROM rust:${RUST_VERSION} as dev-planner +ADD --chmod=755 https://static.rust-lang.org/rustup/archive/${RUSTUP_VERSION}/${RUST_ARCH}/rustup-init /tmp +RUN /tmp/rustup-init \ + -y \ + --no-modify-path \ + --profile minimal \ + --default-toolchain ${RUST_VERSION} \ + --default-host ${RUST_ARCH} + +FROM rust-base as dev-planner RUN cargo install --version 0.1.35 cargo-chef @@ -12,37 +35,32 @@ COPY . . ENV CARGO_TARGET_DIR=/opt/cargo-target RUN cargo chef prepare --recipe-path recipe.json -FROM rust:${RUST_VERSION} as dev +FROM rust-base as dev -RUN < josh::JoshResult<()> { fn make_ssh_command() -> String { let ssh_options = [ - "IdentitiesOnly=yes", "LogLevel=ERROR", "UserKnownHostsFile=/dev/null", "StrictHostKeyChecking=no", diff --git a/josh-ssh-dev-server/main.go b/josh-ssh-dev-server/main.go index af4fbadc5..6c45f4f32 100644 --- a/josh-ssh-dev-server/main.go +++ b/josh-ssh-dev-server/main.go @@ -113,12 +113,10 @@ func runServer(port uint, shell string) { } }() - go func() { - _, err = io.Copy(session, stdout) - if err != nil { - return - } - }() + _, err = io.Copy(session, stdout) + if err != nil { + return + } _ = cmd.Wait() log.Printf("subprocess with task_id %d exited\n", taskId) diff --git a/josh-ssh-shell/docker/Dockerfile b/josh-ssh-shell/docker/Dockerfile deleted file mode 100644 index 48918e128..000000000 --- a/josh-ssh-shell/docker/Dockerfile +++ /dev/null @@ -1,99 +0,0 @@ -# syntax = docker/dockerfile:1.4-labs - -FROM alpine:3.16 - -RUN apk add --no-cache openssh bash git xz tree shadow - -ARG S6_OVERLAY_VERSION=3.1.2.1 -ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp -RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz -ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp -RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz - -ARG GIT_GID_UID=2001 - -RUN addgroup -g ${GIT_GID_UID} git -RUN adduser \ - -h /home/git \ - -s josh-ssh-shell \ - -G git \ - -D \ - -u ${GIT_GID_UID} \ - git - -# sshd will call josh-ssh-shell -c "git command" - -# https://unix.stackexchange.com/a/193131/336647 -RUN usermod -p '*' git - -COPY josh-auth-key.sh /opt/scripts/ -COPY etc/ssh/sshd_config /etc/ssh/ - -ARG RC6_D=/etc/s6-overlay/s6-rc.d - -# s6: josh-generate-keys - -COPY josh-generate-keys.sh /opt/scripts/ - -WORKDIR ${RC6_D}/josh-generate-keys - -COPY <&1 echo "Persistent volume not mounted" - exit 1 - fi - - _ensure_dir ${KEY_DIR} - _ensure_owner ${KEY_DIR} git:git - _ensure_mode ${KEY_DIR} 700 - - if { - [[ ! -f ${KEY_DIR}/id_${KEY_TYPE} ]] || [[ ! -f ${KEY_DIR}/id_${KEY_TYPE}.pub ]] - }; then - 2>&1 echo "Generating SSH server key" - ssh-keygen -t ${KEY_TYPE} -N "" -f ${KEY_DIR}/id_${KEY_TYPE} -C git - fi - - _ensure_owner ${KEY_DIR}/id_${KEY_TYPE} git:git - _ensure_mode ${KEY_DIR}/id_${KEY_TYPE} 600 - - _ensure_owner ${KEY_DIR}/id_${KEY_TYPE}.pub git:git - _ensure_mode ${KEY_DIR}/id_${KEY_TYPE}.pub 644 -} - -_create_keys diff --git a/src/filter/mod.rs b/src/filter/mod.rs index ed854ef8f..da618f9a1 100644 --- a/src/filter/mod.rs +++ b/src/filter/mod.rs @@ -100,6 +100,7 @@ enum Op { Squash(Option>), Rev(std::collections::HashMap), Linear, + Unsign, RegexReplace(regex::Regex, String), @@ -277,6 +278,7 @@ fn spec2(op: &Op) -> String { format!(":SQUASH={}", s) } Op::Linear => ":linear".to_string(), + Op::Unsign => ":unsign".to_string(), Op::Subdir(path) => format!(":/{}", parse::quote(&path.to_string_lossy())), Op::File(path) => format!("::{}", parse::quote(&path.to_string_lossy())), Op::Prefix(path) => format!(":prefix={}", parse::quote(&path.to_string_lossy())), @@ -387,6 +389,7 @@ fn apply_to_commit2( &[], &commit.tree()?, None, + true, )) .transpose() } @@ -467,6 +470,28 @@ fn apply_to_commit2( )) .transpose(); } + Op::Unsign => { + let parents: Vec<_> = commit.parent_ids().collect(); + + let filtered_parents: Vec<_> = parents + .iter() + .map(|p| transaction.get(filter, *p)) + .collect(); + if filtered_parents.iter().any(|p| p.is_none()) { + return Ok(None); + } + let filtered_parents = filtered_parents.iter().map(|p| p.unwrap()).collect(); + + return Some(history::remove_commit_signature( + commit, + filtered_parents, + commit.tree()?, + transaction, + filter, + None, + )) + .transpose(); + } Op::Compose(filters) => { let filtered = filters .iter() @@ -661,6 +686,7 @@ fn apply2<'a>( Op::Squash(None) => Ok(tree), Op::Squash(Some(_)) => Err(josh_error("not applicable to tree")), Op::Linear => Ok(tree), + Op::Unsign => Ok(tree), Op::Rev(_) => Err(josh_error("not applicable to tree")), Op::RegexReplace(regex, replacement) => { tree::regex_replace(tree.id(), ®ex, &replacement, transaction) diff --git a/src/filter/opt.rs b/src/filter/opt.rs index 868ae0808..6beb8df65 100644 --- a/src/filter/opt.rs +++ b/src/filter/opt.rs @@ -422,6 +422,7 @@ pub fn invert(filter: Filter) -> JoshResult { let result = match to_op(filter) { Op::Nop => Some(Op::Nop), Op::Linear => Some(Op::Nop), + Op::Unsign => Some(Op::Unsign), Op::Empty => Some(Op::Empty), Op::Subdir(path) => Some(Op::Prefix(path)), Op::File(path) => Some(Op::File(path)), diff --git a/src/filter/parse.rs b/src/filter/parse.rs index dbf084568..4127cfcbb 100644 --- a/src/filter/parse.rs +++ b/src/filter/parse.rs @@ -37,6 +37,7 @@ fn make_op(args: &[&str]) -> JoshResult { ["SQUASH"] => Ok(Op::Squash(None)), ["SQUASH", _ids @ ..] => Err(josh_error("SQUASH with ids can't be parsed")), ["linear"] => Ok(Op::Linear), + ["unsign"] => Ok(Op::Unsign), ["PATHS"] => Ok(Op::Paths), #[cfg(feature = "search")] ["INDEX"] => Ok(Op::Index), diff --git a/src/graphql.rs b/src/graphql.rs index c84177a27..f312c69cb 100644 --- a/src/graphql.rs +++ b/src/graphql.rs @@ -147,7 +147,8 @@ impl Revision { let ts = filter_commit.time().seconds(); - let ndt = chrono::NaiveDateTime::from_timestamp(ts, 0); + let ndt = chrono::NaiveDateTime::from_timestamp_opt(ts, 0) + .ok_or(josh_error("from_timestamp_opt"))?; Ok(ndt.format(&format).to_string()) } diff --git a/src/history.rs b/src/history.rs index 21a70c6c7..25eea1953 100644 --- a/src/history.rs +++ b/src/history.rs @@ -182,13 +182,8 @@ pub fn rewrite_commit( parents: &[&git2::Commit], tree: &git2::Tree, message: Option<(String, String, String)>, + unsign: bool, ) -> JoshResult { - if message == None && base.tree()?.id() == tree.id() && all_equal(base.parents(), parents) { - // Looks like an optimization, but in fact serves to not change the commit in case - // it was signed. - return Ok(base.id()); - } - let b = if let Some((message, author, email)) = message { let a = base.author(); let new_a = git2::Signature::new(&author, &email, &a.when())?; @@ -205,7 +200,7 @@ pub fn rewrite_commit( )? }; - if let Ok((sig, _)) = repo.extract_signature(&base.id(), None) { + if let (false, Ok((sig, _))) = (unsign, repo.extract_signature(&base.id(), None)) { // Re-create the object with the original signature (which of course does not match any // more, but this is needed to guarantee perfect round-trips). let b = b @@ -220,20 +215,6 @@ pub fn rewrite_commit( return Ok(repo.odb()?.write(git2::ObjectType::Commit, &b)?); } -fn all_equal(a: git2::Parents, b: &[&git2::Commit]) -> bool { - let a: Vec<_> = a.collect(); - if a.len() != b.len() { - return false; - } - - for (x, y) in b.iter().zip(a.iter()) { - if x.id() != y.id() { - return false; - } - } - true -} - fn find_oldest_similar_commit( transaction: &cache::Transaction, filter: filter::Filter, @@ -527,6 +508,7 @@ pub fn unapply_filter( &original_parents_refs, &new_tree, None, + false, )?; if let Some(ref mut change_ids) = change_ids { @@ -560,6 +542,30 @@ fn select_parent_commits<'a>( } } +pub fn remove_commit_signature<'a>( + original_commit: &'a git2::Commit, + filtered_parent_ids: Vec, + filtered_tree: git2::Tree<'a>, + transaction: &cache::Transaction, + filter: filter::Filter, + message: Option<(String, String, String)>, +) -> JoshResult { + let (r, is_new) = create_filtered_commit2( + transaction.repo(), + original_commit, + filtered_parent_ids, + filtered_tree, + message, + true, + )?; + + let store = is_new || original_commit.parent_ids().len() != 1; + + transaction.insert(filter, original_commit.id(), r, store); + + Ok(r) +} + pub fn drop_commit<'a>( original_commit: &'a git2::Commit, filtered_parent_ids: Vec, @@ -591,6 +597,7 @@ pub fn create_filtered_commit<'a>( filtered_parent_ids, filtered_tree, message, + false, )?; let store = is_new || original_commit.parent_ids().len() != 1; @@ -606,6 +613,7 @@ fn create_filtered_commit2<'a>( filtered_parent_ids: Vec, filtered_tree: git2::Tree<'a>, message: Option<(String, String, String)>, + unsign: bool, ) -> JoshResult<(git2::Oid, bool)> { let filtered_parent_commits: Result, _> = filtered_parent_ids .iter() @@ -651,6 +659,7 @@ fn create_filtered_commit2<'a>( &selected_filtered_parent_commits, &filtered_tree, message, + unsign, )?, true, )) diff --git a/tester.sh b/tester.sh index b66508f38..608206b44 100755 --- a/tester.sh +++ b/tester.sh @@ -9,7 +9,7 @@ set -e shopt -s extglob shopt -s inherit_errexit -if (( $# > 1 )) && [[ "${1}" == "--no-build-container" ]]; then +if (( $# >= 1 )) && [[ "${1}" == "--no-build-container" ]]; then NO_BUILD_CONTAINER=1 shift else diff --git a/tests/filter/cmdline.t b/tests/filter/cmdline.t index 300f076a8..669d0fdcb 100644 --- a/tests/filter/cmdline.t +++ b/tests/filter/cmdline.t @@ -107,11 +107,11 @@ * initial $ cat c/.joshinfo - cat: c/.joshinfo: No such file or directory + *: No such file or directory (glob) [1] $ cat a/b/.joshinfo - cat: a/b/.joshinfo: No such file or directory + *: No such file or directory (glob) [1] $ git show libs/master | grep $(cat c/.joshinfo | grep commit | sed 's/commit: //') diff --git a/tests/filter/gpgsig.t b/tests/filter/gpgsig.t index c29e3caba..cfdd817e8 100644 --- a/tests/filter/gpgsig.t +++ b/tests/filter/gpgsig.t @@ -35,3 +35,25 @@ If 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb shows up then the signature was lost $ git rev-parse master double-filtered cb22ebb8e47b109f7add68b1043e561e0db09802 cb22ebb8e47b109f7add68b1043e561e0db09802 + +Remove the signature, the shas are different. + $ josh-filter :unsign refs/heads/master --update refs/heads/filtered -s + [1] :unsign + $ git rev-parse master filtered + cb22ebb8e47b109f7add68b1043e561e0db09802 + 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb + $ josh-filter --reverse :unsign refs/heads/double-filtered --update refs/heads/filtered -s + [1] :unsign + $ git rev-parse master double-filtered + cb22ebb8e47b109f7add68b1043e561e0db09802 + cb22ebb8e47b109f7add68b1043e561e0db09802 + +Round trip does not work but reversed works since the commit exists + $ josh-filter :prefix=extra:unsign refs/heads/master --update refs/heads/filtered + $ josh-filter :/extra refs/heads/filtered --update refs/heads/double-filtered + $ git branch reversed + $ josh-filter --reverse :prefix=extra:unsign refs/heads/reversed --update refs/heads/filtered + $ git rev-parse master double-filtered reversed + cb22ebb8e47b109f7add68b1043e561e0db09802 + 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb + cb22ebb8e47b109f7add68b1043e561e0db09802 diff --git a/tests/filter/workspace_modify_chain.t b/tests/filter/workspace_modify_chain.t index 89d235bc3..71eb7dc99 100644 --- a/tests/filter/workspace_modify_chain.t +++ b/tests/filter/workspace_modify_chain.t @@ -72,7 +72,7 @@ 4 directories, 6 files $ cat ws/file1 - cat: ws/file1: No such file or directory + *: No such file or directory (glob) [1] $ cat sub1/file1 contents1 diff --git a/tests/filter/workspace_shadow_file.t b/tests/filter/workspace_shadow_file.t index e0e97930e..4a26691d2 100644 --- a/tests/filter/workspace_shadow_file.t +++ b/tests/filter/workspace_shadow_file.t @@ -1,5 +1,10 @@ $ export TESTTMP=${PWD} - $ export "PATH=${TESTDIR}/../../target/debug/:${PATH}" + $ if [ -n "${CARGO_TARGET_DIR+x}" ]; then + > export TARGET_DIR=${CARGO_TARGET_DIR} + > else + > export TARGET_DIR=${TESTDIR}/../../target + > fi + $ export "PATH=${TARGET_DIR}/debug:${PATH}" $ cd ${TESTTMP} $ git init -q real_repo 1> /dev/null diff --git a/tests/proxy/clone_subtree_no_master.t b/tests/proxy/clone_subtree_no_master.t index bc5c447c5..dbad79eba 100644 --- a/tests/proxy/clone_subtree_no_master.t +++ b/tests/proxy/clone_subtree_no_master.t @@ -58,7 +58,7 @@ [1] $ cat .git/refs/remotes/origin/HEAD - cat: .git/refs/remotes/origin/HEAD: No such file or directory + *: No such file or directory (glob) [1] $ tree @@ -71,7 +71,7 @@ [128] $ cat file1 - cat: file1: No such file or directory + *: No such file or directory (glob) [1] $ bash ${TESTDIR}/destroy_test_env.sh diff --git a/tests/proxy/workspace_in_workspace_prefix.t b/tests/proxy/workspace_in_workspace_prefix.t index 422665684..94432d5e6 100644 --- a/tests/proxy/workspace_in_workspace_prefix.t +++ b/tests/proxy/workspace_in_workspace_prefix.t @@ -231,5 +231,5 @@ 100644 blob 63c4399dfb47e109da4e7d6c01751b5171b9aa38\tworkspace.josh (esc) $ cat foo/workspace.josh - cat: foo/workspace.josh: No such file or directory + *: No such file or directory (glob) [1] diff --git a/tests/proxy/workspace_modify_chain_prefix_subtree.t b/tests/proxy/workspace_modify_chain_prefix_subtree.t index 9fd11d252..5c56987ca 100644 --- a/tests/proxy/workspace_modify_chain_prefix_subtree.t +++ b/tests/proxy/workspace_modify_chain_prefix_subtree.t @@ -249,7 +249,7 @@ Note that ws/d/ is now present in the ws * initial $ cat sub1/subsub/file1 - cat: sub1/subsub/file1: No such file or directory + *: No such file or directory (glob) [1] $ git checkout -q HEAD~1 1> /dev/null